merge develop

This commit is contained in:
Roman Chvanikov 2019-07-12 18:08:27 +03:00
commit eae991b06a
160 changed files with 4263 additions and 1310 deletions

2
.mailmap Normal file
View file

@ -0,0 +1,2 @@
Ariadne Conill <ariadne@dereferenced.org> <nenolod@dereferenced.org>
Ariadne Conill <ariadne@dereferenced.org> <nenolod@gmail.com>

View file

@ -4,28 +4,36 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Added ### Changed
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`) - **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
Configuration: `federation_incoming_replies_max_depth` option - Configuration: OpenGraph and TwitterCard providers enabled by default
- Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses) - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
- Mastodon API, streaming: Add support for passing the token in the `Sec-WebSocket-Protocol` header - NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
- Admin API: Return users' tags when querying reports
- Admin API: Return avatar and display name when querying users
- Admin API: Allow querying user by ID
- Added synchronization of following/followers counters for external users
### Fixed ### Fixed
- Not being able to pin unlisted posts - Not being able to pin unlisted posts
- Metadata rendering errors resulting in the entire page being inaccessible - Metadata rendering errors resulting in the entire page being inaccessible
- Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`) - Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`)
- Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity - Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity
- Mastodon API: Add `account_id`, `type`, `offset`, and `limit` to search API (`/api/v1/search` and `/api/v2/search`)
### Added
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
Configuration: `federation_incoming_replies_max_depth` option
- Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses)
- Mastodon API, streaming: Add support for passing the token in the `Sec-WebSocket-Protocol` header
- Mastodon API, extension: Ability to reset avatar, profile banner, and background
- Admin API: Return users' tags when querying reports
- Admin API: Return avatar and display name when querying users
- Admin API: Allow querying user by ID
- Admin API: Added support for `tuples`.
- Added synchronization of following/followers counters for external users
- Configuration: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`.
- Mastodon API: Add support for categories for custom emojis by reusing the group feature. <https://github.com/tootsuite/mastodon/pull/11196>
### Changed ### Changed
- Configuration: OpenGraph and TwitterCard providers enabled by default
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
- Admin API: changed json structure for saving config settings.
### Changed
- NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
## [1.0.0] - 2019-06-29 ## [1.0.0] - 2019-06-29
### Security ### Security
@ -88,7 +96,6 @@ Configuration: `federation_incoming_replies_max_depth` option
- OAuth: added job to clean expired access tokens - OAuth: added job to clean expired access tokens
- MRF: Support for rejecting reports from specific instances (`mrf_simple`) - MRF: Support for rejecting reports from specific instances (`mrf_simple`)
- MRF: Support for stripping avatars and banner images from specific instances (`mrf_simple`) - MRF: Support for stripping avatars and banner images from specific instances (`mrf_simple`)
- Ability to reset avatar, profile banner and backgroud
- MRF: Support for running subchains. - MRF: Support for running subchains.
- Configuration: `skip_thread_containment` option - Configuration: `skip_thread_containment` option
- Configuration: `rate_limit` option. See `Pleroma.Plugs.RateLimiter` documentation for details. - Configuration: `rate_limit` option. See `Pleroma.Plugs.RateLimiter` documentation for details.

View file

@ -250,13 +250,7 @@
skip_thread_containment: true, skip_thread_containment: true,
limit_to_local_content: :unauthenticated, limit_to_local_content: :unauthenticated,
dynamic_configuration: false, dynamic_configuration: false,
external_user_synchronization: [ external_user_synchronization: true
enabled: false,
# every 2 hours
interval: 60 * 60 * 2,
max_retries: 3,
limit: 500
]
config :pleroma, :markup, config :pleroma, :markup,
# XXX - unfortunately, inline images must be enabled by default right now, because # XXX - unfortunately, inline images must be enabled by default right now, because
@ -501,7 +495,7 @@
config :pleroma, :auth, oauth_consumer_strategies: oauth_consumer_strategies config :pleroma, :auth, oauth_consumer_strategies: oauth_consumer_strategies
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail, enabled: false
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics" config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"

View file

@ -23,7 +23,7 @@
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads" config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test, enabled: true
config :pleroma, :instance, config :pleroma, :instance,
email: "admin@example.com", email: "admin@example.com",
@ -65,7 +65,9 @@
total_user_limit: 3, total_user_limit: 3,
enabled: false enabled: false
config :pleroma, :rate_limit, app_account_creation: {10_000, 5} config :pleroma, :rate_limit,
search: [{1000, 30}, {1000, 30}],
app_account_creation: {10_000, 5}
config :pleroma, :http_security, report_uri: "https://endpoint.com" config :pleroma, :http_security, report_uri: "https://endpoint.com"

View file

@ -573,7 +573,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
configs: [ configs: [
{ {
"group": string, "group": string,
"key": string, "key": string or string with leading `:` for atoms,
"value": string or {} or [] or {"tuple": []} "value": string or {} or [] or {"tuple": []}
} }
] ]
@ -583,10 +583,11 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
## `/api/pleroma/admin/config` ## `/api/pleroma/admin/config`
### Update config settings ### Update config settings
Module name can be passed as string, which starts with `Pleroma`, e.g. `"Pleroma.Upload"`. Module name can be passed as string, which starts with `Pleroma`, e.g. `"Pleroma.Upload"`.
Atom or boolean value can be passed with `:` in the beginning, e.g. `":true"`, `":upload"`. For keys it is not needed. Atom keys and values can be passed with `:` in the beginning, e.g. `":upload"`.
Integer with `i:`, e.g. `"i:150"`. Tuples can be passed as `{"tuple": ["first_val", Pleroma.Module, []]}`.
Tuple with more than 2 values with `{"tuple": ["first_val", Pleroma.Module, []]}`.
`{"tuple": ["some_string", "Pleroma.Some.Module", []]}` will be converted to `{"some_string", Pleroma.Some.Module, []}`. `{"tuple": ["some_string", "Pleroma.Some.Module", []]}` will be converted to `{"some_string", Pleroma.Some.Module, []}`.
Keywords can be passed as lists with 2 child tuples, e.g.
`[{"tuple": ["first_val", Pleroma.Module]}, {"tuple": ["second_val", true]}]`.
Compile time settings (need instance reboot): Compile time settings (need instance reboot):
- all settings by this keys: - all settings by this keys:
@ -603,7 +604,7 @@ Compile time settings (need instance reboot):
- Params: - Params:
- `configs` => [ - `configs` => [
- `group` (string) - `group` (string)
- `key` (string) - `key` (string or string with leading `:` for atoms)
- `value` (string, [], {} or {"tuple": []}) - `value` (string, [], {} or {"tuple": []})
- `delete` = true (optional, if parameter must be deleted) - `delete` = true (optional, if parameter must be deleted)
] ]
@ -616,23 +617,24 @@ Compile time settings (need instance reboot):
{ {
"group": "pleroma", "group": "pleroma",
"key": "Pleroma.Upload", "key": "Pleroma.Upload",
"value": { "value": [
"uploader": "Pleroma.Uploaders.Local", {"tuple": [":uploader", "Pleroma.Uploaders.Local"]},
"filters": ["Pleroma.Upload.Filter.Dedupe"], {"tuple": [":filters", ["Pleroma.Upload.Filter.Dedupe"]]},
"link_name": ":true", {"tuple": [":link_name", true]},
"proxy_remote": ":false", {"tuple": [":proxy_remote", false]},
"proxy_opts": { {"tuple": [":proxy_opts", [
"redirect_on_failure": ":false", {"tuple": [":redirect_on_failure", false]},
"max_body_length": "i:1048576", {"tuple": [":max_body_length", 1048576]},
"http": { {"tuple": [":http": [
"follow_redirect": ":true", {"tuple": [":follow_redirect", true]},
"pool": ":upload" {"tuple": [":pool", ":upload"]},
} ]]}
}, ]
"dispatch": { ]},
{"tuple": [":dispatch", {
"tuple": ["/api/v1/streaming", "Pleroma.Web.MastodonAPI.WebsocketHandler", []] "tuple": ["/api/v1/streaming", "Pleroma.Web.MastodonAPI.WebsocketHandler", []]
} }]}
} ]
} }
] ]
} }
@ -644,7 +646,7 @@ Compile time settings (need instance reboot):
configs: [ configs: [
{ {
"group": string, "group": string,
"key": string, "key": string or string with leading `:` for atoms,
"value": string or {} or [] or {"tuple": []} "value": string or {} or [] or {"tuple": []}
} }
] ]

View file

@ -46,6 +46,14 @@ Has these additional fields under the `pleroma` object:
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials` - `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
- `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials` - `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
### Extensions for PleromaFE
These endpoints added for controlling PleromaFE features over the Mastodon API
- PATCH `/api/v1/accounts/update_avatar`: Set/clear user avatar image
- PATCH `/api/v1/accounts/update_banner`: Set/clear user banner image
- PATCH `/api/v1/accounts/update_background`: Set/clear user background image
### Source ### Source
Has these additional fields under the `pleroma` object: Has these additional fields under the `pleroma` object:

View file

@ -41,6 +41,7 @@ This filter replaces the filename (not the path) of an upload. For complete obfu
## Pleroma.Emails.Mailer ## Pleroma.Emails.Mailer
* `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox. * `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox.
* `api_key` / `password` and / or other adapter-specific settings, per the above documentation. * `api_key` / `password` and / or other adapter-specific settings, per the above documentation.
* `enabled`: Allows enable/disable send emails. Default: `false`.
An example for Sendgrid adapter: An example for Sendgrid adapter:
@ -125,11 +126,7 @@ config :pleroma, Pleroma.Emails.Mailer,
* `skip_thread_containment`: Skip filter out broken threads. The default is `false`. * `skip_thread_containment`: Skip filter out broken threads. The default is `false`.
* `limit_to_local_content`: Limit unauthenticated users to search for local statutes and users only. Possible values: `:unauthenticated`, `:all` and `false`. The default is `:unauthenticated`. * `limit_to_local_content`: Limit unauthenticated users to search for local statutes and users only. Possible values: `:unauthenticated`, `:all` and `false`. The default is `:unauthenticated`.
* `dynamic_configuration`: Allow transferring configuration to DB with the subsequent customization from Admin api. * `dynamic_configuration`: Allow transferring configuration to DB with the subsequent customization from Admin api.
* `external_user_synchronization`: Following/followers counters synchronization settings. * `external_user_synchronization`: Enabling following/followers counters synchronization for external users.
* `enabled`: Enables synchronization
* `interval`: Interval between synchronization.
* `max_retries`: Max rettries for host. After exceeding the limit, the check will not be carried out for users from this host.
* `limit`: Users batch size for processing in one time.

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Healthcheck do defmodule Pleroma.Healthcheck do
@moduledoc """ @moduledoc """
Module collects metrics about app and assign healthy status. Module collects metrics about app and assign healthy status.

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
Postgrex.Types.define( Postgrex.Types.define(
Pleroma.PostgresTypes, Pleroma.PostgresTypes,
[] ++ Ecto.Adapters.Postgres.extensions(), [] ++ Ecto.Adapters.Postgres.extensions(),

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Benchmark do defmodule Mix.Tasks.Pleroma.Benchmark do
import Mix.Pleroma import Mix.Pleroma
use Mix.Task use Mix.Task

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Config do defmodule Mix.Tasks.Pleroma.Config do
use Mix.Task use Mix.Task
import Mix.Pleroma import Mix.Pleroma

View file

@ -34,6 +34,8 @@ defmodule Mix.Tasks.Pleroma.Instance do
- `--db-configurable Y/N` - Allow/disallow configuring instance from admin part - `--db-configurable Y/N` - Allow/disallow configuring instance from admin part
- `--uploads-dir` - the directory uploads go in when using a local uploader - `--uploads-dir` - the directory uploads go in when using a local uploader
- `--static-dir` - the directory custom public files should be read from (custom emojis, frontend bundle overrides, robots.txt, etc.) - `--static-dir` - the directory custom public files should be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)
- `--listen-ip` - the ip the app should listen to, defaults to 127.0.0.1
- `--listen-port` - the port the app should listen to, defaults to 4000
""" """
def run(["gen" | rest]) do def run(["gen" | rest]) do
@ -56,7 +58,9 @@ def run(["gen" | rest]) do
indexable: :string, indexable: :string,
db_configurable: :string, db_configurable: :string,
uploads_dir: :string, uploads_dir: :string,
static_dir: :string static_dir: :string,
listen_ip: :string,
listen_port: :string
], ],
aliases: [ aliases: [
o: :output, o: :output,
@ -146,6 +150,22 @@ def run(["gen" | rest]) do
"n" "n"
) === "y" ) === "y"
listen_port =
get_option(
options,
:listen_port,
"What port will the app listen to (leave it if you are using the default setup with nginx)?",
4000
)
listen_ip =
get_option(
options,
:listen_ip,
"What ip will the app listen to (leave it if you are using the default setup with nginx)?",
"127.0.0.1"
)
uploads_dir = uploads_dir =
get_option( get_option(
options, options,
@ -188,7 +208,9 @@ def run(["gen" | rest]) do
db_configurable?: db_configurable?, db_configurable?: db_configurable?,
static_dir: static_dir, static_dir: static_dir,
uploads_dir: uploads_dir, uploads_dir: uploads_dir,
rum_enabled: rum_enabled rum_enabled: rum_enabled,
listen_ip: listen_ip,
listen_port: listen_port
) )
result_psql = result_psql =

View file

@ -344,5 +344,5 @@ def restrict_deactivated_users(query) do
) )
end end
defdelegate search(user, query), to: Pleroma.Activity.Search defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
end end

View file

@ -5,14 +5,17 @@
defmodule Pleroma.Activity.Search do defmodule Pleroma.Activity.Search do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Object.Fetcher alias Pleroma.Object.Fetcher
alias Pleroma.Repo alias Pleroma.Pagination
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.ActivityPub.Visibility
import Ecto.Query import Ecto.Query
def search(user, search_query) do def search(user, search_query, options \\ []) do
index_type = if Pleroma.Config.get([:database, :rum_enabled]), do: :rum, else: :gin index_type = if Pleroma.Config.get([:database, :rum_enabled]), do: :rum, else: :gin
limit = Enum.min([Keyword.get(options, :limit), 40])
offset = Keyword.get(options, :offset, 0)
author = Keyword.get(options, :author)
Activity Activity
|> Activity.with_preloaded_object() |> Activity.with_preloaded_object()
@ -20,15 +23,23 @@ def search(user, search_query) do
|> restrict_public() |> restrict_public()
|> query_with(index_type, search_query) |> query_with(index_type, search_query)
|> maybe_restrict_local(user) |> maybe_restrict_local(user)
|> Repo.all() |> maybe_restrict_author(author)
|> Pagination.fetch_paginated(%{"offset" => offset, "limit" => limit}, :offset)
|> maybe_fetch(user, search_query) |> maybe_fetch(user, search_query)
end end
def maybe_restrict_author(query, %User{} = author) do
from([a, o] in query,
where: a.actor == ^author.ap_id
)
end
def maybe_restrict_author(query, _), do: query
defp restrict_public(q) do defp restrict_public(q) do
from([a, o] in q, from([a, o] in q,
where: fragment("?->>'type' = 'Create'", a.data), where: fragment("?->>'type' = 'Create'", a.data),
where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients, where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients
limit: 40
) )
end end

View file

@ -155,11 +155,7 @@ def start(_type, _args) do
start: {Pleroma.Web.Endpoint, :start_link, []}, start: {Pleroma.Web.Endpoint, :start_link, []},
type: :supervisor type: :supervisor
}, },
%{id: Pleroma.Gopher.Server, start: {Pleroma.Gopher.Server, :start_link, []}}, %{id: Pleroma.Gopher.Server, start: {Pleroma.Gopher.Server, :start_link, []}}
%{
id: Pleroma.User.SynchronizationWorker,
start: {Pleroma.User.SynchronizationWorker, :start_link, []}
}
] ]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.BBS.Authenticator do defmodule Pleroma.BBS.Authenticator do
use Sshd.PasswordAuthenticator use Sshd.PasswordAuthenticator
alias Comeonin.Pbkdf2 alias Comeonin.Pbkdf2

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.BBS.Handler do defmodule Pleroma.BBS.Handler do
use Sshd.ShellHandler use Sshd.ShellHandler
alias Pleroma.Activity alias Pleroma.Activity

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Bookmark do defmodule Pleroma.Bookmark do
use Ecto.Schema use Ecto.Schema

View file

@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha do defmodule Pleroma.Captcha do
import Pleroma.Web.Gettext
alias Calendar.DateTime alias Calendar.DateTime
alias Plug.Crypto.KeyGenerator alias Plug.Crypto.KeyGenerator
alias Plug.Crypto.MessageEncryptor alias Plug.Crypto.MessageEncryptor
@ -83,10 +85,11 @@ def handle_call({:validate, token, captcha, answer_data}, _from, state) do
with {:ok, data} <- MessageEncryptor.decrypt(answer_data, secret, sign_secret), with {:ok, data} <- MessageEncryptor.decrypt(answer_data, secret, sign_secret),
%{at: at, answer_data: answer_md5} <- :erlang.binary_to_term(data) do %{at: at, answer_data: answer_md5} <- :erlang.binary_to_term(data) do
try do try do
if DateTime.before?(at, valid_if_after), do: throw({:error, "CAPTCHA expired"}) if DateTime.before?(at, valid_if_after),
do: throw({:error, dgettext("errors", "CAPTCHA expired")})
if not is_nil(Cachex.get!(:used_captcha_cache, token)), if not is_nil(Cachex.get!(:used_captcha_cache, token)),
do: throw({:error, "CAPTCHA already used"}) do: throw({:error, dgettext("errors", "CAPTCHA already used")})
res = method().validate(token, captcha, answer_md5) res = method().validate(token, captcha, answer_md5)
# Throw if an error occurs # Throw if an error occurs
@ -101,7 +104,7 @@ def handle_call({:validate, token, captcha, answer_data}, _from, state) do
:throw, e -> e :throw, e -> e
end end
else else
_ -> {:error, "Invalid answer data"} _ -> {:error, dgettext("errors", "Invalid answer data")}
end end
{:reply, result, state} {:reply, result, state}

View file

@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha.Kocaptcha do defmodule Pleroma.Captcha.Kocaptcha do
import Pleroma.Web.Gettext
alias Pleroma.Captcha.Service alias Pleroma.Captcha.Service
@behaviour Service @behaviour Service
@ -12,7 +13,7 @@ def new do
case Tesla.get(endpoint <> "/new") do case Tesla.get(endpoint <> "/new") do
{:error, _} -> {:error, _} ->
%{error: "Kocaptcha service unavailable"} %{error: dgettext("errors", "Kocaptcha service unavailable")}
{:ok, res} -> {:ok, res} ->
json_resp = Jason.decode!(res.body) json_resp = Jason.decode!(res.body)
@ -32,6 +33,6 @@ def validate(_token, captcha, answer_data) do
if not is_nil(captcha) and if not is_nil(captcha) and
:crypto.hash(:md5, captcha) |> Base.encode16() == String.upcase(answer_data), :crypto.hash(:md5, captcha) |> Base.encode16() == String.upcase(answer_data),
do: :ok, do: :ok,
else: {:error, "Invalid CAPTCHA"} else: {:error, dgettext("errors", "Invalid CAPTCHA")}
end end
end end

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.TransferTask do defmodule Pleroma.Config.TransferTask do
use Task use Task
alias Pleroma.Web.AdminAPI.Config alias Pleroma.Web.AdminAPI.Config

View file

@ -3,11 +3,58 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.Mailer do defmodule Pleroma.Emails.Mailer do
use Swoosh.Mailer, otp_app: :pleroma @moduledoc """
Defines the Pleroma mailer.
The module contains functions to delivery email using Swoosh.Mailer.
"""
alias Swoosh.DeliveryError
@otp_app :pleroma
@mailer_config [otp: :pleroma]
@spec enabled?() :: boolean()
def enabled?, do: Pleroma.Config.get([__MODULE__, :enabled])
@doc "add email to queue"
def deliver_async(email, config \\ []) do def deliver_async(email, config \\ []) do
PleromaJobQueue.enqueue(:mailer, __MODULE__, [:deliver_async, email, config]) PleromaJobQueue.enqueue(:mailer, __MODULE__, [:deliver_async, email, config])
end end
@doc "callback to perform send email from queue"
def perform(:deliver_async, email, config), do: deliver(email, config) def perform(:deliver_async, email, config), do: deliver(email, config)
@spec deliver(Swoosh.Email.t(), Keyword.t()) :: {:ok, term} | {:error, term}
def deliver(email, config \\ [])
def deliver(email, config) do
case enabled?() do
true -> Swoosh.Mailer.deliver(email, parse_config(config))
false -> {:error, :deliveries_disabled}
end
end
@spec deliver!(Swoosh.Email.t(), Keyword.t()) :: term | no_return
def deliver!(email, config \\ [])
def deliver!(email, config) do
case deliver(email, config) do
{:ok, result} -> result
{:error, reason} -> raise DeliveryError, reason: reason
end
end
@on_load :validate_dependency
@doc false
def validate_dependency do
parse_config([])
|> Keyword.get(:adapter)
|> Swoosh.Mailer.validate_dependency()
end
defp parse_config(config) do
Swoosh.Mailer.parse_config(@otp_app, __MODULE__, @mailer_config, config)
end
end end

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Instances do defmodule Pleroma.Instances do
@moduledoc "Instances context." @moduledoc "Instances context."

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Instances.Instance do defmodule Pleroma.Instances.Instance do
@moduledoc "Instance." @moduledoc "Instance."

View file

@ -44,7 +44,15 @@ def get_by_ap_id(ap_id) do
Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id))) Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id)))
end end
defp warn_on_no_object_preloaded(ap_id) do
"Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object"
|> Logger.debug()
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
end
def normalize(_, fetch_remote \\ true, options \\ []) def normalize(_, fetch_remote \\ true, options \\ [])
# If we pass an Activity to Object.normalize(), we can try to use the preloaded object. # If we pass an Activity to Object.normalize(), we can try to use the preloaded object.
# Use this whenever possible, especially when walking graphs in an O(N) loop! # Use this whenever possible, especially when walking graphs in an O(N) loop!
def normalize(%Object{} = object, _, _), do: object def normalize(%Object{} = object, _, _), do: object
@ -55,25 +63,15 @@ def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}, _, _) do
%Object{id: "pleroma:fake_object_id", data: data} %Object{id: "pleroma:fake_object_id", data: data}
end end
# Catch and log Object.normalize() calls where the Activity's child object is not # No preloaded object
# preloaded.
def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}, fetch_remote, _) do def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}, fetch_remote, _) do
Logger.debug( warn_on_no_object_preloaded(ap_id)
"Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!"
)
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
normalize(ap_id, fetch_remote) normalize(ap_id, fetch_remote)
end end
# No preloaded object
def normalize(%Activity{data: %{"object" => ap_id}}, fetch_remote, _) do def normalize(%Activity{data: %{"object" => ap_id}}, fetch_remote, _) do
Logger.debug( warn_on_no_object_preloaded(ap_id)
"Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!"
)
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
normalize(ap_id, fetch_remote) normalize(ap_id, fetch_remote)
end end

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Object.Fetcher do defmodule Pleroma.Object.Fetcher do
alias Pleroma.HTTP alias Pleroma.HTTP
alias Pleroma.Object alias Pleroma.Object

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ObjectTombstone do defmodule Pleroma.ObjectTombstone do
@enforce_keys [:id, :formerType, :deleted] @enforce_keys [:id, :formerType, :deleted]
defstruct [:id, :formerType, :deleted, type: "Tombstone"] defstruct [:id, :formerType, :deleted, type: "Tombstone"]

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Pagination do defmodule Pleroma.Pagination do
@moduledoc """ @moduledoc """
Implements Mastodon-compatible pagination. Implements Mastodon-compatible pagination.
@ -10,16 +14,28 @@ defmodule Pleroma.Pagination do
@default_limit 20 @default_limit 20
def fetch_paginated(query, params) do def fetch_paginated(query, params, type \\ :keyset)
def fetch_paginated(query, params, :keyset) do
options = cast_params(params) options = cast_params(params)
query query
|> paginate(options) |> paginate(options, :keyset)
|> Repo.all() |> Repo.all()
|> enforce_order(options) |> enforce_order(options)
end end
def paginate(query, options) do def fetch_paginated(query, params, :offset) do
options = cast_params(params)
query
|> paginate(options, :offset)
|> Repo.all()
end
def paginate(query, options, method \\ :keyset)
def paginate(query, options, :keyset) do
query query
|> restrict(:min_id, options) |> restrict(:min_id, options)
|> restrict(:since_id, options) |> restrict(:since_id, options)
@ -28,11 +44,18 @@ def paginate(query, options) do
|> restrict(:limit, options) |> restrict(:limit, options)
end end
def paginate(query, options, :offset) do
query
|> restrict(:offset, options)
|> restrict(:limit, options)
end
defp cast_params(params) do defp cast_params(params) do
param_types = %{ param_types = %{
min_id: :string, min_id: :string,
since_id: :string, since_id: :string,
max_id: :string, max_id: :string,
offset: :integer,
limit: :integer limit: :integer
} }
@ -66,6 +89,10 @@ defp restrict(query, :order, _options) do
order_by(query, [u], fragment("? desc nulls last", u.id)) order_by(query, [u], fragment("? desc nulls last", u.id))
end end
defp restrict(query, :offset, %{offset: offset}) do
offset(query, ^offset)
end
defp restrict(query, :limit, options) do defp restrict(query, :limit, options) do
limit = Map.get(options, :limit, @default_limit) limit = Map.get(options, :limit, @default_limit)

View file

@ -4,6 +4,7 @@
defmodule Pleroma.Plugs.EnsureAuthenticatedPlug do defmodule Pleroma.Plugs.EnsureAuthenticatedPlug do
import Plug.Conn import Plug.Conn
import Pleroma.Web.TranslationHelpers
alias Pleroma.User alias Pleroma.User
def init(options) do def init(options) do
@ -16,8 +17,7 @@ def call(%{assigns: %{user: %User{}}} = conn, _) do
def call(conn, _) do def call(conn, _) do
conn conn
|> put_resp_content_type("application/json") |> render_error(:forbidden, "Invalid credentials.")
|> send_resp(403, Jason.encode!(%{error: "Invalid credentials."}))
|> halt |> halt
end end
end end

View file

@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug do defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug do
import Pleroma.Web.TranslationHelpers
import Plug.Conn import Plug.Conn
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.User alias Pleroma.User
@ -23,8 +24,7 @@ def call(conn, _) do
{false, _} -> {false, _} ->
conn conn
|> put_resp_content_type("application/json") |> render_error(:forbidden, "This resource requires authentication.")
|> send_resp(403, Jason.encode!(%{error: "This resource requires authentication."}))
|> halt |> halt
end end
end end

View file

@ -4,6 +4,7 @@
defmodule Pleroma.Plugs.OAuthScopesPlug do defmodule Pleroma.Plugs.OAuthScopesPlug do
import Plug.Conn import Plug.Conn
import Pleroma.Web.Gettext
@behaviour Plug @behaviour Plug
@ -30,11 +31,14 @@ def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
true -> true ->
missing_scopes = scopes -- token.scopes missing_scopes = scopes -- token.scopes
error_message = "Insufficient permissions: #{Enum.join(missing_scopes, " #{op} ")}." permissions = Enum.join(missing_scopes, " #{op} ")
error_message =
dgettext("errors", "Insufficient permissions: %{permissions}.", permissions: permissions)
conn conn
|> put_resp_content_type("application/json") |> put_resp_content_type("application/json")
|> send_resp(403, Jason.encode!(%{error: error_message})) |> send_resp(:forbidden, Jason.encode!(%{error: error_message}))
|> halt() |> halt()
end end
end end

View file

@ -44,8 +44,7 @@ defmodule Pleroma.Plugs.RateLimiter do
... ...
end end
""" """
import Pleroma.Web.TranslationHelpers
import Phoenix.Controller, only: [json: 2]
import Plug.Conn import Plug.Conn
alias Pleroma.User alias Pleroma.User
@ -63,7 +62,7 @@ def call(conn, nil), do: conn
def call(conn, opts) do def call(conn, opts) do
case check_rate(conn, opts) do case check_rate(conn, opts) do
{:ok, _count} -> conn {:ok, _count} -> conn
{:error, _count} -> render_error(conn) {:error, _count} -> render_throttled_error(conn)
end end
end end
@ -85,10 +84,9 @@ def ip(%{remote_ip: remote_ip}) do
|> Enum.join(".") |> Enum.join(".")
end end
defp render_error(conn) do defp render_throttled_error(conn) do
conn conn
|> put_status(:too_many_requests) |> render_error(:too_many_requests, "Throttled")
|> json(%{error: "Throttled"})
|> halt() |> halt()
end end
end end

View file

@ -0,0 +1,63 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
# NOTE: this module is based on https://github.com/smeevil/set_locale
defmodule Pleroma.Plugs.SetLocalePlug do
import Plug.Conn, only: [get_req_header: 2, assign: 3]
def init(_), do: nil
def call(conn, _) do
locale = get_locale_from_header(conn) || Gettext.get_locale()
Gettext.put_locale(locale)
assign(conn, :locale, locale)
end
defp get_locale_from_header(conn) do
conn
|> extract_accept_language()
|> Enum.find(&supported_locale?/1)
end
defp extract_accept_language(conn) do
case get_req_header(conn, "accept-language") do
[value | _] ->
value
|> String.split(",")
|> Enum.map(&parse_language_option/1)
|> Enum.sort(&(&1.quality > &2.quality))
|> Enum.map(& &1.tag)
|> Enum.reject(&is_nil/1)
|> ensure_language_fallbacks()
_ ->
[]
end
end
defp supported_locale?(locale) do
Pleroma.Web.Gettext
|> Gettext.known_locales()
|> Enum.member?(locale)
end
defp parse_language_option(string) do
captures = Regex.named_captures(~r/^\s?(?<tag>[\w\-]+)(?:;q=(?<quality>[\d\.]+))?$/i, string)
quality =
case Float.parse(captures["quality"] || "1.0") do
{val, _} -> val
:error -> 1.0
end
%{tag: captures["tag"], quality: quality}
end
defp ensure_language_fallbacks(tags) do
Enum.flat_map(tags, fn tag ->
[language | _] = String.split(tag, "-")
if Enum.member?(tags, language), do: [tag], else: [tag, language]
end)
end
end

View file

@ -7,6 +7,7 @@ defmodule Pleroma.Plugs.UploadedMedia do
""" """
import Plug.Conn import Plug.Conn
import Pleroma.Web.Gettext
require Logger require Logger
@behaviour Plug @behaviour Plug
@ -45,7 +46,7 @@ def call(%{request_path: <<"/", @path, "/", file::binary>>} = conn, opts) do
else else
_ -> _ ->
conn conn
|> send_resp(500, "Failed") |> send_resp(:internal_server_error, dgettext("errors", "Failed"))
|> halt() |> halt()
end end
end end
@ -64,7 +65,7 @@ defp get_media(conn, {:static_dir, directory}, _, opts) do
conn conn
else else
conn conn
|> send_resp(404, "Not found") |> send_resp(:not_found, dgettext("errors", "Not found"))
|> halt() |> halt()
end end
end end
@ -84,7 +85,7 @@ defp get_media(conn, unknown, _, _) do
Logger.error("#{__MODULE__}: Unknown get startegy: #{inspect(unknown)}") Logger.error("#{__MODULE__}: Unknown get startegy: #{inspect(unknown)}")
conn conn
|> send_resp(500, "Internal Error") |> send_resp(:internal_server_error, dgettext("errors", "Internal Error"))
|> halt() |> halt()
end end
end end

View file

@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.UserIsAdminPlug do defmodule Pleroma.Plugs.UserIsAdminPlug do
import Pleroma.Web.TranslationHelpers
import Plug.Conn import Plug.Conn
alias Pleroma.User alias Pleroma.User
@ -16,8 +17,7 @@ def call(%{assigns: %{user: %User{info: %{is_admin: true}}}} = conn, _) do
def call(conn, _) do def call(conn, _) do
conn conn
|> put_resp_content_type("application/json") |> render_error(:forbidden, "User is not admin.")
|> send_resp(403, Jason.encode!(%{error: "User is not admin."}))
|> halt |> halt
end end
end end

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReverseProxy.Client do defmodule Pleroma.ReverseProxy.Client do
@callback request(atom(), String.t(), [tuple()], String.t(), list()) :: @callback request(atom(), String.t(), [tuple()], String.t(), list()) ::
{:ok, pos_integer(), [tuple()], reference() | map()} {:ok, pos_integer(), [tuple()], reference() | map()}

View file

@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Uploaders.Uploader do defmodule Pleroma.Uploaders.Uploader do
import Pleroma.Web.Gettext
@moduledoc """ @moduledoc """
Defines the contract to put and get an uploaded file to any backend. Defines the contract to put and get an uploaded file to any backend.
""" """
@ -66,7 +68,7 @@ defp handle_callback(uploader, upload) do
{:error, error} {:error, error}
end end
after after
30_000 -> {:error, "Uploader callback timeout"} 30_000 -> {:error, dgettext("errors", "Uploader callback timeout")}
end end
end end
end end

View file

@ -52,6 +52,7 @@ defmodule Pleroma.User do
field(:avatar, :map) field(:avatar, :map)
field(:local, :boolean, default: true) field(:local, :boolean, default: true)
field(:follower_address, :string) field(:follower_address, :string)
field(:following_address, :string)
field(:search_rank, :float, virtual: true) field(:search_rank, :float, virtual: true)
field(:search_type, :integer, virtual: true) field(:search_type, :integer, virtual: true)
field(:tags, {:array, :string}, default: []) field(:tags, {:array, :string}, default: [])
@ -108,6 +109,10 @@ def ap_id(%User{nickname: nickname}) do
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers" def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
@spec ap_following(User.t()) :: Sring.t()
def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa
def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
def user_info(%User{} = user, args \\ %{}) do def user_info(%User{} = user, args \\ %{}) do
following_count = following_count =
if args[:following_count], do: args[:following_count], else: following_count(user) if args[:following_count], do: args[:following_count], else: following_count(user)
@ -129,6 +134,7 @@ def set_info_cache(user, args) do
Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user, args)) Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user, args))
end end
@spec restrict_deactivated(Ecto.Query.t()) :: Ecto.Query.t()
def restrict_deactivated(query) do def restrict_deactivated(query) do
from(u in query, from(u in query,
where: not fragment("? \\? 'deactivated' AND ?->'deactivated' @> 'true'", u.info, u.info) where: not fragment("? \\? 'deactivated' AND ?->'deactivated' @> 'true'", u.info, u.info)
@ -163,9 +169,10 @@ def remote_user_creation(params) do
if changes.valid? do if changes.valid? do
case info_cng.changes[:source_data] do case info_cng.changes[:source_data] do
%{"followers" => followers} -> %{"followers" => followers, "following" => following} ->
changes changes
|> put_change(:follower_address, followers) |> put_change(:follower_address, followers)
|> put_change(:following_address, following)
_ -> _ ->
followers = User.ap_followers(%User{nickname: changes.changes[:nickname]}) followers = User.ap_followers(%User{nickname: changes.changes[:nickname]})
@ -197,7 +204,14 @@ def upgrade_changeset(struct, params \\ %{}) do
|> User.Info.user_upgrade(params[:info]) |> User.Info.user_upgrade(params[:info])
struct struct
|> cast(params, [:bio, :name, :follower_address, :avatar, :last_refreshed_at]) |> cast(params, [
:bio,
:name,
:follower_address,
:following_address,
:avatar,
:last_refreshed_at
])
|> unique_constraint(:nickname) |> unique_constraint(:nickname)
|> validate_format(:nickname, local_nickname_regex()) |> validate_format(:nickname, local_nickname_regex())
|> validate_length(:bio, max: 5000) |> validate_length(:bio, max: 5000)
@ -938,6 +952,8 @@ def delete(%User{} = user),
@spec perform(atom(), User.t()) :: {:ok, User.t()} @spec perform(atom(), User.t()) :: {:ok, User.t()}
def perform(:delete, %User{} = user) do def perform(:delete, %User{} = user) do
{:ok, _user} = ActivityPub.delete(user)
# Remove all relationships # Remove all relationships
{:ok, followers} = User.get_followers(user) {:ok, followers} = User.get_followers(user)
@ -954,8 +970,8 @@ def perform(:delete, %User{} = user) do
end) end)
delete_user_activities(user) delete_user_activities(user)
invalidate_cache(user)
{:ok, _user} = Repo.delete(user) Repo.delete(user)
end end
@spec perform(atom(), User.t()) :: {:ok, User.t()} @spec perform(atom(), User.t()) :: {:ok, User.t()}
@ -1011,42 +1027,20 @@ def perform(:follow_import, %User{} = follower, followed_identifiers)
) )
end end
@spec sync_follow_counter() :: :ok @spec external_users_query() :: Ecto.Query.t()
def sync_follow_counter, def external_users_query do
do: PleromaJobQueue.enqueue(:background, __MODULE__, [:sync_follow_counters]) User.Query.build(%{
external: true,
@spec perform(:sync_follow_counters) :: :ok active: true,
def perform(:sync_follow_counters) do order_by: :id
{:ok, _pid} = Agent.start_link(fn -> %{} end, name: :domain_errors) })
config = Pleroma.Config.get([:instance, :external_user_synchronization])
:ok = sync_follow_counters(config)
Agent.stop(:domain_errors)
end
@spec sync_follow_counters(keyword()) :: :ok
def sync_follow_counters(opts \\ []) do
users = external_users(opts)
if length(users) > 0 do
errors = Agent.get(:domain_errors, fn state -> state end)
{last, updated_errors} = User.Synchronization.call(users, errors, opts)
Agent.update(:domain_errors, fn _state -> updated_errors end)
sync_follow_counters(max_id: last.id, limit: opts[:limit])
else
:ok
end
end end
@spec external_users(keyword()) :: [User.t()] @spec external_users(keyword()) :: [User.t()]
def external_users(opts \\ []) do def external_users(opts \\ []) do
query = query =
User.Query.build(%{ external_users_query()
external: true, |> select([u], struct(u, [:id, :ap_id, :info]))
active: true,
order_by: :id,
select: [:id, :ap_id, :info]
})
query = query =
if opts[:max_id], if opts[:max_id],

View file

@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.Search do defmodule Pleroma.User.Search do
alias Pleroma.Pagination
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
import Ecto.Query import Ecto.Query
@ -18,8 +19,7 @@ def search(query_string, opts \\ []) do
for_user = Keyword.get(opts, :for_user) for_user = Keyword.get(opts, :for_user)
# Strip the beginning @ off if there is a query query_string = format_query(query_string)
query_string = String.trim_leading(query_string, "@")
maybe_resolve(resolve, for_user, query_string) maybe_resolve(resolve, for_user, query_string)
@ -33,13 +33,24 @@ def search(query_string, opts \\ []) do
query_string query_string
|> search_query(for_user, following) |> search_query(for_user, following)
|> paginate(result_limit, offset) |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset)
|> Repo.all()
end) end)
results results
end end
defp format_query(query_string) do
# Strip the beginning @ off if there is a query
query_string = String.trim_leading(query_string, "@")
with [name, domain] <- String.split(query_string, "@"),
formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:]+/, "") do
name <> "@" <> to_string(:idna.encode(formatted_domain))
else
_ -> query_string
end
end
defp search_query(query_string, for_user, following) do defp search_query(query_string, for_user, following) do
for_user for_user
|> base_query(following) |> base_query(following)
@ -76,10 +87,6 @@ defp filter_blocked_domains(query, %User{info: %{domain_blocks: domain_blocks}})
defp filter_blocked_domains(query, _), do: query defp filter_blocked_domains(query, _), do: query
defp paginate(query, limit, offset) do
from(q in query, limit: ^limit, offset: ^offset)
end
defp union_subqueries({fts_subquery, trigram_subquery}) do defp union_subqueries({fts_subquery, trigram_subquery}) do
from(s in trigram_subquery, union_all: ^fts_subquery) from(s in trigram_subquery, union_all: ^fts_subquery)
end end
@ -151,7 +158,7 @@ defp boost_search_rank_query(query, for_user) do
defp fts_search_subquery(query, term) do defp fts_search_subquery(query, term) do
processed_query = processed_query =
String.trim_trailing(term, "@" <> local_domain()) String.trim_trailing(term, "@" <> local_domain())
|> String.replace(~r/\W+/, " ") |> String.replace(~r/[!-\/|@|[-`|{-~|:-?]+/, " ")
|> String.trim() |> String.trim()
|> String.split() |> String.split()
|> Enum.map(&(&1 <> ":*")) |> Enum.map(&(&1 <> ":*"))

View file

@ -1,60 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.Synchronization do
alias Pleroma.HTTP
alias Pleroma.User
@spec call([User.t()], map(), keyword()) :: {User.t(), map()}
def call(users, errors, opts \\ []) do
do_call(users, errors, opts)
end
defp do_call([user | []], errors, opts) do
updated = fetch_counters(user, errors, opts)
{user, updated}
end
defp do_call([user | others], errors, opts) do
updated = fetch_counters(user, errors, opts)
do_call(others, updated, opts)
end
defp fetch_counters(user, errors, opts) do
%{host: host} = URI.parse(user.ap_id)
info = %{}
{following, errors} = fetch_counter(user.ap_id <> "/following", host, errors, opts)
info = if following, do: Map.put(info, :following_count, following), else: info
{followers, errors} = fetch_counter(user.ap_id <> "/followers", host, errors, opts)
info = if followers, do: Map.put(info, :follower_count, followers), else: info
User.set_info_cache(user, info)
errors
end
defp available_domain?(domain, errors, opts) do
max_retries = Keyword.get(opts, :max_retries, 3)
not (Map.has_key?(errors, domain) && errors[domain] >= max_retries)
end
defp fetch_counter(url, host, errors, opts) do
with true <- available_domain?(host, errors, opts),
{:ok, %{body: body, status: code}} when code in 200..299 <-
HTTP.get(
url,
[{:Accept, "application/activity+json"}]
),
{:ok, data} <- Jason.decode(body) do
{data["totalItems"], errors}
else
false ->
{nil, errors}
_ ->
{nil, Map.update(errors, host, 1, &(&1 + 1))}
end
end
end

View file

@ -1,32 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-onl
defmodule Pleroma.User.SynchronizationWorker do
use GenServer
def start_link do
config = Pleroma.Config.get([:instance, :external_user_synchronization])
if config[:enabled] do
GenServer.start_link(__MODULE__, interval: config[:interval])
else
:ignore
end
end
def init(opts) do
schedule_next(opts)
{:ok, opts}
end
def handle_info(:sync_follow_counters, opts) do
Pleroma.User.sync_follow_counter()
schedule_next(opts)
{:noreply, opts}
end
defp schedule_next(opts) do
Process.send_after(self(), :sync_follow_counters, opts[:interval])
end
end

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.WelcomeMessage do defmodule Pleroma.User.WelcomeMessage do
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI

View file

@ -405,6 +405,19 @@ def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
end end
end end
def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
with data <- %{
"to" => [follower_address],
"type" => "Delete",
"actor" => ap_id,
"object" => %{"type" => "Person", "id" => ap_id}
},
{:ok, activity} <- insert(data, true, true),
:ok <- maybe_federate(activity) do
{:ok, user}
end
end
def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
user = User.get_cached_by_ap_id(actor) user = User.get_cached_by_ap_id(actor)
to = (object.data["to"] || []) ++ (object.data["cc"] || []) to = (object.data["to"] || []) ++ (object.data["cc"] || [])
@ -981,6 +994,7 @@ defp object_to_user_data(data) do
avatar: avatar, avatar: avatar,
name: data["name"], name: data["name"],
follower_address: data["followers"], follower_address: data["followers"],
following_address: data["following"],
bio: data["summary"] bio: data["summary"]
} }

View file

@ -31,9 +31,8 @@ def relay_active?(conn, _) do
conn conn
else else
conn conn
|> put_status(404) |> render_error(:not_found, "not found")
|> json(%{error: "not found"}) |> halt()
|> halt
end end
end end
@ -190,7 +189,7 @@ def inbox(conn, params) do
Logger.info(inspect(conn.req_headers)) Logger.info(inspect(conn.req_headers))
end end
json(conn, "error") json(conn, dgettext("errors", "error"))
end end
def relay(conn, _params) do def relay(conn, _params) do
@ -218,9 +217,15 @@ def read_inbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = par
|> put_resp_header("content-type", "application/activity+json") |> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("inbox.json", %{user: user, max_id: params["max_id"]})) |> json(UserView.render("inbox.json", %{user: user, max_id: params["max_id"]}))
else else
err =
dgettext("errors", "can't read inbox of %{nickname} as %{as_nickname}",
nickname: nickname,
as_nickname: user.nickname
)
conn conn
|> put_status(:forbidden) |> put_status(:forbidden)
|> json("can't read inbox of #{nickname} as #{user.nickname}") |> json(err)
end end
end end
@ -246,7 +251,7 @@ def handle_user_activity(user, %{"type" => "Delete"} = params) do
{:ok, delete} <- ActivityPub.delete(object) do {:ok, delete} <- ActivityPub.delete(object) do
{:ok, delete} {:ok, delete}
else else
_ -> {:error, "Can't delete object"} _ -> {:error, dgettext("errors", "Can't delete object")}
end end
end end
@ -255,12 +260,12 @@ def handle_user_activity(user, %{"type" => "Like"} = params) do
{:ok, activity, _object} <- ActivityPub.like(user, object) do {:ok, activity, _object} <- ActivityPub.like(user, object) do
{:ok, activity} {:ok, activity}
else else
_ -> {:error, "Can't like object"} _ -> {:error, dgettext("errors", "Can't like object")}
end end
end end
def handle_user_activity(_, _) do def handle_user_activity(_, _) do
{:error, "Unhandled activity type"} {:error, dgettext("errors", "Unhandled activity type")}
end end
def update_outbox( def update_outbox(
@ -288,22 +293,28 @@ def update_outbox(
|> json(message) |> json(message)
end end
else else
err =
dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",
nickname: nickname,
as_nickname: user.nickname
)
conn conn
|> put_status(:forbidden) |> put_status(:forbidden)
|> json("can't update outbox of #{nickname} as #{user.nickname}") |> json(err)
end end
end end
def errors(conn, {:error, :not_found}) do def errors(conn, {:error, :not_found}) do
conn conn
|> put_status(404) |> put_status(:not_found)
|> json("Not found") |> json(dgettext("errors", "Not found"))
end end
def errors(conn, _e) do def errors(conn, _e) do
conn conn
|> put_status(500) |> put_status(:internal_server_error)
|> json("error") |> json(dgettext("errors", "error"))
end end
defp set_requester_reachable(%Plug.Conn{} = conn, _) do defp set_requester_reachable(%Plug.Conn{} = conn, _) do

View file

@ -9,8 +9,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
@behaviour Pleroma.Web.ActivityPub.MRF @behaviour Pleroma.Web.ActivityPub.MRF
@reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless]) @reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
def filter_by_summary( def filter_by_summary(
%{"summary" => parent_summary} = _parent, %{data: %{"summary" => parent_summary}} = _in_reply_to,
%{"summary" => child_summary} = child %{"summary" => child_summary} = child
) )
when not is_nil(child_summary) and byte_size(child_summary) > 0 and when not is_nil(child_summary) and byte_size(child_summary) > 0 and
@ -24,17 +25,13 @@ def filter_by_summary(
end end
end end
def filter_by_summary(_parent, child), do: child def filter_by_summary(_in_reply_to, child), do: child
def filter(%{"type" => activity_type} = object) when activity_type == "Create" do
child = object["object"]
in_reply_to = Object.normalize(child["inReplyTo"])
def filter(%{"type" => "Create", "object" => child_object} = object) do
child = child =
if(in_reply_to, child_object["inReplyTo"]
do: filter_by_summary(in_reply_to.data, child), |> Object.normalize(child_object["inReplyTo"])
else: child |> filter_by_summary(child_object)
)
object = Map.put(object, "object", child) object = Map.put(object, "object", child)

View file

@ -10,19 +10,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do
def filter( def filter(
%{ %{
"type" => "Create", "type" => "Create",
"object" => %{"content" => content, "attachment" => _attachment} = child_object "object" => %{"content" => content, "attachment" => _} = _child_object
} = object } = object
) )
when content in [".", "<p>.</p>"] do when content in [".", "<p>.</p>"] do
child_object = {:ok, put_in(object, ["object", "content"], "")}
child_object
|> Map.put("content", "")
object =
object
|> Map.put("object", child_object)
{:ok, object}
end end
@impl true @impl true

View file

@ -8,18 +8,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
@behaviour Pleroma.Web.ActivityPub.MRF @behaviour Pleroma.Web.ActivityPub.MRF
def filter(%{"type" => activity_type} = object) when activity_type == "Create" do def filter(%{"type" => "Create", "object" => child_object} = object) do
scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy]) scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy])
child = object["object"]
content = content =
child["content"] child_object["content"]
|> HTML.filter_tags(scrub_policy) |> HTML.filter_tags(scrub_policy)
child = Map.put(child, "content", content) object = put_in(object, ["object", "content"], content)
object = Map.put(object, "object", child)
{:ok, object} {:ok, object}
end end

View file

@ -3,46 +3,42 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
alias Pleroma.User
@moduledoc "Rejects non-public (followers-only, direct) activities" @moduledoc "Rejects non-public (followers-only, direct) activities"
alias Pleroma.Config
alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF @behaviour Pleroma.Web.ActivityPub.MRF
@public "https://www.w3.org/ns/activitystreams#Public"
@impl true @impl true
def filter(%{"type" => "Create"} = object) do def filter(%{"type" => "Create"} = object) do
user = User.get_cached_by_ap_id(object["actor"]) user = User.get_cached_by_ap_id(object["actor"])
public = "https://www.w3.org/ns/activitystreams#Public"
# Determine visibility # Determine visibility
visibility = visibility =
cond do cond do
public in object["to"] -> "public" @public in object["to"] -> "public"
public in object["cc"] -> "unlisted" @public in object["cc"] -> "unlisted"
user.follower_address in object["to"] -> "followers" user.follower_address in object["to"] -> "followers"
true -> "direct" true -> "direct"
end end
policy = Pleroma.Config.get(:mrf_rejectnonpublic) policy = Config.get(:mrf_rejectnonpublic)
case visibility do cond do
"public" -> visibility in ["public", "unlisted"] ->
{:ok, object} {:ok, object}
"unlisted" -> visibility == "followers" and Keyword.get(policy, :allow_followersonly) ->
{:ok, object} {:ok, object}
"followers" -> visibility == "direct" and Keyword.get(policy, :allow_direct) ->
with true <- Keyword.get(policy, :allow_followersonly) do
{:ok, object} {:ok, object}
else
_e -> {:reject, nil}
end
"direct" -> true ->
with true <- Keyword.get(policy, :allow_direct) do {:reject, nil}
{:ok, object}
else
_e -> {:reject, nil}
end
end end
end end

View file

@ -19,12 +19,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
- `mrf_tag:disable-any-subscription`: Reject any follow requests - `mrf_tag:disable-any-subscription`: Reject any follow requests
""" """
@public "https://www.w3.org/ns/activitystreams#Public"
defp get_tags(%User{tags: tags}) when is_list(tags), do: tags defp get_tags(%User{tags: tags}) when is_list(tags), do: tags
defp get_tags(_), do: [] defp get_tags(_), do: []
defp process_tag( defp process_tag(
"mrf_tag:media-force-nsfw", "mrf_tag:media-force-nsfw",
%{"type" => "Create", "object" => %{"attachment" => child_attachment} = object} = message %{
"type" => "Create",
"object" => %{"attachment" => child_attachment} = object
} = message
) )
when length(child_attachment) > 0 do when length(child_attachment) > 0 do
tags = (object["tag"] || []) ++ ["nsfw"] tags = (object["tag"] || []) ++ ["nsfw"]
@ -41,7 +46,10 @@ defp process_tag(
defp process_tag( defp process_tag(
"mrf_tag:media-strip", "mrf_tag:media-strip",
%{"type" => "Create", "object" => %{"attachment" => child_attachment} = object} = message %{
"type" => "Create",
"object" => %{"attachment" => child_attachment} = object
} = message
) )
when length(child_attachment) > 0 do when length(child_attachment) > 0 do
object = Map.delete(object, "attachment") object = Map.delete(object, "attachment")
@ -52,19 +60,22 @@ defp process_tag(
defp process_tag( defp process_tag(
"mrf_tag:force-unlisted", "mrf_tag:force-unlisted",
%{"type" => "Create", "to" => to, "cc" => cc, "actor" => actor} = message %{
"type" => "Create",
"to" => to,
"cc" => cc,
"actor" => actor,
"object" => object
} = message
) do ) do
user = User.get_cached_by_ap_id(actor) user = User.get_cached_by_ap_id(actor)
if Enum.member?(to, "https://www.w3.org/ns/activitystreams#Public") do if Enum.member?(to, @public) do
to = to = List.delete(to, @public) ++ [user.follower_address]
List.delete(to, "https://www.w3.org/ns/activitystreams#Public") ++ [user.follower_address] cc = List.delete(cc, user.follower_address) ++ [@public]
cc =
List.delete(cc, user.follower_address) ++ ["https://www.w3.org/ns/activitystreams#Public"]
object = object =
message["object"] object
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
@ -82,19 +93,22 @@ defp process_tag(
defp process_tag( defp process_tag(
"mrf_tag:sandbox", "mrf_tag:sandbox",
%{"type" => "Create", "to" => to, "cc" => cc, "actor" => actor} = message %{
"type" => "Create",
"to" => to,
"cc" => cc,
"actor" => actor,
"object" => object
} = message
) do ) do
user = User.get_cached_by_ap_id(actor) user = User.get_cached_by_ap_id(actor)
if Enum.member?(to, "https://www.w3.org/ns/activitystreams#Public") or if Enum.member?(to, @public) or Enum.member?(cc, @public) do
Enum.member?(cc, "https://www.w3.org/ns/activitystreams#Public") do to = List.delete(to, @public) ++ [user.follower_address]
to = cc = List.delete(cc, @public)
List.delete(to, "https://www.w3.org/ns/activitystreams#Public") ++ [user.follower_address]
cc = List.delete(cc, "https://www.w3.org/ns/activitystreams#Public")
object = object =
message["object"] object
|> Map.put("to", to) |> Map.put("to", to)
|> Map.put("cc", cc) |> Map.put("cc", cc)
@ -123,7 +137,8 @@ defp process_tag(
end end
end end
defp process_tag("mrf_tag:disable-any-subscription", %{"type" => "Follow"}), do: {:reject, nil} defp process_tag("mrf_tag:disable-any-subscription", %{"type" => "Follow"}),
do: {:reject, nil}
defp process_tag(_, message), do: {:ok, message} defp process_tag(_, message), do: {:ok, message}

View file

@ -21,7 +21,12 @@ defp filter_by_list(%{"actor" => actor} = object, allow_list) do
@impl true @impl true
def filter(%{"actor" => actor} = object) do def filter(%{"actor" => actor} = object) do
actor_info = URI.parse(actor) actor_info = URI.parse(actor)
allow_list = Config.get([:mrf_user_allowlist, String.to_atom(actor_info.host)], [])
allow_list =
Config.get(
[:mrf_user_allowlist, String.to_atom(actor_info.host)],
[]
)
filter_by_list(object, allow_list) filter_by_list(object, allow_list)
end end

View file

@ -641,7 +641,7 @@ def handle_incoming(
# an error or a tombstone. This would allow us to verify that a deletion actually took # an error or a tombstone. This would allow us to verify that a deletion actually took
# place. # place.
def handle_incoming( def handle_incoming(
%{"type" => "Delete", "object" => object_id, "actor" => _actor, "id" => _id} = data, %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = data,
_options _options
) do ) do
object_id = Utils.get_ap_id(object_id) object_id = Utils.get_ap_id(object_id)
@ -653,7 +653,30 @@ def handle_incoming(
{:ok, activity} <- ActivityPub.delete(object, false) do {:ok, activity} <- ActivityPub.delete(object, false) do
{:ok, activity} {:ok, activity}
else else
_e -> :error nil ->
case User.get_cached_by_ap_id(object_id) do
%User{ap_id: ^actor} = user ->
{:ok, followers} = User.get_followers(user)
Enum.each(followers, fn follower ->
User.unfollow(follower, user)
end)
{:ok, friends} = User.get_friends(user)
Enum.each(friends, fn followed ->
User.unfollow(user, followed)
end)
User.invalidate_cache(user)
Repo.delete(user)
nil ->
:error
end
_e ->
:error
end end
end end
@ -1064,6 +1087,10 @@ def upgrade_user_from_ap_id(ap_id) do
PleromaJobQueue.enqueue(:transmogrifier, __MODULE__, [:user_upgrade, user]) PleromaJobQueue.enqueue(:transmogrifier, __MODULE__, [:user_upgrade, user])
end end
if Pleroma.Config.get([:instance, :external_user_synchronization]) do
update_following_followers_counters(user)
end
{:ok, user} {:ok, user}
else else
%User{} = user -> {:ok, user} %User{} = user -> {:ok, user}
@ -1096,4 +1123,27 @@ def maybe_fix_user_object(data) do
data data
|> maybe_fix_user_url |> maybe_fix_user_url
end end
def update_following_followers_counters(user) do
info = %{}
following = fetch_counter(user.following_address)
info = if following, do: Map.put(info, :following_count, following), else: info
followers = fetch_counter(user.follower_address)
info = if followers, do: Map.put(info, :follower_count, followers), else: info
User.set_info_cache(user, info)
end
defp fetch_counter(url) do
with {:ok, %{body: body, status: code}} when code in 200..299 <-
Pleroma.HTTP.get(
url,
[{:Accept, "application/activity+json"}]
),
{:ok, data} <- Jason.decode(body) do
data["totalItems"]
end
end
end end

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Visibility do defmodule Pleroma.Web.ActivityPub.Visibility do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Object alias Pleroma.Object

View file

@ -160,9 +160,7 @@ def right_add(conn, %{"permission_group" => permission_group, "nickname" => nick
end end
def right_add(conn, _) do def right_add(conn, _) do
conn render_error(conn, :not_found, "No such permission_group")
|> put_status(404)
|> json(%{error: "No such permission_group"})
end end
def right_get(conn, %{"nickname" => nickname}) do def right_get(conn, %{"nickname" => nickname}) do
@ -184,9 +182,7 @@ def right_delete(
) )
when permission_group in ["moderator", "admin"] do when permission_group in ["moderator", "admin"] do
if admin_nickname == nickname do if admin_nickname == nickname do
conn render_error(conn, :forbidden, "You can't revoke your own admin status.")
|> put_status(403)
|> json(%{error: "You can't revoke your own admin status."})
else else
user = User.get_cached_by_nickname(nickname) user = User.get_cached_by_nickname(nickname)
@ -207,9 +203,7 @@ def right_delete(
end end
def right_delete(conn, _) do def right_delete(conn, _) do
conn render_error(conn, :not_found, "No such permission_group")
|> put_status(404)
|> json(%{error: "No such permission_group"})
end end
def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
@ -377,13 +371,13 @@ def config_update(conn, %{"configs" => configs}) do
if Pleroma.Config.get([:instance, :dynamic_configuration]) do if Pleroma.Config.get([:instance, :dynamic_configuration]) do
updated = updated =
Enum.map(configs, fn Enum.map(configs, fn
%{"group" => group, "key" => key, "value" => value} ->
{:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
config
%{"group" => group, "key" => key, "delete" => "true"} -> %{"group" => group, "key" => key, "delete" => "true"} ->
{:ok, _} = Config.delete(%{group: group, key: key}) {:ok, _} = Config.delete(%{group: group, key: key})
nil nil
%{"group" => group, "key" => key, "value" => value} ->
{:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
config
end) end)
|> Enum.reject(&is_nil(&1)) |> Enum.reject(&is_nil(&1))
@ -401,26 +395,26 @@ def config_update(conn, %{"configs" => configs}) do
def errors(conn, {:error, :not_found}) do def errors(conn, {:error, :not_found}) do
conn conn
|> put_status(404) |> put_status(:not_found)
|> json("Not found") |> json(dgettext("errors", "Not found"))
end end
def errors(conn, {:error, reason}) do def errors(conn, {:error, reason}) do
conn conn
|> put_status(400) |> put_status(:bad_request)
|> json(reason) |> json(reason)
end end
def errors(conn, {:param_cast, _}) do def errors(conn, {:param_cast, _}) do
conn conn
|> put_status(400) |> put_status(:bad_request)
|> json("Invalid parameters") |> json(dgettext("errors", "Invalid parameters"))
end end
def errors(conn, _) do def errors(conn, _) do
conn conn
|> put_status(500) |> put_status(:internal_server_error)
|> json("Something went wrong") |> json(dgettext("errors", "Something went wrong"))
end end
defp page_params(params) do defp page_params(params) do

View file

@ -5,6 +5,7 @@
defmodule Pleroma.Web.AdminAPI.Config do defmodule Pleroma.Web.AdminAPI.Config do
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset import Ecto.Changeset
import Pleroma.Web.Gettext
alias __MODULE__ alias __MODULE__
alias Pleroma.Repo alias Pleroma.Repo
@ -57,104 +58,95 @@ def delete(params) do
with %Config{} = config <- Config.get_by_params(params) do with %Config{} = config <- Config.get_by_params(params) do
Repo.delete(config) Repo.delete(config)
else else
nil -> {:error, "Config with params #{inspect(params)} not found"} nil ->
err =
dgettext("errors", "Config with params %{params} not found", params: inspect(params))
{:error, err}
end end
end end
@spec from_binary(binary()) :: term() @spec from_binary(binary()) :: term()
def from_binary(value), do: :erlang.binary_to_term(value) def from_binary(binary), do: :erlang.binary_to_term(binary)
@spec from_binary_to_map(binary()) :: any() @spec from_binary_with_convert(binary()) :: any()
def from_binary_to_map(binary) do def from_binary_with_convert(binary) do
from_binary(binary) from_binary(binary)
|> do_convert() |> do_convert()
end end
defp do_convert([{k, v}] = value) when is_list(value) and length(value) == 1, defp do_convert(entity) when is_list(entity) do
do: %{k => do_convert(v)} for v <- entity, into: [], do: do_convert(v)
end
defp do_convert(values) when is_list(values), do: for(val <- values, do: do_convert(val)) defp do_convert(entity) when is_map(entity) do
for {k, v} <- entity, into: %{}, do: {do_convert(k), do_convert(v)}
end
defp do_convert({k, v} = value) when is_tuple(value), defp do_convert({:dispatch, [entity]}), do: %{"tuple" => [":dispatch", [inspect(entity)]]}
do: %{k => do_convert(v)}
defp do_convert(value) when is_tuple(value), do: %{"tuple" => do_convert(Tuple.to_list(value))} defp do_convert(entity) when is_tuple(entity),
do: %{"tuple" => do_convert(Tuple.to_list(entity))}
defp do_convert(value) when is_binary(value) or is_map(value) or is_number(value), do: value defp do_convert(entity) when is_boolean(entity) or is_number(entity) or is_nil(entity),
do: entity
defp do_convert(value) when is_atom(value) do defp do_convert(entity) when is_atom(entity) do
string = to_string(value) string = to_string(entity)
if String.starts_with?(string, "Elixir."), if String.starts_with?(string, "Elixir."),
do: String.trim_leading(string, "Elixir."), do: do_convert(string),
else: value else: ":" <> string
end end
defp do_convert("Elixir." <> module_name), do: module_name
defp do_convert(entity) when is_binary(entity), do: entity
@spec transform(any()) :: binary() @spec transform(any()) :: binary()
def transform(%{"tuple" => _} = entity), do: :erlang.term_to_binary(do_transform(entity)) def transform(entity) when is_binary(entity) or is_map(entity) or is_list(entity) do
:erlang.term_to_binary(do_transform(entity))
def transform(entity) when is_map(entity) do
tuples =
for {k, v} <- entity,
into: [],
do: {if(is_atom(k), do: k, else: String.to_atom(k)), do_transform(v)}
Enum.reject(tuples, fn {_k, v} -> is_nil(v) end)
|> Enum.sort()
|> :erlang.term_to_binary()
end
def transform(entity) when is_list(entity) do
list = Enum.map(entity, &do_transform(&1))
:erlang.term_to_binary(list)
end end
def transform(entity), do: :erlang.term_to_binary(entity) def transform(entity), do: :erlang.term_to_binary(entity)
defp do_transform(%Regex{} = value) when is_map(value), do: value defp do_transform(%Regex{} = entity) when is_map(entity), do: entity
defp do_transform(%{"tuple" => [k, values] = entity}) when length(entity) == 2 do defp do_transform(%{"tuple" => [":dispatch", [entity]]}) do
{do_transform(k), do_transform(values)} cleaned_string = String.replace(entity, ~r/[^\w|^{:,[|^,|^[|^\]^}|^\/|^\.|^"]^\s/, "")
{dispatch_settings, []} = Code.eval_string(cleaned_string, [], requires: [], macros: [])
{:dispatch, [dispatch_settings]}
end end
defp do_transform(%{"tuple" => values}) do defp do_transform(%{"tuple" => entity}) do
Enum.reduce(values, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end) Enum.reduce(entity, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end)
end end
defp do_transform(value) when is_map(value) do defp do_transform(entity) when is_map(entity) do
values = for {key, val} <- value, into: [], do: {String.to_atom(key), do_transform(val)} for {k, v} <- entity, into: %{}, do: {do_transform(k), do_transform(v)}
Enum.sort(values)
end end
defp do_transform(value) when is_list(value) do defp do_transform(entity) when is_list(entity) do
Enum.map(value, &do_transform(&1)) for v <- entity, into: [], do: do_transform(v)
end end
defp do_transform(entity) when is_list(entity) and length(entity) == 1, do: hd(entity) defp do_transform(entity) when is_binary(entity) do
String.trim(entity)
defp do_transform(value) when is_binary(value) do
String.trim(value)
|> do_transform_string() |> do_transform_string()
end end
defp do_transform(value), do: value defp do_transform(entity), do: entity
defp do_transform_string(value) when byte_size(value) == 0, do: nil defp do_transform_string("~r/" <> pattern) do
pattern = String.trim_trailing(pattern, "/")
~r/#{pattern}/
end
defp do_transform_string(":" <> atom), do: String.to_atom(atom)
defp do_transform_string(value) do defp do_transform_string(value) do
cond do if String.starts_with?(value, "Pleroma") or String.starts_with?(value, "Phoenix"),
String.starts_with?(value, "Pleroma") or String.starts_with?(value, "Phoenix") -> do: String.to_existing_atom("Elixir." <> value),
String.to_existing_atom("Elixir." <> value) else: value
String.starts_with?(value, ":") ->
String.replace(value, ":", "") |> String.to_existing_atom()
String.starts_with?(value, "i:") ->
String.replace(value, "i:", "") |> String.to_integer()
true ->
value
end
end end
end end

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ConfigView do defmodule Pleroma.Web.AdminAPI.ConfigView do
use Pleroma.Web, :view use Pleroma.Web, :view
@ -11,7 +15,7 @@ def render("show.json", %{config: config}) do
%{ %{
key: config.key, key: config.key,
group: config.group, group: config.group,
value: Pleroma.Web.AdminAPI.Config.from_binary_to_map(config.value) value: Pleroma.Web.AdminAPI.Config.from_binary_with_convert(config.value)
} }
end end
end end

View file

@ -13,6 +13,7 @@ defmodule Pleroma.Web.CommonAPI do
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.ActivityPub.Visibility
import Pleroma.Web.Gettext
import Pleroma.Web.CommonAPI.Utils import Pleroma.Web.CommonAPI.Utils
def follow(follower, followed) do def follow(follower, followed) do
@ -74,7 +75,7 @@ def delete(activity_id, user) do
{:ok, delete} {:ok, delete}
else else
_ -> _ ->
{:error, "Could not delete"} {:error, dgettext("errors", "Could not delete")}
end end
end end
@ -85,7 +86,7 @@ def repeat(id_or_ap_id, user) do
ActivityPub.announce(user, object) ActivityPub.announce(user, object)
else else
_ -> _ ->
{:error, "Could not repeat"} {:error, dgettext("errors", "Could not repeat")}
end end
end end
@ -95,7 +96,7 @@ def unrepeat(id_or_ap_id, user) do
ActivityPub.unannounce(user, object) ActivityPub.unannounce(user, object)
else else
_ -> _ ->
{:error, "Could not unrepeat"} {:error, dgettext("errors", "Could not unrepeat")}
end end
end end
@ -106,7 +107,7 @@ def favorite(id_or_ap_id, user) do
ActivityPub.like(user, object) ActivityPub.like(user, object)
else else
_ -> _ ->
{:error, "Could not favorite"} {:error, dgettext("errors", "Could not favorite")}
end end
end end
@ -116,7 +117,7 @@ def unfavorite(id_or_ap_id, user) do
ActivityPub.unlike(user, object) ActivityPub.unlike(user, object)
else else
_ -> _ ->
{:error, "Could not unfavorite"} {:error, dgettext("errors", "Could not unfavorite")}
end end
end end
@ -148,10 +149,10 @@ def vote(user, object, choices) do
object = Object.get_cached_by_ap_id(object.data["id"]) object = Object.get_cached_by_ap_id(object.data["id"])
{:ok, answer_activities, object} {:ok, answer_activities, object}
else else
{:author, _} -> {:error, "Poll's author can't vote"} {:author, _} -> {:error, dgettext("errors", "Poll's author can't vote")}
{:existing_votes, _} -> {:error, "Already voted"} {:existing_votes, _} -> {:error, dgettext("errors", "Already voted")}
{:choice_check, {_, false}} -> {:error, "Invalid indices"} {:choice_check, {_, false}} -> {:error, dgettext("errors", "Invalid indices")}
{:count_check, false} -> {:error, "Too many choices"} {:count_check, false} -> {:error, dgettext("errors", "Too many choices")}
end end
end end
@ -248,9 +249,14 @@ def post(user, %{"status" => status} = data) do
res res
else else
{:private_to_public, true} -> {:error, "The message visibility must be direct"} {:private_to_public, true} ->
{:error, _} = e -> e {:error, dgettext("errors", "The message visibility must be direct")}
e -> {:error, e}
{:error, _} = e ->
e
e ->
{:error, e}
end end
end end
@ -301,7 +307,7 @@ def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do
{:error, err} {:error, err}
_ -> _ ->
{:error, "Could not pin"} {:error, dgettext("errors", "Could not pin")}
end end
end end
@ -318,7 +324,7 @@ def unpin(id_or_ap_id, user) do
{:error, err} {:error, err}
_ -> _ ->
{:error, "Could not unpin"} {:error, dgettext("errors", "Could not unpin")}
end end
end end
@ -326,7 +332,7 @@ def add_mute(user, activity) do
with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]) do with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]) do
{:ok, activity} {:ok, activity}
else else
{:error, _} -> {:error, "conversation is already muted"} {:error, _} -> {:error, dgettext("errors", "conversation is already muted")}
end end
end end
@ -371,8 +377,8 @@ def report(user, data) do
{:ok, activity} {:ok, activity}
else else
{:error, err} -> {:error, err} {:error, err} -> {:error, err}
{:account_id, %{}} -> {:error, "Valid `account_id` required"} {:account_id, %{}} -> {:error, dgettext("errors", "Valid `account_id` required")}
{:account, nil} -> {:error, "Account not found"} {:account, nil} -> {:error, dgettext("errors", "Account not found")}
end end
end end
@ -381,14 +387,9 @@ def update_report_state(activity_id, state) do
{:ok, activity} <- Utils.update_report_state(activity, state) do {:ok, activity} <- Utils.update_report_state(activity, state) do
{:ok, activity} {:ok, activity}
else else
nil -> nil -> {:error, :not_found}
{:error, :not_found} {:error, reason} -> {:error, reason}
_ -> {:error, dgettext("errors", "Could not update state")}
{:error, reason} ->
{:error, reason}
_ ->
{:error, "Could not update state"}
end end
end end
@ -398,11 +399,8 @@ def update_activity_scope(activity_id, opts \\ %{}) do
{:ok, activity} <- set_visibility(activity, opts) do {:ok, activity} <- set_visibility(activity, opts) do
{:ok, activity} {:ok, activity}
else else
nil -> nil -> {:error, :not_found}
{:error, :not_found} {:error, reason} -> {:error, reason}
{:error, reason} ->
{:error, reason}
end end
end end

View file

@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.CommonAPI.Utils do defmodule Pleroma.Web.CommonAPI.Utils do
import Pleroma.Web.Gettext
alias Calendar.Strftime alias Calendar.Strftime
alias Comeonin.Pbkdf2 alias Comeonin.Pbkdf2
alias Pleroma.Activity alias Pleroma.Activity
@ -372,7 +374,7 @@ def confirm_current_password(user, password) do
true <- Pbkdf2.checkpw(password, db_user.password_hash) do true <- Pbkdf2.checkpw(password, db_user.password_hash) do
{:ok, db_user} {:ok, db_user}
else else
_ -> {:error, "Invalid password."} _ -> {:error, dgettext("errors", "Invalid password.")}
end end
end end
@ -455,7 +457,8 @@ def make_report_content_html(comment) do
if String.length(comment) <= max_size do if String.length(comment) <= max_size do
{:ok, format_input(comment, "text/plain")} {:ok, format_input(comment, "text/plain")}
else else
{:error, "Comment must be up to #{max_size} characters"} {:error,
dgettext("errors", "Comment must be up to %{max_size} characters", max_size: max_size)}
end end
end end
@ -490,7 +493,7 @@ def conversation_id_to_context(id) do
context context
else else
_e -> _e ->
{:error, "No such conversation"} {:error, dgettext("errors", "No such conversation")}
end end
end end
@ -512,10 +515,10 @@ def validate_character_limit(full_payload, attachments, limit) do
if length > 0 or Enum.count(attachments) > 0 do if length > 0 or Enum.count(attachments) > 0 do
:ok :ok
else else
{:error, "Cannot post an empty status without attachments"} {:error, dgettext("errors", "Cannot post an empty status without attachments")}
end end
else else
{:error, "The status is over the character limit"} {:error, dgettext("errors", "The status is over the character limit")}
end end
end end
end end

View file

@ -7,13 +7,9 @@ defmodule Pleroma.Web.Endpoint do
socket("/socket", Pleroma.Web.UserSocket) socket("/socket", Pleroma.Web.UserSocket)
# Serve at "/" the static files from "priv/static" directory. plug(Pleroma.Plugs.SetLocalePlug)
#
# You should set gzip to true if you are running phoenix.digest
# when deploying your static files in production.
plug(CORSPlug) plug(CORSPlug)
plug(Pleroma.Plugs.HTTPSecurityPlug) plug(Pleroma.Plugs.HTTPSecurityPlug)
plug(Pleroma.Plugs.UploadedMedia) plug(Pleroma.Plugs.UploadedMedia)
@static_cache_control "public, no-cache" @static_cache_control "public, no-cache"
@ -30,6 +26,10 @@ defmodule Pleroma.Web.Endpoint do
} }
) )
# Serve at "/" the static files from "priv/static" directory.
#
# You should set gzip to true if you are running phoenix.digest
# when deploying your static files in production.
plug( plug(
Plug.Static, Plug.Static,
at: "/", at: "/",

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.MastodonAPI do defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
import Ecto.Query import Ecto.Query
import Ecto.Changeset import Ecto.Changeset

View file

@ -160,10 +160,7 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
AccountView.render("account.json", %{user: user, for: user, with_pleroma_settings: true}) AccountView.render("account.json", %{user: user, for: user, with_pleroma_settings: true})
) )
else else
_e -> _e -> render_error(conn, :forbidden, "Invalid request")
conn
|> put_status(403)
|> json(%{error: "Invalid request"})
end end
end end
@ -258,10 +255,7 @@ def user(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do
account = AccountView.render("account.json", %{user: user, for: for_user}) account = AccountView.render("account.json", %{user: user, for: for_user})
json(conn, account) json(conn, account)
else else
_e -> _e -> render_error(conn, :not_found, "Can't find user")
conn
|> put_status(404)
|> json(%{error: "Can't find user"})
end end
end end
@ -305,7 +299,9 @@ defp mastodonized_emoji do
"static_url" => url, "static_url" => url,
"visible_in_picker" => true, "visible_in_picker" => true,
"url" => url, "url" => url,
"tags" => tags "tags" => tags,
# Assuming that a comma is authorized in the category name
"category" => (tags -- ["Custom"]) |> Enum.join(",")
} }
end) end)
end end
@ -509,15 +505,8 @@ def get_poll(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|> put_view(StatusView) |> put_view(StatusView)
|> try_render("poll.json", %{object: object, for: user}) |> try_render("poll.json", %{object: object, for: user})
else else
nil -> nil -> render_error(conn, :not_found, "Record not found")
conn false -> render_error(conn, :not_found, "Record not found")
|> put_status(404)
|> json(%{error: "Record not found"})
false ->
conn
|> put_status(404)
|> json(%{error: "Record not found"})
end end
end end
@ -546,18 +535,14 @@ def poll_vote(%{assigns: %{user: user}} = conn, %{"id" => id, "choices" => choic
|> try_render("poll.json", %{object: object, for: user}) |> try_render("poll.json", %{object: object, for: user})
else else
nil -> nil ->
conn render_error(conn, :not_found, "Record not found")
|> put_status(404)
|> json(%{error: "Record not found"})
false -> false ->
conn render_error(conn, :not_found, "Record not found")
|> put_status(404)
|> json(%{error: "Record not found"})
{:error, message} -> {:error, message} ->
conn conn
|> put_status(422) |> put_status(:unprocessable_entity)
|> json(%{error: message}) |> json(%{error: message})
end end
end end
@ -646,10 +631,7 @@ def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
json(conn, %{}) json(conn, %{})
else else
_e -> _e -> render_error(conn, :forbidden, "Can't delete this post")
conn
|> put_status(403)
|> json(%{error: "Can't delete this post"})
end end
end end
@ -697,8 +679,8 @@ def pin_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
else else
{:error, reason} -> {:error, reason} ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:bad_request)
|> send_resp(:bad_request, Jason.encode!(%{"error" => reason})) |> json(%{"error" => reason})
end end
end end
@ -774,8 +756,8 @@ def get_notification(%{assigns: %{user: user}} = conn, %{"id" => id} = _params)
else else
{:error, reason} -> {:error, reason} ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:forbidden)
|> send_resp(403, Jason.encode!(%{"error" => reason})) |> json(%{"error" => reason})
end end
end end
@ -790,8 +772,8 @@ def dismiss_notification(%{assigns: %{user: user}} = conn, %{"id" => id} = _para
else else
{:error, reason} -> {:error, reason} ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:forbidden)
|> send_resp(403, Jason.encode!(%{"error" => reason})) |> json(%{"error" => reason})
end end
end end
@ -869,9 +851,7 @@ def set_mascot(%{assigns: %{user: user}} = conn, %{"file" => file}) do
conn conn
|> json(rendered) |> json(rendered)
else else
conn render_error(conn, :unsupported_media_type, "mascots can only be images")
|> put_resp_content_type("application/json")
|> send_resp(415, Jason.encode!(%{"error" => "mascots can only be images"}))
end end
end end
end end
@ -1000,8 +980,8 @@ def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}
else else
{:error, message} -> {:error, message} ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:forbidden)
|> send_resp(403, Jason.encode!(%{"error" => message})) |> json(%{error: message})
end end
end end
@ -1014,8 +994,8 @@ def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) d
else else
{:error, message} -> {:error, message} ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:forbidden)
|> send_resp(403, Jason.encode!(%{"error" => message})) |> json(%{error: message})
end end
end end
@ -1032,8 +1012,8 @@ def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
{:error, message} -> {:error, message} ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:forbidden)
|> send_resp(403, Jason.encode!(%{"error" => message})) |> json(%{error: message})
end end
end end
@ -1050,8 +1030,8 @@ def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
{:error, message} -> {:error, message} ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:forbidden)
|> send_resp(403, Jason.encode!(%{"error" => message})) |> json(%{error: message})
end end
end end
@ -1080,8 +1060,8 @@ def mute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
else else
{:error, message} -> {:error, message} ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:forbidden)
|> send_resp(403, Jason.encode!(%{"error" => message})) |> json(%{error: message})
end end
end end
@ -1094,8 +1074,8 @@ def unmute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
else else
{:error, message} -> {:error, message} ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:forbidden)
|> send_resp(403, Jason.encode!(%{"error" => message})) |> json(%{error: message})
end end
end end
@ -1116,8 +1096,8 @@ def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
else else
{:error, message} -> {:error, message} ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:forbidden)
|> send_resp(403, Jason.encode!(%{"error" => message})) |> json(%{error: message})
end end
end end
@ -1131,8 +1111,8 @@ def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
else else
{:error, message} -> {:error, message} ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:forbidden)
|> send_resp(403, Jason.encode!(%{"error" => message})) |> json(%{error: message})
end end
end end
@ -1166,8 +1146,8 @@ def subscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do
else else
{:error, message} -> {:error, message} ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:forbidden)
|> send_resp(403, Jason.encode!(%{"error" => message})) |> json(%{error: message})
end end
end end
@ -1180,8 +1160,8 @@ def unsubscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do
else else
{:error, message} -> {:error, message} ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:forbidden)
|> send_resp(403, Jason.encode!(%{"error" => message})) |> json(%{error: message})
end end
end end
@ -1229,13 +1209,8 @@ def user_favourites(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params
|> put_view(StatusView) |> put_view(StatusView)
|> render("index.json", %{activities: activities, for: for_user, as: :activity}) |> render("index.json", %{activities: activities, for: for_user, as: :activity})
else else
nil -> nil -> {:error, :not_found}
{:error, :not_found} true -> render_error(conn, :forbidden, "Can't get favorites")
true ->
conn
|> put_status(403)
|> json(%{error: "Can't get favorites"})
end end
end end
@ -1267,10 +1242,7 @@ def get_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do
res = ListView.render("list.json", list: list) res = ListView.render("list.json", list: list)
json(conn, res) json(conn, res)
else else
_e -> _e -> render_error(conn, :not_found, "Record not found")
conn
|> put_status(404)
|> json(%{error: "Record not found"})
end end
end end
@ -1286,7 +1258,7 @@ def delete_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do
json(conn, %{}) json(conn, %{})
else else
_e -> _e ->
json(conn, "error") json(conn, dgettext("errors", "error"))
end end
end end
@ -1337,7 +1309,7 @@ def rename_list(%{assigns: %{user: user}} = conn, %{"id" => id, "title" => title
json(conn, res) json(conn, res)
else else
_e -> _e ->
json(conn, "error") json(conn, dgettext("errors", "error"))
end end
end end
@ -1361,10 +1333,7 @@ def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params)
|> put_view(StatusView) |> put_view(StatusView)
|> render("index.json", %{activities: activities, for: user, as: :activity}) |> render("index.json", %{activities: activities, for: user, as: :activity})
else else
_e -> _e -> render_error(conn, :forbidden, "Error.")
conn
|> put_status(403)
|> json(%{error: "Error."})
end end
end end
@ -1483,8 +1452,8 @@ def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _para
else else
e -> e ->
conn conn
|> put_resp_content_type("application/json") |> put_status(:internal_server_error)
|> send_resp(500, Jason.encode!(%{"error" => inspect(e)})) |> json(%{error: inspect(e)})
end end
end end
@ -1652,20 +1621,18 @@ def errors(conn, {:error, %Changeset{} = changeset}) do
|> Enum.map_join(", ", fn {_k, v} -> v end) |> Enum.map_join(", ", fn {_k, v} -> v end)
conn conn
|> put_status(422) |> put_status(:unprocessable_entity)
|> json(%{error: error_message}) |> json(%{error: error_message})
end end
def errors(conn, {:error, :not_found}) do def errors(conn, {:error, :not_found}) do
conn render_error(conn, :not_found, "Record not found")
|> put_status(404)
|> json(%{error: "Record not found"})
end end
def errors(conn, _) do def errors(conn, _) do
conn conn
|> put_status(500) |> put_status(:internal_server_error)
|> json("Something went wrong") |> json(dgettext("errors", "Something went wrong"))
end end
def suggestions(%{assigns: %{user: user}} = conn, _) do def suggestions(%{assigns: %{user: user}} = conn, _) do
@ -1785,21 +1752,17 @@ def account_register(
else else
{:error, errors} -> {:error, errors} ->
conn conn
|> put_status(400) |> put_status(:bad_request)
|> json(Jason.encode!(errors)) |> json(errors)
end end
end end
def account_register(%{assigns: %{app: _app}} = conn, _params) do def account_register(%{assigns: %{app: _app}} = conn, _params) do
conn render_error(conn, :bad_request, "Missing parameters")
|> put_status(400)
|> json(%{error: "Missing parameters"})
end end
def account_register(conn, _) do def account_register(conn, _) do
conn render_error(conn, :forbidden, "Invalid credentials")
|> put_status(403)
|> json(%{error: "Invalid credentials"})
end end
def conversations(%{assigns: %{user: user}} = conn, params) do def conversations(%{assigns: %{user: user}} = conn, params) do
@ -1829,21 +1792,14 @@ def conversation_read(%{assigns: %{user: user}} = conn, %{"id" => participation_
def try_render(conn, target, params) def try_render(conn, target, params)
when is_binary(target) do when is_binary(target) do
res = render(conn, target, params) case render(conn, target, params) do
nil -> render_error(conn, :not_implemented, "Can't display this activity")
if res == nil do res -> res
conn
|> put_status(501)
|> json(%{error: "Can't display this activity"})
else
res
end end
end end
def try_render(conn, _, _) do def try_render(conn, _, _) do
conn render_error(conn, :not_implemented, "Can't display this activity")
|> put_status(501)
|> json(%{error: "Can't display this activity"})
end end
defp present?(nil), do: false defp present?(nil), do: false

View file

@ -4,61 +4,18 @@
defmodule Pleroma.Web.MastodonAPI.SearchController do defmodule Pleroma.Web.MastodonAPI.SearchController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Plugs.RateLimiter
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
alias Pleroma.Web.ControllerHelper
require Logger require Logger
plug(RateLimiter, :search when action in [:search, :search2, :account_search])
plug(Pleroma.Plugs.RateLimiter, :search when action in [:search, :search2, :account_search])
def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, [])
statuses = with_fallback(fn -> Activity.search(user, query) end, [])
tags_path = Web.base_url() <> "/tag/"
tags =
query
|> String.split()
|> Enum.uniq()
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
|> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
|> Enum.map(fn tag -> %{name: tag, url: tags_path <> tag} end)
res = %{
"accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
"statuses" =>
StatusView.render("index.json", activities: statuses, for: user, as: :activity),
"hashtags" => tags
}
json(conn, res)
end
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, [])
statuses = with_fallback(fn -> Activity.search(user, query) end, [])
tags =
query
|> String.split()
|> Enum.uniq()
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
|> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
res = %{
"accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
"statuses" =>
StatusView.render("index.json", activities: statuses, for: user, as: :activity),
"hashtags" => tags
}
json(conn, res)
end
def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = User.search(query, search_options(params, user)) accounts = User.search(query, search_options(params, user))
@ -67,17 +24,86 @@ def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) d
json(conn, res) json(conn, res)
end end
def search2(conn, params), do: do_search(:v2, conn, params)
def search(conn, params), do: do_search(:v1, conn, params)
defp do_search(version, %{assigns: %{user: user}} = conn, %{"q" => query} = params) do
options = search_options(params, user)
timeout = Keyword.get(Repo.config(), :timeout, 15_000)
default_values = %{"statuses" => [], "accounts" => [], "hashtags" => []}
result =
default_values
|> Enum.map(fn {resource, default_value} ->
if params["type"] == nil or params["type"] == resource do
{resource, fn -> resource_search(version, resource, query, options) end}
else
{resource, fn -> default_value end}
end
end)
|> Task.async_stream(fn {resource, f} -> {resource, with_fallback(f)} end,
timeout: timeout,
on_timeout: :kill_task
)
|> Enum.reduce(default_values, fn
{:ok, {resource, result}}, acc ->
Map.put(acc, resource, result)
_error, acc ->
acc
end)
json(conn, result)
end
defp search_options(params, user) do defp search_options(params, user) do
[ [
resolve: params["resolve"] == "true", resolve: params["resolve"] == "true",
following: params["following"] == "true", following: params["following"] == "true",
limit: ControllerHelper.fetch_integer_param(params, "limit"), limit: ControllerHelper.fetch_integer_param(params, "limit"),
offset: ControllerHelper.fetch_integer_param(params, "offset"), offset: ControllerHelper.fetch_integer_param(params, "offset"),
type: params["type"],
author: get_author(params),
for_user: user for_user: user
] ]
|> Enum.filter(&elem(&1, 1))
end end
defp with_fallback(f, fallback) do defp resource_search(_, "accounts", query, options) do
accounts = with_fallback(fn -> User.search(query, options) end)
AccountView.render("accounts.json", users: accounts, for: options[:for_user], as: :user)
end
defp resource_search(_, "statuses", query, options) do
statuses = with_fallback(fn -> Activity.search(options[:for_user], query, options) end)
StatusView.render("index.json", activities: statuses, for: options[:for_user], as: :activity)
end
defp resource_search(:v2, "hashtags", query, _options) do
tags_path = Web.base_url() <> "/tag/"
query
|> prepare_tags()
|> Enum.map(fn tag ->
tag = String.trim_leading(tag, "#")
%{name: tag, url: tags_path <> tag}
end)
end
defp resource_search(:v1, "hashtags", query, _options) do
query
|> prepare_tags()
|> Enum.map(fn tag -> String.trim_leading(tag, "#") end)
end
defp prepare_tags(query) do
query
|> String.split()
|> Enum.uniq()
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
end
defp with_fallback(f, fallback \\ []) do
try do try do
f.() f.()
rescue rescue
@ -86,4 +112,9 @@ defp with_fallback(f, fallback) do
fallback fallback
end end
end end
defp get_author(%{"account_id" => account_id}) when is_binary(account_id),
do: User.get_cached_by_id(account_id)
defp get_author(_params), do: nil
end end

View file

@ -59,13 +59,13 @@ def delete(%{assigns: %{user: user, token: token}} = conn, _params) do
# #
def errors(conn, {:error, :not_found}) do def errors(conn, {:error, :not_found}) do
conn conn
|> put_status(404) |> put_status(:not_found)
|> json("Not found") |> json(dgettext("errors", "Not found"))
end end
def errors(conn, _) do def errors(conn, _) do
conn conn
|> put_status(500) |> put_status(:internal_server_error)
|> json("Something went wrong") |> json(dgettext("errors", "Something went wrong"))
end end
end end

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.ConversationView do defmodule Pleroma.Web.MastodonAPI.ConversationView do
use Pleroma.Web, :view use Pleroma.Web, :view

View file

@ -19,6 +19,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1] import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1]
# TODO: Add cached version. # TODO: Add cached version.
defp get_replied_to_activities([]), do: %{}
defp get_replied_to_activities(activities) do defp get_replied_to_activities(activities) do
activities activities
|> Enum.map(fn |> Enum.map(fn
@ -147,8 +149,14 @@ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity
tags = object.data["tag"] || [] tags = object.data["tag"] || []
sensitive = object.data["sensitive"] || Enum.member?(tags, "nsfw") sensitive = object.data["sensitive"] || Enum.member?(tags, "nsfw")
tag_mentions =
tags
|> Enum.filter(fn tag -> is_map(tag) and tag["type"] == "Mention" end)
|> Enum.map(fn tag -> tag["href"] end)
mentions = mentions =
activity.recipients (object.data["to"] ++ tag_mentions)
|> Enum.uniq()
|> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end) |> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end)
|> Enum.filter(& &1) |> Enum.filter(& &1)
|> Enum.map(fn user -> AccountView.render("mention.json", %{user: user}) end) |> Enum.map(fn user -> AccountView.render("mention.json", %{user: user}) end)

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Metadata.PlayerView do defmodule Pleroma.Web.Metadata.PlayerView do
use Pleroma.Web, :view use Pleroma.Web, :view
import Phoenix.HTML.Tag, only: [content_tag: 3, tag: 2] import Phoenix.HTML.Tag, only: [content_tag: 3, tag: 2]

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Metadata.Providers.RelMe do defmodule Pleroma.Web.Metadata.Providers.RelMe do
alias Pleroma.Web.Metadata.Providers.Provider alias Pleroma.Web.Metadata.Providers.Provider
@behaviour Provider @behaviour Provider

View file

@ -29,7 +29,7 @@ def check_password(conn, %{"user" => username, "pass" => password}) do
else else
false -> false ->
conn conn
|> put_status(403) |> put_status(:forbidden)
|> json(false) |> json(false)
_ -> _ ->

View file

@ -201,8 +201,6 @@ def nodeinfo(conn, %{"version" => "2.1"}) do
end end
def nodeinfo(conn, _) do def nodeinfo(conn, _) do
conn render_error(conn, :not_found, "Nodeinfo schema version not handled")
|> put_status(404)
|> json(%{error: "Nodeinfo schema version not handled"})
end end
end end

View file

@ -9,21 +9,24 @@ defmodule Pleroma.Web.OAuth.FallbackController do
def call(conn, {:register, :generic_error}) do def call(conn, {:register, :generic_error}) do
conn conn
|> put_status(:internal_server_error) |> put_status(:internal_server_error)
|> put_flash(:error, "Unknown error, please check the details and try again.") |> put_flash(
:error,
dgettext("errors", "Unknown error, please check the details and try again.")
)
|> OAuthController.registration_details(conn.params) |> OAuthController.registration_details(conn.params)
end end
def call(conn, {:register, _error}) do def call(conn, {:register, _error}) do
conn conn
|> put_status(:unauthorized) |> put_status(:unauthorized)
|> put_flash(:error, "Invalid Username/Password") |> put_flash(:error, dgettext("errors", "Invalid Username/Password"))
|> OAuthController.registration_details(conn.params) |> OAuthController.registration_details(conn.params)
end end
def call(conn, _error) do def call(conn, _error) do
conn conn
|> put_status(:unauthorized) |> put_status(:unauthorized)
|> put_flash(:error, "Invalid Username/Password") |> put_flash(:error, dgettext("errors", "Invalid Username/Password"))
|> OAuthController.authorize(conn.params) |> OAuthController.authorize(conn.params)
end end
end end

View file

@ -90,7 +90,7 @@ defp handle_existing_authorization(
redirect(conn, external: url) redirect(conn, external: url)
else else
conn conn
|> put_flash(:error, "Unlisted redirect_uri.") |> put_flash(:error, dgettext("errors", "Unlisted redirect_uri."))
|> redirect(external: redirect_uri(conn, redirect_uri)) |> redirect(external: redirect_uri(conn, redirect_uri))
end end
end end
@ -128,7 +128,7 @@ def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{
redirect(conn, external: url) redirect(conn, external: url)
else else
conn conn
|> put_flash(:error, "Unlisted redirect_uri.") |> put_flash(:error, dgettext("errors", "Unlisted redirect_uri."))
|> redirect(external: redirect_uri(conn, redirect_uri)) |> redirect(external: redirect_uri(conn, redirect_uri))
end end
end end
@ -142,7 +142,7 @@ defp handle_create_authorization_error(
# Per https://github.com/tootsuite/mastodon/blob/ # Per https://github.com/tootsuite/mastodon/blob/
# 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L39 # 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L39
conn conn
|> put_flash(:error, "This action is outside the authorized scopes") |> put_flash(:error, dgettext("errors", "This action is outside the authorized scopes"))
|> put_status(:unauthorized) |> put_status(:unauthorized)
|> authorize(params) |> authorize(params)
end end
@ -155,7 +155,7 @@ defp handle_create_authorization_error(
# Per https://github.com/tootsuite/mastodon/blob/ # Per https://github.com/tootsuite/mastodon/blob/
# 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76 # 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76
conn conn
|> put_flash(:error, "Your login is missing a confirmed e-mail address") |> put_flash(:error, dgettext("errors", "Your login is missing a confirmed e-mail address"))
|> put_status(:forbidden) |> put_status(:forbidden)
|> authorize(params) |> authorize(params)
end end
@ -176,9 +176,7 @@ def token_exchange(
json(conn, Token.Response.build(user, token, response_attrs)) json(conn, Token.Response.build(user, token, response_attrs))
else else
_error -> _error -> render_invalid_credentials_error(conn)
put_status(conn, 400)
|> json(%{error: "Invalid credentials"})
end end
end end
@ -192,9 +190,7 @@ def token_exchange(%Plug.Conn{} = conn, %{"grant_type" => "authorization_code"}
json(conn, Token.Response.build(user, token, response_attrs)) json(conn, Token.Response.build(user, token, response_attrs))
else else
_error -> _error -> render_invalid_credentials_error(conn)
put_status(conn, 400)
|> json(%{error: "Invalid credentials"})
end end
end end
@ -214,18 +210,13 @@ def token_exchange(
{:auth_active, false} -> {:auth_active, false} ->
# Per https://github.com/tootsuite/mastodon/blob/ # Per https://github.com/tootsuite/mastodon/blob/
# 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76 # 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76
conn render_error(conn, :forbidden, "Your login is missing a confirmed e-mail address")
|> put_status(:forbidden)
|> json(%{error: "Your login is missing a confirmed e-mail address"})
{:user_active, false} -> {:user_active, false} ->
conn render_error(conn, :forbidden, "Your account is currently disabled")
|> put_status(:forbidden)
|> json(%{error: "Your account is currently disabled"})
_error -> _error ->
put_status(conn, 400) render_invalid_credentials_error(conn)
|> json(%{error: "Invalid credentials"})
end end
end end
@ -247,9 +238,7 @@ def token_exchange(%Plug.Conn{} = conn, %{"grant_type" => "client_credentials"}
{:ok, token} <- Token.exchange_token(app, auth) do {:ok, token} <- Token.exchange_token(app, auth) do
json(conn, Token.Response.build_for_client_credentials(token)) json(conn, Token.Response.build_for_client_credentials(token))
else else
_error -> _error -> render_invalid_credentials_error(conn)
put_status(conn, 400)
|> json(%{error: "Invalid credentials"})
end end
end end
@ -271,9 +260,7 @@ def token_revoke(%Plug.Conn{} = conn, params), do: bad_request(conn, params)
# Response for bad request # Response for bad request
defp bad_request(%Plug.Conn{} = conn, _) do defp bad_request(%Plug.Conn{} = conn, _) do
conn render_error(conn, :internal_server_error, "Bad request")
|> put_status(500)
|> json(%{error: "Bad request"})
end end
@doc "Prepares OAuth request to provider for Ueberauth" @doc "Prepares OAuth request to provider for Ueberauth"
@ -304,9 +291,11 @@ def prepare_request(%Plug.Conn{} = conn, %{
def request(%Plug.Conn{} = conn, params) do def request(%Plug.Conn{} = conn, params) do
message = message =
if params["provider"] do if params["provider"] do
"Unsupported OAuth provider: #{params["provider"]}." dgettext("errors", "Unsupported OAuth provider: %{provider}.",
provider: params["provider"]
)
else else
"Bad OAuth request." dgettext("errors", "Bad OAuth request.")
end end
conn conn
@ -320,7 +309,10 @@ def callback(%Plug.Conn{assigns: %{ueberauth_failure: failure}} = conn, params)
message = Enum.join(messages, "; ") message = Enum.join(messages, "; ")
conn conn
|> put_flash(:error, "Failed to authenticate: #{message}.") |> put_flash(
:error,
dgettext("errors", "Failed to authenticate: %{message}.", message: message)
)
|> redirect(external: redirect_uri(conn, params["redirect_uri"])) |> redirect(external: redirect_uri(conn, params["redirect_uri"]))
end end
@ -350,7 +342,7 @@ def callback(%Plug.Conn{} = conn, params) do
Logger.debug(inspect(["OAUTH_ERROR", error, conn.assigns])) Logger.debug(inspect(["OAUTH_ERROR", error, conn.assigns]))
conn conn
|> put_flash(:error, "Failed to set up user account.") |> put_flash(:error, dgettext("errors", "Failed to set up user account."))
|> redirect(external: redirect_uri(conn, params["redirect_uri"])) |> redirect(external: redirect_uri(conn, params["redirect_uri"]))
end end
end end
@ -468,4 +460,8 @@ def default_redirect_uri(%App{} = app) do
|> String.split() |> String.split()
|> Enum.at(0) |> Enum.at(0)
end end
defp render_invalid_credentials_error(conn) do
render_error(conn, :bad_request, "Invalid credentials")
end
end end

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.Token.Response do defmodule Pleroma.Web.OAuth.Token.Response do
@moduledoc false @moduledoc false

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.Token.Strategy.RefreshToken do defmodule Pleroma.Web.OAuth.Token.Strategy.RefreshToken do
@moduledoc """ @moduledoc """
Functions for dealing with refresh token strategy. Functions for dealing with refresh token strategy.

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.Token.Strategy.Revoke do defmodule Pleroma.Web.OAuth.Token.Strategy.Revoke do
@moduledoc """ @moduledoc """
Functions for dealing with revocation. Functions for dealing with revocation.

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.Token.Utils do defmodule Pleroma.Web.OAuth.Token.Utils do
@moduledoc """ @moduledoc """
Auxiliary functions for dealing with tokens. Auxiliary functions for dealing with tokens.

View file

@ -245,14 +245,10 @@ defp represent_activity(conn, _, activity, user) do
end end
def errors(conn, {:error, :not_found}) do def errors(conn, {:error, :not_found}) do
conn render_error(conn, :not_found, "Not found")
|> put_status(404)
|> text("Not found")
end end
def errors(conn, _) do def errors(conn, _) do
conn render_error(conn, :internal_server_error, "Something went wrong")
|> put_status(500)
|> text("Something went wrong")
end end
end end

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do
def parse(html, data, prefix, error_message, key_name, value_name \\ "content") do def parse(html, data, prefix, error_message, key_name, value_name \\ "content") do
meta_data = meta_data =

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do
def parse(html, _data) do def parse(html, _data) do
with elements = [_ | _] <- get_discovery_data(html), with elements = [_ | _] <- get_discovery_data(html),

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parsers.OGP do defmodule Pleroma.Web.RichMedia.Parsers.OGP do
def parse(html, data) do def parse(html, data) do
Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse( Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse(

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do
def parse(html, data) do def parse(html, data) do
Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse( Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse(

View file

@ -0,0 +1,17 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TranslationHelpers do
defmacro render_error(conn, status, msgid, bindings \\ Macro.escape(%{})) do
quote do
require Pleroma.Web.Gettext
unquote(conn)
|> Plug.Conn.put_status(unquote(status))
|> Phoenix.Controller.json(%{
error: Pleroma.Web.Gettext.dgettext("errors", unquote(msgid), unquote(bindings))
})
end
end
end

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.UploaderController do defmodule Pleroma.Web.UploaderController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
@ -8,7 +12,7 @@ def callback(conn, %{"upload_path" => upload_path} = params) do
end end
def callbacks(conn, _) do def callbacks(conn, _) do
send_resp(conn, 400, "bad request") render_error(conn, :bad_request, "bad request")
end end
defp process_callback(conn, pid, params) when is_pid(pid) do defp process_callback(conn, pid, params) when is_pid(pid) do
@ -20,6 +24,6 @@ defp process_callback(conn, pid, params) when is_pid(pid) do
end end
defp process_callback(conn, _, _) do defp process_callback(conn, _, _) do
send_resp(conn, 400, "bad request") render_error(conn, :bad_request, "bad request")
end end
end end

View file

@ -23,9 +23,11 @@ defmodule Pleroma.Web do
def controller do def controller do
quote do quote do
use Phoenix.Controller, namespace: Pleroma.Web use Phoenix.Controller, namespace: Pleroma.Web
import Plug.Conn import Plug.Conn
import Pleroma.Web.Gettext import Pleroma.Web.Gettext
import Pleroma.Web.Router.Helpers import Pleroma.Web.Router.Helpers
import Pleroma.Web.TranslationHelpers
plug(:set_put_layout) plug(:set_put_layout)

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Phoenix.Transports.WebSocket.Raw do defmodule Phoenix.Transports.WebSocket.Raw do
import Plug.Conn, import Plug.Conn,
only: [ only: [

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.XmlBuilder do defmodule Pleroma.XmlBuilder do
def to_xml({tag, attributes, content}) do def to_xml({tag, attributes, content}) do
open_tag = make_open_tag(tag, attributes) open_tag = make_open_tag(tag, attributes)

View file

@ -125,7 +125,7 @@ defp deps do
{:cors_plug, "~> 1.5"}, {:cors_plug, "~> 1.5"},
{:ex_doc, "~> 0.20.2", only: :dev, runtime: false}, {:ex_doc, "~> 0.20.2", only: :dev, runtime: false},
{:web_push_encryption, "~> 0.2.1"}, {:web_push_encryption, "~> 0.2.1"},
{:swoosh, "~> 0.20"}, {:swoosh, "~> 0.23.2"},
{:phoenix_swoosh, "~> 0.2"}, {:phoenix_swoosh, "~> 0.2"},
{:gen_smtp, "~> 0.13"}, {:gen_smtp, "~> 0.13"},
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test}, {:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test},

View file

@ -18,7 +18,7 @@
"crontab": {:hex, :crontab, "1.1.5", "2c9439506ceb0e9045de75879e994b88d6f0be88bfe017d58cb356c66c4a5482", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, "crontab": {:hex, :crontab, "1.1.5", "2c9439506ceb0e9045de75879e994b88d6f0be88bfe017d58cb356c66c4a5482", [: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", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},
"db_connection": {:hex, :db_connection, "2.0.6", "bde2f85d047969c5b5800cb8f4b3ed6316c8cb11487afedac4aa5f93fd39abfa", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, "db_connection": {:hex, :db_connection, "2.0.6", "bde2f85d047969c5b5800cb8f4b3ed6316c8cb11487afedac4aa5f93fd39abfa", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
"decimal": {:hex, :decimal, "1.7.0", "30d6b52c88541f9a66637359ddf85016df9eb266170d53105f02e4a67e00c5aa", [:mix], [], "hexpm"}, "decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"},
"earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"}, "earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"},
"ecto": {:hex, :ecto, "3.1.4", "69d852da7a9f04ede725855a35ede48d158ca11a404fe94f8b2fb3b2162cd3c9", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, "ecto": {:hex, :ecto, "3.1.4", "69d852da7a9f04ede725855a35ede48d158ca11a404fe94f8b2fb3b2162cd3c9", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
@ -34,10 +34,8 @@
"ex_syslogger": {:git, "https://github.com/slashmili/ex_syslogger.git", "f3963399047af17e038897c69e20d552e6899e1d", [tag: "1.4.0"]}, "ex_syslogger": {:git, "https://github.com/slashmili/ex_syslogger.git", "f3963399047af17e038897c69e20d552e6899e1d", [tag: "1.4.0"]},
"excoveralls": {:hex, :excoveralls, "0.11.1", "dd677fbdd49114fdbdbf445540ec735808250d56b011077798316505064edb2c", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, "excoveralls": {:hex, :excoveralls, "0.11.1", "dd677fbdd49114fdbdbf445540ec735808250d56b011077798316505064edb2c", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"floki": {:hex, :floki, "0.20.4", "be42ac911fece24b4c72f3b5846774b6e61b83fe685c2fc9d62093277fb3bc86", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}, {:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"}, "floki": {:hex, :floki, "0.20.4", "be42ac911fece24b4c72f3b5846774b6e61b83fe685c2fc9d62093277fb3bc86", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}, {:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
"gen_smtp": {:hex, :gen_smtp, "0.13.0", "11f08504c4bdd831dc520b8f84a1dce5ce624474a797394e7aafd3c29f5dcd25", [:rebar3], [], "hexpm"}, "gen_smtp": {:hex, :gen_smtp, "0.14.0", "39846a03522456077c6429b4badfd1d55e5e7d0fdfb65e935b7c5e38549d9202", [:rebar3], [], "hexpm"},
"gen_stage": {:hex, :gen_stage, "0.14.1", "9d46723fda072d4f4bb31a102560013f7960f5d80ea44dcb96fd6304ed61e7a4", [:mix], [], "hexpm"}, "gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"},
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
"gettext": {:hex, :gettext, "0.16.1", "e2130b25eebcbe02bb343b119a07ae2c7e28bd4b146c4a154da2ffb2b3507af2", [:mix], [], "hexpm"},
"hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, "hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm"}, "html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm"},
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"}, "html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
@ -72,19 +70,20 @@
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"}, "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},
"postgrex": {:hex, :postgrex, "0.14.3", "5754dee2fdf6e9e508cbf49ab138df964278700b764177e8f3871e658b345a1e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, "postgrex": {:hex, :postgrex, "0.14.3", "5754dee2fdf6e9e508cbf49ab138df964278700b764177e8f3871e658b345a1e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"prometheus": {:hex, :prometheus, "4.2.2", "a830e77b79dc6d28183f4db050a7cac926a6c58f1872f9ef94a35cd989aceef8", [:mix, :rebar3], [], "hexpm"}, "prometheus": {:hex, :prometheus, "4.2.2", "a830e77b79dc6d28183f4db050a7cac926a6c58f1872f9ef94a35cd989aceef8", [:mix, :rebar3], [], "hexpm"},
"prometheus_ecto": {:hex, :prometheus_ecto, "1.4.1", "6c768ea9654de871e5b32fab2eac348467b3021604ebebbcbd8bcbe806a65ed5", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"}, "prometheus_ecto": {:hex, :prometheus_ecto, "1.4.1", "6c768ea9654de871e5b32fab2eac348467b3021604ebebbcbd8bcbe806a65ed5", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_ex": {:hex, :prometheus_ex, "3.0.5", "fa58cfd983487fc5ead331e9a3e0aa622c67232b3ec71710ced122c4c453a02f", [:mix], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"}, "prometheus_ex": {:hex, :prometheus_ex, "3.0.5", "fa58cfd983487fc5ead331e9a3e0aa622c67232b3ec71710ced122c4c453a02f", [:mix], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_phoenix": {:hex, :prometheus_phoenix, "1.2.1", "964a74dfbc055f781d3a75631e06ce3816a2913976d1df7830283aa3118a797a", [:mix], [{:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.3 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"}, "prometheus_phoenix": {:hex, :prometheus_phoenix, "1.2.1", "964a74dfbc055f781d3a75631e06ce3816a2913976d1df7830283aa3118a797a", [:mix], [{:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.3 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_plugs": {:hex, :prometheus_plugs, "1.1.5", "25933d48f8af3a5941dd7b621c889749894d8a1082a6ff7c67cc99dec26377c5", [:mix], [{:accept, "~> 0.1", [hex: :accept, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}, {:prometheus_process_collector, "~> 1.1", [hex: :prometheus_process_collector, repo: "hexpm", optional: true]}], "hexpm"}, "prometheus_plugs": {:hex, :prometheus_plugs, "1.1.5", "25933d48f8af3a5941dd7b621c889749894d8a1082a6ff7c67cc99dec26377c5", [:mix], [{:accept, "~> 0.1", [hex: :accept, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}, {:prometheus_process_collector, "~> 1.1", [hex: :prometheus_process_collector, repo: "hexpm", optional: true]}], "hexpm"},
"prometheus_process_collector": {:hex, :prometheus_process_collector, "1.4.0", "6dbd39e3165b9ef1c94a7a820e9ffe08479f949dcdd431ed4aaea7b250eebfde", [:rebar3], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"},
"quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm"}, "quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm"},
"quantum": {:hex, :quantum, "2.3.4", "72a0e8855e2adc101459eac8454787cb74ab4169de6ca50f670e72142d4960e9", [:mix], [{:calendar, "~> 0.17", [hex: :calendar, repo: "hexpm", optional: true]}, {:crontab, "~> 1.1", [hex: :crontab, repo: "hexpm", optional: false]}, {:gen_stage, "~> 0.12", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:swarm, "~> 3.3", [hex: :swarm, repo: "hexpm", optional: false]}, {:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: true]}], "hexpm"}, "quantum": {:hex, :quantum, "2.3.4", "72a0e8855e2adc101459eac8454787cb74ab4169de6ca50f670e72142d4960e9", [:mix], [{:calendar, "~> 0.17", [hex: :calendar, repo: "hexpm", optional: true]}, {:crontab, "~> 1.1", [hex: :crontab, repo: "hexpm", optional: false]}, {:gen_stage, "~> 0.12", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:swarm, "~> 3.3", [hex: :swarm, repo: "hexpm", optional: false]}, {:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: true]}], "hexpm"},
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
"recon": {:git, "https://github.com/ferd/recon.git", "75d70c7c08926d2f24f1ee6de14ee50fe8a52763", [tag: "2.4.0"]}, "recon": {:git, "https://github.com/ferd/recon.git", "75d70c7c08926d2f24f1ee6de14ee50fe8a52763", [tag: "2.4.0"]},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
"swarm": {:hex, :swarm, "3.4.0", "64f8b30055d74640d2186c66354b33b999438692a91be275bb89cdc7e401f448", [:mix], [{:gen_state_machine, "~> 2.0", [hex: :gen_state_machine, repo: "hexpm", optional: false]}, {:libring, "~> 1.0", [hex: :libring, repo: "hexpm", optional: false]}], "hexpm"}, "swoosh": {:hex, :swoosh, "0.23.2", "7dda95ff0bf54a2298328d6899c74dae1223777b43563ccebebb4b5d2b61df38", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"swoosh": {:hex, :swoosh, "0.23.1", "209b7cc6d862c09d2a064c16caa4d4d1c9c936285476459e16591e0065f8432b", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.12", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]}, "syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]},
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"}, "telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
"tesla": {:hex, :tesla, "1.2.1", "864783cc27f71dd8c8969163704752476cec0f3a51eb3b06393b3971dc9733ff", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"}, "tesla": {:hex, :tesla, "1.2.1", "864783cc27f71dd8c8969163704752476cec0f3a51eb3b06393b3971dc9733ff", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},

View file

@ -91,3 +91,375 @@ msgstr ""
msgid "must be equal to %{number}" msgid "must be equal to %{number}"
msgstr "" msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:381
msgid "Account not found"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:153
msgid "Already voted"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:263
msgid "Bad request"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:254
msgid "Can't delete object"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:569
msgid "Can't delete this post"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1731
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1737
msgid "Can't display this activity"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:195
msgid "Can't find user"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1148
msgid "Can't get favorites"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:263
msgid "Can't like object"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:518
msgid "Cannot post an empty status without attachments"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:461
msgid "Comment must be up to %{max_size} characters"
msgstr ""
#, elixir-format
#: lib/pleroma/web/admin_api/config.ex:63
msgid "Config with params %{params} not found"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:78
msgid "Could not delete"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:110
msgid "Could not favorite"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:310
msgid "Could not pin"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:89
msgid "Could not repeat"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:120
msgid "Could not unfavorite"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:327
msgid "Could not unpin"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:99
msgid "Could not unrepeat"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:392
msgid "Could not update state"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1271
msgid "Error."
msgstr ""
#, elixir-format
#: lib/pleroma/captcha/kocaptcha.ex:36
msgid "Invalid CAPTCHA"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1700
#: lib/pleroma/web/oauth/oauth_controller.ex:465
msgid "Invalid credentials"
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:20
msgid "Invalid credentials."
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:154
msgid "Invalid indices"
msgstr ""
#, elixir-format
#: lib/pleroma/web/admin_api/admin_api_controller.ex:411
msgid "Invalid parameters"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:377
msgid "Invalid password."
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:163
msgid "Invalid request"
msgstr ""
#, elixir-format
#: lib/pleroma/captcha/kocaptcha.ex:16
msgid "Kocaptcha service unavailable"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1696
msgid "Missing parameters"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:496
msgid "No such conversation"
msgstr ""
#, elixir-format
#: lib/pleroma/web/admin_api/admin_api_controller.ex:163
#: lib/pleroma/web/admin_api/admin_api_controller.ex:206
msgid "No such permission_group"
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/uploaded_media.ex:69
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:311
#: lib/pleroma/web/admin_api/admin_api_controller.ex:399
#: lib/pleroma/web/mastodon_api/subscription_controller.ex:63
#: lib/pleroma/web/ostatus/ostatus_controller.ex:248
msgid "Not found"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:152
msgid "Poll's author can't vote"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:443
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:444
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:473
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:476
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1180
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1564
msgid "Record not found"
msgstr ""
#, elixir-format
#: lib/pleroma/web/admin_api/admin_api_controller.ex:417
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1570
#: lib/pleroma/web/mastodon_api/subscription_controller.ex:69
#: lib/pleroma/web/ostatus/ostatus_controller.ex:252
msgid "Something went wrong"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:253
msgid "The message visibility must be direct"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:521
msgid "The status is over the character limit"
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:27
msgid "This resource requires authentication."
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/rate_limiter.ex:89
msgid "Throttled"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:155
msgid "Too many choices"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:268
msgid "Unhandled activity type"
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/user_is_admin_plug.ex:20
msgid "User is not admin."
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:380
msgid "Valid `account_id` required"
msgstr ""
#, elixir-format
#: lib/pleroma/web/admin_api/admin_api_controller.ex:185
msgid "You can't revoke your own admin status."
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:216
msgid "Your account is currently disabled"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:158
#: lib/pleroma/web/oauth/oauth_controller.ex:213
msgid "Your login is missing a confirmed e-mail address"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:221
msgid "can't read inbox of %{nickname} as %{as_nickname}"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:297
msgid "can't update outbox of %{nickname} as %{as_nickname}"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:335
msgid "conversation is already muted"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:192
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:317
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1196
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1247
msgid "error"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:789
msgid "mascots can only be images"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:34
msgid "not found"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:298
msgid "Bad OAuth request."
msgstr ""
#, elixir-format
#: lib/pleroma/captcha/captcha.ex:92
msgid "CAPTCHA already used"
msgstr ""
#, elixir-format
#: lib/pleroma/captcha/captcha.ex:89
msgid "CAPTCHA expired"
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/uploaded_media.ex:50
msgid "Failed"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:314
msgid "Failed to authenticate: %{message}."
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:345
msgid "Failed to set up user account."
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/oauth_scopes_plug.ex:37
msgid "Insufficient permissions: %{permissions}."
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/uploaded_media.ex:89
msgid "Internal Error"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/fallback_controller.ex:22
#: lib/pleroma/web/oauth/fallback_controller.ex:29
msgid "Invalid Username/Password"
msgstr ""
#, elixir-format
#: lib/pleroma/captcha/captcha.ex:107
msgid "Invalid answer data"
msgstr ""
#, elixir-format
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:204
msgid "Nodeinfo schema version not handled"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:145
msgid "This action is outside the authorized scopes"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/fallback_controller.ex:14
msgid "Unknown error, please check the details and try again."
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:93
#: lib/pleroma/web/oauth/oauth_controller.ex:131
msgid "Unlisted redirect_uri."
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:294
msgid "Unsupported OAuth provider: %{provider}."
msgstr ""
#, elixir-format
#: lib/pleroma/uploaders/uploader.ex:71
msgid "Uploader callback timeout"
msgstr ""
#, elixir-format
#: lib/pleroma/web/uploader_controller.ex:11
#: lib/pleroma/web/uploader_controller.ex:23
msgid "bad request"
msgstr ""

View file

@ -7,7 +7,6 @@
## Run `mix gettext.extract` to bring this file up to ## Run `mix gettext.extract` to bring this file up to
## date. Leave `msgstr`s empty as changing them here as no ## date. Leave `msgstr`s empty as changing them here as no
## effect: edit them in PO (`.po`) files instead. ## effect: edit them in PO (`.po`) files instead.
## From Ecto.Changeset.cast/4 ## From Ecto.Changeset.cast/4
msgid "can't be blank" msgid "can't be blank"
msgstr "" msgstr ""
@ -89,3 +88,375 @@ msgstr ""
msgid "must be equal to %{number}" msgid "must be equal to %{number}"
msgstr "" msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:381
msgid "Account not found"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:153
msgid "Already voted"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:263
msgid "Bad request"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:254
msgid "Can't delete object"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:569
msgid "Can't delete this post"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1731
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1737
msgid "Can't display this activity"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:195
msgid "Can't find user"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1148
msgid "Can't get favorites"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:263
msgid "Can't like object"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:518
msgid "Cannot post an empty status without attachments"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:461
msgid "Comment must be up to %{max_size} characters"
msgstr ""
#, elixir-format
#: lib/pleroma/web/admin_api/config.ex:63
msgid "Config with params %{params} not found"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:78
msgid "Could not delete"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:110
msgid "Could not favorite"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:310
msgid "Could not pin"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:89
msgid "Could not repeat"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:120
msgid "Could not unfavorite"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:327
msgid "Could not unpin"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:99
msgid "Could not unrepeat"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:392
msgid "Could not update state"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1271
msgid "Error."
msgstr ""
#, elixir-format
#: lib/pleroma/captcha/kocaptcha.ex:36
msgid "Invalid CAPTCHA"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1700
#: lib/pleroma/web/oauth/oauth_controller.ex:465
msgid "Invalid credentials"
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:20
msgid "Invalid credentials."
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:154
msgid "Invalid indices"
msgstr ""
#, elixir-format
#: lib/pleroma/web/admin_api/admin_api_controller.ex:411
msgid "Invalid parameters"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:377
msgid "Invalid password."
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:163
msgid "Invalid request"
msgstr ""
#, elixir-format
#: lib/pleroma/captcha/kocaptcha.ex:16
msgid "Kocaptcha service unavailable"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1696
msgid "Missing parameters"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:496
msgid "No such conversation"
msgstr ""
#, elixir-format
#: lib/pleroma/web/admin_api/admin_api_controller.ex:163
#: lib/pleroma/web/admin_api/admin_api_controller.ex:206
msgid "No such permission_group"
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/uploaded_media.ex:69
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:311
#: lib/pleroma/web/admin_api/admin_api_controller.ex:399
#: lib/pleroma/web/mastodon_api/subscription_controller.ex:63
#: lib/pleroma/web/ostatus/ostatus_controller.ex:248
msgid "Not found"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:152
msgid "Poll's author can't vote"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:443
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:444
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:473
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:476
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1180
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1564
msgid "Record not found"
msgstr ""
#, elixir-format
#: lib/pleroma/web/admin_api/admin_api_controller.ex:417
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1570
#: lib/pleroma/web/mastodon_api/subscription_controller.ex:69
#: lib/pleroma/web/ostatus/ostatus_controller.ex:252
msgid "Something went wrong"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:253
msgid "The message visibility must be direct"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:521
msgid "The status is over the character limit"
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:27
msgid "This resource requires authentication."
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/rate_limiter.ex:89
msgid "Throttled"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:155
msgid "Too many choices"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:268
msgid "Unhandled activity type"
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/user_is_admin_plug.ex:20
msgid "User is not admin."
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:380
msgid "Valid `account_id` required"
msgstr ""
#, elixir-format
#: lib/pleroma/web/admin_api/admin_api_controller.ex:185
msgid "You can't revoke your own admin status."
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:216
msgid "Your account is currently disabled"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:158
#: lib/pleroma/web/oauth/oauth_controller.ex:213
msgid "Your login is missing a confirmed e-mail address"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:221
msgid "can't read inbox of %{nickname} as %{as_nickname}"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:297
msgid "can't update outbox of %{nickname} as %{as_nickname}"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:335
msgid "conversation is already muted"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:192
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:317
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1196
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1247
msgid "error"
msgstr ""
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:789
msgid "mascots can only be images"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:34
msgid "not found"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:298
msgid "Bad OAuth request."
msgstr ""
#, elixir-format
#: lib/pleroma/captcha/captcha.ex:92
msgid "CAPTCHA already used"
msgstr ""
#, elixir-format
#: lib/pleroma/captcha/captcha.ex:89
msgid "CAPTCHA expired"
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/uploaded_media.ex:50
msgid "Failed"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:314
msgid "Failed to authenticate: %{message}."
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:345
msgid "Failed to set up user account."
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/oauth_scopes_plug.ex:37
msgid "Insufficient permissions: %{permissions}."
msgstr ""
#, elixir-format
#: lib/pleroma/plugs/uploaded_media.ex:89
msgid "Internal Error"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/fallback_controller.ex:22
#: lib/pleroma/web/oauth/fallback_controller.ex:29
msgid "Invalid Username/Password"
msgstr ""
#, elixir-format
#: lib/pleroma/captcha/captcha.ex:107
msgid "Invalid answer data"
msgstr ""
#, elixir-format
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:204
msgid "Nodeinfo schema version not handled"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:145
msgid "This action is outside the authorized scopes"
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/fallback_controller.ex:14
msgid "Unknown error, please check the details and try again."
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:93
#: lib/pleroma/web/oauth/oauth_controller.ex:131
msgid "Unlisted redirect_uri."
msgstr ""
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:294
msgid "Unsupported OAuth provider: %{provider}."
msgstr ""
#, elixir-format
#: lib/pleroma/uploaders/uploader.ex:71
msgid "Uploader callback timeout"
msgstr ""
#, elixir-format
#: lib/pleroma/web/uploader_controller.ex:11
#: lib/pleroma/web/uploader_controller.ex:23
msgid "bad request"
msgstr ""

View file

@ -0,0 +1,463 @@
## `msgid`s in this file come from POT (.pot) files.
##
## Do not add, change, or remove `msgid`s manually here as
## they're tied to the ones in the corresponding POT file
## (with the same domain).
##
## Use `mix gettext.extract --merge` or `mix gettext.merge`
## to merge POT files into PO files.
msgid ""
msgstr ""
"Language: ru\n"
"Plural-Forms: nplurals=3\n"
msgid "can't be blank"
msgstr "не может быть пустым"
msgid "has already been taken"
msgstr "уже занято"
msgid "is invalid"
msgstr "неверный"
msgid "has invalid format"
msgstr "неверный формат"
msgid "has an invalid entry"
msgstr "содержит неверную запись"
msgid "is reserved"
msgstr "занято"
msgid "does not match confirmation"
msgstr "не совпадает"
msgid "is still associated with this entry"
msgstr "по прежнему связан с этой записью"
msgid "are still associated with this entry"
msgstr "по прежнему связаны с этой записью"
msgid "should be %{count} character(s)"
msgid_plural "should be %{count} character(s)"
msgstr[0] "должен состоять из %{count} символа"
msgstr[1] "должен состоять из %{count} символов"
msgstr[2] "должен состоять из %{count} символов"
msgid "should have %{count} item(s)"
msgid_plural "should have %{count} item(s)"
msgstr[0] "должен содержать %{count} элемент"
msgstr[1] "должен содержать %{count} элемента"
msgstr[2] "должен содержать %{count} элементов"
msgid "should be at least %{count} character(s)"
msgid_plural "should be at least %{count} character(s)"
msgstr[0] "должен быть не менее чем %{count} символа"
msgstr[1] "должен быть не менее чем %{count} символов"
msgstr[2] "должен быть не менее чем %{count} символов"
msgid "should have at least %{count} item(s)"
msgid_plural "should have at least %{count} item(s)"
msgstr[0] "должен быть не менее %{count} элемента"
msgstr[1] "должен быть не менее %{count} элементов"
msgstr[2] "должен быть не менее %{count} элементов"
msgid "should be at most %{count} character(s)"
msgid_plural "should be at most %{count} character(s)"
msgstr[0] "должен быть не более %{count} символа"
msgstr[1] "должен быть не более %{count} символов"
msgstr[2] "должен быть не более %{count} символов"
msgid "should have at most %{count} item(s)"
msgid_plural "should have at most %{count} item(s)"
msgstr[0] "должен содержать не менее %{count} элемента"
msgstr[1] "должен содержать не менее %{count} элемента"
msgstr[2] "должен содержать не менее %{count} элементов"
msgid "must be less than %{number}"
msgstr "должен быть меньше %{number}"
msgid "must be greater than %{number}"
msgstr "должен быть больше %{number}"
msgid "must be less than or equal to %{number}"
msgstr "должен быть меньше или равен %{number}"
msgid "must be greater than or equal to %{number}"
msgstr "должен быть больше или равен %{number}"
msgid "must be equal to %{number}"
msgstr "должен быть равным %{number}"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:381
msgid "Account not found"
msgstr "Учетная запись не найдена"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:153
msgid "Already voted"
msgstr "Уже проголосовал(а)"
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:263
msgid "Bad request"
msgstr "Неверный запрос"
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:254
msgid "Can't delete object"
msgstr "Произошла ошибка при удалении объекта"
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:569
msgid "Can't delete this post"
msgstr "Произошла ошибка при удалении этой записи"
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1731
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1737
msgid "Can't display this activity"
msgstr "Произошла ошибка при показе этой записи"
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:195
msgid "Can't find user"
msgstr "Пользователь не найден"
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1148
msgid "Can't get favorites"
msgstr "Не в состоянии получить избранное"
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:263
msgid "Can't like object"
msgstr "Не могу поставить лайк"
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:518
msgid "Cannot post an empty status without attachments"
msgstr "Нельзя отправить пустой статус без приложений"
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:461
msgid "Comment must be up to %{max_size} characters"
msgstr "Комментарий должен быть не более %{max_size} символов"
#, elixir-format
#: lib/pleroma/web/admin_api/config.ex:63
msgid "Config with params %{params} not found"
msgstr "Параметры конфигурации %{params} не найдены"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:78
msgid "Could not delete"
msgstr "Не в силах удалить"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:110
msgid "Could not favorite"
msgstr "Не в силах добавить в избранное"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:310
msgid "Could not pin"
msgstr "Не в силах прикрепить"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:89
msgid "Could not repeat"
msgstr "Не в силах повторить"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:120
msgid "Could not unfavorite"
msgstr "Не в силах удалить из избранного"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:327
msgid "Could not unpin"
msgstr "Не в силах открепить"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:99
msgid "Could not unrepeat"
msgstr "Не в силах отменить повтор"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:392
msgid "Could not update state"
msgstr "Не в силах обновить состояние"
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1271
msgid "Error."
msgstr "Ошибка"
#, elixir-format
#: lib/pleroma/captcha/kocaptcha.ex:36
msgid "Invalid CAPTCHA"
msgstr "Неверная CAPTCHA"
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1700
#: lib/pleroma/web/oauth/oauth_controller.ex:465
msgid "Invalid credentials"
msgstr "Неверные учетные данные"
#, elixir-format
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:20
msgid "Invalid credentials."
msgstr "Неверные учетные данные"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:154
msgid "Invalid indices"
msgstr "Неверные индексы"
#, elixir-format
#: lib/pleroma/web/admin_api/admin_api_controller.ex:411
msgid "Invalid parameters"
msgstr "Неверны параметры"
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:377
msgid "Invalid password."
msgstr "Неверный пароль"
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:163
msgid "Invalid request"
msgstr "Неверный запрос"
#, elixir-format
#: lib/pleroma/captcha/kocaptcha.ex:16
msgid "Kocaptcha service unavailable"
msgstr "Kocaptcha недоступен"
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1696
msgid "Missing parameters"
msgstr "Не хватает параметров"
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:496
msgid "No such conversation"
msgstr "Разговор не найден"
#, elixir-format
#: lib/pleroma/web/admin_api/admin_api_controller.ex:163
#: lib/pleroma/web/admin_api/admin_api_controller.ex:206
msgid "No such permission_group"
msgstr "Такой группы полномочий не существует"
#, elixir-format
#: lib/pleroma/plugs/uploaded_media.ex:69
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:311
#: lib/pleroma/web/admin_api/admin_api_controller.ex:399
#: lib/pleroma/web/mastodon_api/subscription_controller.ex:63
#: lib/pleroma/web/ostatus/ostatus_controller.ex:248
msgid "Not found"
msgstr "Не найден"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:152
msgid "Poll's author can't vote"
msgstr "Автор опроса не может голосовать"
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:443
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:444
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:473
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:476
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1180
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1564
msgid "Record not found"
msgstr "Запись не найдена"
#, elixir-format
#: lib/pleroma/web/admin_api/admin_api_controller.ex:417
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1570
#: lib/pleroma/web/mastodon_api/subscription_controller.ex:69
#: lib/pleroma/web/ostatus/ostatus_controller.ex:252
msgid "Something went wrong"
msgstr "Что-то пошло не так"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:253
msgid "The message visibility must be direct"
msgstr "Видимость у сообщения должна быть `Личное`"
#, elixir-format
#: lib/pleroma/web/common_api/utils.ex:521
msgid "The status is over the character limit"
msgstr "Превышена длина статуса"
#, elixir-format
#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:27
msgid "This resource requires authentication."
msgstr "Для этого ресурса требуется аутентификация"
#, elixir-format
#: lib/pleroma/plugs/rate_limiter.ex:89
msgid "Throttled"
msgstr "Ограничено. Превышен лимит запросов."
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:155
msgid "Too many choices"
msgstr "Слишком много ответов"
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:268
msgid "Unhandled activity type"
msgstr "Неизвестный тип activity"
#, elixir-format
#: lib/pleroma/plugs/user_is_admin_plug.ex:20
msgid "User is not admin."
msgstr "Пользователь не обладает правами администратора"
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:380
msgid "Valid `account_id` required"
msgstr "Требуется корректный `account_id`"
#, elixir-format
#: lib/pleroma/web/admin_api/admin_api_controller.ex:185
msgid "You can't revoke your own admin status."
msgstr "Вы не можете отозвать статус администратора у вашей учетной записи"
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:216
msgid "Your account is currently disabled"
msgstr "Ваша учетная запись отключена"
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:158
#: lib/pleroma/web/oauth/oauth_controller.ex:213
msgid "Your login is missing a confirmed e-mail address"
msgstr "Ваш e-mail адрес не подтвержден"
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:221
msgid "can't read inbox of %{nickname} as %{as_nickname}"
msgstr ""
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:297
msgid "can't update outbox of %{nickname} as %{as_nickname}"
msgstr ""
#, elixir-format
#: lib/pleroma/web/common_api/common_api.ex:335
msgid "conversation is already muted"
msgstr "разговор уже игнорируется"
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:192
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:317
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1196
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1247
msgid "error"
msgstr "ошибка"
#, elixir-format
#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:789
msgid "mascots can only be images"
msgstr "маскоты должны быть картинками"
#, elixir-format
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:34
msgid "not found"
msgstr "не найдено"
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:298
msgid "Bad OAuth request."
msgstr "Неверный OAuth запрос"
#, elixir-format
#: lib/pleroma/captcha/captcha.ex:92
msgid "CAPTCHA already used"
msgstr "CAPTCHA уже использована"
#, elixir-format
#: lib/pleroma/captcha/captcha.ex:89
msgid "CAPTCHA expired"
msgstr "CAPTCHA устарела"
#, elixir-format
#: lib/pleroma/plugs/uploaded_media.ex:50
msgid "Failed"
msgstr "Ошибка"
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:314
msgid "Failed to authenticate: %{message}."
msgstr "Ошибка при входе: %{message}"
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:345
msgid "Failed to set up user account."
msgstr "Ошибка при создании учетной записи"
#, elixir-format
#: lib/pleroma/plugs/oauth_scopes_plug.ex:37
msgid "Insufficient permissions: %{permissions}."
msgstr "Недостаточно полномочий: %{permissions}"
#, elixir-format
#: lib/pleroma/plugs/uploaded_media.ex:89
msgid "Internal Error"
msgstr "Внутренняя ошибка"
#, elixir-format
#: lib/pleroma/web/oauth/fallback_controller.ex:22
#: lib/pleroma/web/oauth/fallback_controller.ex:29
msgid "Invalid Username/Password"
msgstr "Неверное имя пользователя или пароль"
#, elixir-format
#: lib/pleroma/captcha/captcha.ex:107
msgid "Invalid answer data"
msgstr "Неверный ответ"
#, elixir-format
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:204
msgid "Nodeinfo schema version not handled"
msgstr "Версия схемы Nodeinfo не учитывается"
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:145
msgid "This action is outside the authorized scopes"
msgstr "Это действие выходит за рамки доступных полномочий"
#, elixir-format
#: lib/pleroma/web/oauth/fallback_controller.ex:14
msgid "Unknown error, please check the details and try again."
msgstr "Неизвестная ошибка. Пожалуйста, проверьте данные и попробуйте снова."
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:93
#: lib/pleroma/web/oauth/oauth_controller.ex:131
msgid "Unlisted redirect_uri."
msgstr "Неизвестный redirect_uri"
#, elixir-format
#: lib/pleroma/web/oauth/oauth_controller.ex:294
msgid "Unsupported OAuth provider: %{provider}."
msgstr "Неизвестный OAuth провайдер: %{provider}"
#, elixir-format
#: lib/pleroma/uploaders/uploader.ex:71
msgid "Uploader callback timeout"
msgstr "Тайм-аут при загрузке"
#, elixir-format
#: lib/pleroma/web/uploader_controller.ex:11
#: lib/pleroma/web/uploader_controller.ex:23
msgid "bad request"
msgstr "неправильный запрос"

View file

@ -0,0 +1,9 @@
defmodule Pleroma.Repo.Migrations.AddFollowingAddressToUser do
use Ecto.Migration
def change do
alter table(:users) do
add(:following_address, :string, unique: true)
end
end
end

View file

@ -0,0 +1,8 @@
defmodule Pleroma.Repo.Migrations.AddFollowingAddressIndexToUser do
use Ecto.Migration
@disable_ddl_transaction true
def change do
create(index(:users, [:following_address], concurrently: true))
end
end

View file

@ -0,0 +1,20 @@
defmodule Pleroma.Repo.Migrations.AddFollowingAddressFromSourceData do
use Ecto.Migration
import Ecto.Query
alias Pleroma.User
def change do
query =
User.external_users_query()
|> select([u], struct(u, [:id, :ap_id, :info]))
Pleroma.Repo.stream(query)
|> Enum.each(fn
%{info: %{source_data: source_data}} = user ->
Ecto.Changeset.cast(user, %{following_address: source_data["following"]}, [
:following_address
])
|> Pleroma.Repo.update()
end)
end
end

View file

@ -11,6 +11,7 @@ end %>
config :pleroma, Pleroma.Web.Endpoint, config :pleroma, Pleroma.Web.Endpoint,
url: [host: "<%= domain %>", scheme: "https", port: <%= port %>], url: [host: "<%= domain %>", scheme: "https", port: <%= port %>],
http: [ip: {<%= String.replace(listen_ip, ".", ", ") %>}, port: <%= listen_port %>],
secret_key_base: "<%= secret %>", secret_key_base: "<%= secret %>",
signing_salt: "<%= signing_salt %>" signing_salt: "<%= signing_salt %>"

View file

@ -6,6 +6,7 @@ defmodule Pleroma.ActivityTest do
use Pleroma.DataCase use Pleroma.DataCase
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Bookmark alias Pleroma.Bookmark
alias Pleroma.Object
alias Pleroma.ThreadMute alias Pleroma.ThreadMute
import Pleroma.Factory import Pleroma.Factory
@ -18,15 +19,18 @@ test "returns an activity by it's AP id" do
test "returns activities by it's objects AP ids" do test "returns activities by it's objects AP ids" do
activity = insert(:note_activity) activity = insert(:note_activity)
[found_activity] = Activity.get_all_create_by_object_ap_id(activity.data["object"]["id"]) object_data = Object.normalize(activity).data
[found_activity] = Activity.get_all_create_by_object_ap_id(object_data["id"])
assert activity == found_activity assert activity == found_activity
end end
test "returns the activity that created an object" do test "returns the activity that created an object" do
activity = insert(:note_activity) activity = insert(:note_activity)
object_data = Object.normalize(activity).data
found_activity = Activity.get_create_by_object_ap_id(activity.data["object"]["id"]) found_activity = Activity.get_create_by_object_ap_id(object_data["id"])
assert activity == found_activity assert activity == found_activity
end end

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.BBS.HandlerTest do defmodule Pleroma.BBS.HandlerTest do
use Pleroma.DataCase use Pleroma.DataCase
alias Pleroma.Activity alias Pleroma.Activity
@ -59,6 +63,7 @@ test "replying" do
another_user = insert(:user) another_user = insert(:user)
{:ok, activity} = CommonAPI.post(another_user, %{"status" => "this is a test post"}) {:ok, activity} = CommonAPI.post(another_user, %{"status" => "this is a test post"})
activity_object = Object.normalize(activity)
output = output =
capture_io(fn -> capture_io(fn ->
@ -76,8 +81,9 @@ test "replying" do
) )
assert reply.actor == user.ap_id assert reply.actor == user.ap_id
object = Object.normalize(reply)
assert object.data["content"] == "this is a reply" reply_object_data = Object.normalize(reply).data
assert object.data["inReplyTo"] == activity.data["object"] assert reply_object_data["content"] == "this is a reply"
assert reply_object_data["inReplyTo"] == activity_object.data["id"]
end end
end end

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.BookmarkTest do defmodule Pleroma.BookmarkTest do
use Pleroma.DataCase use Pleroma.DataCase
import Pleroma.Factory import Pleroma.Factory

View file

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.TransferTaskTest do defmodule Pleroma.Config.TransferTaskTest do
use Pleroma.DataCase use Pleroma.DataCase

View file

@ -0,0 +1,37 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.AdminEmailTest do
use Pleroma.DataCase
import Pleroma.Factory
alias Pleroma.Emails.AdminEmail
alias Pleroma.Web.Router.Helpers
test "build report email" do
config = Pleroma.Config.get(:instance)
to_user = insert(:user)
reporter = insert(:user)
account = insert(:user)
res =
AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment")
status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, "12")
reporter_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.nickname)
account_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, account.nickname)
assert res.to == [{to_user.name, to_user.email}]
assert res.from == {config[:name], config[:notify_email]}
assert res.reply_to == {reporter.name, reporter.email}
assert res.subject == "#{config[:name]} Report"
assert res.html_body ==
"<p>Reported by: <a href=\"#{reporter_url}\">#{reporter.nickname}</a></p>\n<p>Reported Account: <a href=\"#{
account_url
}\">#{account.nickname}</a></p>\n<p>Comment: Test comment\n<p> Statuses:\n <ul>\n <li><a href=\"#{
status_url
}\">#{status_url}</li>\n </ul>\n</p>\n\n"
end
end

View file

@ -0,0 +1,57 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.MailerTest do
use Pleroma.DataCase
alias Pleroma.Emails.Mailer
import Swoosh.TestAssertions
@email %Swoosh.Email{
from: {"Pleroma", "noreply@example.com"},
html_body: "Test email",
subject: "Pleroma test email",
to: [{"Test User", "user1@example.com"}]
}
setup do
value = Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled])
on_exit(fn -> Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], value) end)
:ok
end
test "not send email when mailer is disabled" do
Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false)
Mailer.deliver(@email)
refute_email_sent(
from: {"Pleroma", "noreply@example.com"},
to: [{"Test User", "user1@example.com"}],
html_body: "Test email",
subject: "Pleroma test email"
)
end
test "send email" do
Mailer.deliver(@email)
assert_email_sent(
from: {"Pleroma", "noreply@example.com"},
to: [{"Test User", "user1@example.com"}],
html_body: "Test email",
subject: "Pleroma test email"
)
end
test "perform" do
Mailer.perform(:deliver_async, @email, [])
assert_email_sent(
from: {"Pleroma", "noreply@example.com"},
to: [{"Test User", "user1@example.com"}],
html_body: "Test email",
subject: "Pleroma test email"
)
end
end

View file

@ -0,0 +1,48 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.UserEmailTest do
use Pleroma.DataCase
alias Pleroma.Emails.UserEmail
alias Pleroma.Web.Endpoint
alias Pleroma.Web.Router
import Pleroma.Factory
test "build password reset email" do
config = Pleroma.Config.get(:instance)
user = insert(:user)
email = UserEmail.password_reset_email(user, "test_token")
assert email.from == {config[:name], config[:notify_email]}
assert email.to == [{user.name, user.email}]
assert email.subject == "Password reset"
assert email.html_body =~ Router.Helpers.reset_password_url(Endpoint, :reset, "test_token")
end
test "build user invitation email" do
config = Pleroma.Config.get(:instance)
user = insert(:user)
token = %Pleroma.UserInviteToken{token: "test-token"}
email = UserEmail.user_invitation_email(user, token, "test@test.com", "Jonh")
assert email.from == {config[:name], config[:notify_email]}
assert email.subject == "Invitation to Pleroma"
assert email.to == [{"Jonh", "test@test.com"}]
assert email.html_body =~
Router.Helpers.redirect_url(Endpoint, :registration_page, token.token)
end
test "build account confirmation email" do
config = Pleroma.Config.get(:instance)
user = insert(:user, info: %Pleroma.User.Info{confirmation_token: "conf-token"})
email = UserEmail.account_confirmation_email(user)
assert email.from == {config[:name], config[:notify_email]}
assert email.to == [{user.name, user.email}]
assert email.subject == "#{config[:name]} account confirmation"
assert email.html_body =~
Router.Helpers.confirm_email_url(Endpoint, :confirm_email, user.id, "conf-token")
end
end

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