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/).
## [Unreleased]
### 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
- 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
### Changed
- **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: OpenGraph and TwitterCard providers enabled by default
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
- NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
### Fixed
- Not being able to pin unlisted posts
- 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: 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
- Configuration: OpenGraph and TwitterCard providers enabled by default
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
### Changed
- NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
- Admin API: changed json structure for saving config settings.
## [1.0.0] - 2019-06-29
### Security
@ -88,7 +96,6 @@ Configuration: `federation_incoming_replies_max_depth` option
- OAuth: added job to clean expired access tokens
- MRF: Support for rejecting reports 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.
- Configuration: `skip_thread_containment` option
- Configuration: `rate_limit` option. See `Pleroma.Plugs.RateLimiter` documentation for details.

View File

@ -250,13 +250,7 @@ config :pleroma, :instance,
skip_thread_containment: true,
limit_to_local_content: :unauthenticated,
dynamic_configuration: false,
external_user_synchronization: [
enabled: false,
# every 2 hours
interval: 60 * 60 * 2,
max_retries: 3,
limit: 500
]
external_user_synchronization: true
config :pleroma, :markup,
# XXX - unfortunately, inline images must be enabled by default right now, because
@ -501,7 +495,7 @@ config :ueberauth,
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"

View File

@ -23,7 +23,7 @@ config :pleroma, Pleroma.Upload, filters: [], link_name: false
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,
email: "admin@example.com",
@ -65,7 +65,9 @@ config :pleroma, Pleroma.ScheduledActivity,
total_user_limit: 3,
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"

View File

@ -573,7 +573,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
configs: [
{
"group": string,
"key": string,
"key": string or string with leading `:` for atoms,
"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`
### Update config settings
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.
Integer with `i:`, e.g. `"i:150"`.
Tuple with more than 2 values with `{"tuple": ["first_val", Pleroma.Module, []]}`.
Atom keys and values can be passed with `:` in the beginning, e.g. `":upload"`.
Tuples can be passed as `{"tuple": ["first_val", Pleroma.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):
- all settings by this keys:
@ -603,7 +604,7 @@ Compile time settings (need instance reboot):
- Params:
- `configs` => [
- `group` (string)
- `key` (string)
- `key` (string or string with leading `:` for atoms)
- `value` (string, [], {} or {"tuple": []})
- `delete` = true (optional, if parameter must be deleted)
]
@ -616,24 +617,25 @@ Compile time settings (need instance reboot):
{
"group": "pleroma",
"key": "Pleroma.Upload",
"value": {
"uploader": "Pleroma.Uploaders.Local",
"filters": ["Pleroma.Upload.Filter.Dedupe"],
"link_name": ":true",
"proxy_remote": ":false",
"proxy_opts": {
"redirect_on_failure": ":false",
"max_body_length": "i:1048576",
"http": {
"follow_redirect": ":true",
"pool": ":upload"
}
},
"dispatch": {
"value": [
{"tuple": [":uploader", "Pleroma.Uploaders.Local"]},
{"tuple": [":filters", ["Pleroma.Upload.Filter.Dedupe"]]},
{"tuple": [":link_name", true]},
{"tuple": [":proxy_remote", false]},
{"tuple": [":proxy_opts", [
{"tuple": [":redirect_on_failure", false]},
{"tuple": [":max_body_length", 1048576]},
{"tuple": [":http": [
{"tuple": [":follow_redirect", true]},
{"tuple": [":pool", ":upload"]},
]]}
]
]},
{"tuple": [":dispatch", {
"tuple": ["/api/v1/streaming", "Pleroma.Web.MastodonAPI.WebsocketHandler", []]
}
}
}
}]}
]
}
]
}
@ -644,7 +646,7 @@ Compile time settings (need instance reboot):
configs: [
{
"group": string,
"key": string,
"key": string or string with leading `:` for atoms,
"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`
- `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
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
* `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.
* `enabled`: Allows enable/disable send emails. Default: `false`.
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`.
* `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.
* `external_user_synchronization`: Following/followers counters synchronization settings.
* `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.
* `external_user_synchronization`: Enabling following/followers counters synchronization for external users.

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
@moduledoc """
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(
Pleroma.PostgresTypes,
[] ++ 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
import Mix.Pleroma
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
use Mix.Task
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
- `--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.)
- `--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
@ -56,7 +58,9 @@ defmodule Mix.Tasks.Pleroma.Instance do
indexable: :string,
db_configurable: :string,
uploads_dir: :string,
static_dir: :string
static_dir: :string,
listen_ip: :string,
listen_port: :string
],
aliases: [
o: :output,
@ -146,6 +150,22 @@ defmodule Mix.Tasks.Pleroma.Instance do
"n"
) === "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 =
get_option(
options,
@ -188,7 +208,9 @@ defmodule Mix.Tasks.Pleroma.Instance do
db_configurable?: db_configurable?,
static_dir: static_dir,
uploads_dir: uploads_dir,
rum_enabled: rum_enabled
rum_enabled: rum_enabled,
listen_ip: listen_ip,
listen_port: listen_port
)
result_psql =

View File

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

View File

@ -5,14 +5,17 @@
defmodule Pleroma.Activity.Search do
alias Pleroma.Activity
alias Pleroma.Object.Fetcher
alias Pleroma.Repo
alias Pleroma.Pagination
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Visibility
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
limit = Enum.min([Keyword.get(options, :limit), 40])
offset = Keyword.get(options, :offset, 0)
author = Keyword.get(options, :author)
Activity
|> Activity.with_preloaded_object()
@ -20,15 +23,23 @@ defmodule Pleroma.Activity.Search do
|> restrict_public()
|> query_with(index_type, search_query)
|> maybe_restrict_local(user)
|> Repo.all()
|> maybe_restrict_author(author)
|> Pagination.fetch_paginated(%{"offset" => offset, "limit" => limit}, :offset)
|> maybe_fetch(user, search_query)
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
from([a, o] in q,
where: fragment("?->>'type' = 'Create'", a.data),
where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
limit: 40
where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients
)
end

View File

@ -155,11 +155,7 @@ defmodule Pleroma.Application do
start: {Pleroma.Web.Endpoint, :start_link, []},
type: :supervisor
},
%{id: Pleroma.Gopher.Server, start: {Pleroma.Gopher.Server, :start_link, []}},
%{
id: Pleroma.User.SynchronizationWorker,
start: {Pleroma.User.SynchronizationWorker, :start_link, []}
}
%{id: Pleroma.Gopher.Server, start: {Pleroma.Gopher.Server, :start_link, []}}
]
# 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
use Sshd.PasswordAuthenticator
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
use Sshd.ShellHandler
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
use Ecto.Schema

View File

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

View File

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

View File

@ -3,11 +3,58 @@
# SPDX-License-Identifier: AGPL-3.0-only
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
PleromaJobQueue.enqueue(:mailer, __MODULE__, [:deliver_async, email, config])
end
@doc "callback to perform send email from queue"
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

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
@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
@moduledoc "Instance."

View File

@ -44,7 +44,15 @@ defmodule Pleroma.Object do
Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id)))
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 \\ [])
# 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!
def normalize(%Object{} = object, _, _), do: object
@ -55,25 +63,15 @@ defmodule Pleroma.Object do
%Object{id: "pleroma:fake_object_id", data: data}
end
# Catch and log Object.normalize() calls where the Activity's child object is not
# preloaded.
# No preloaded object
def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}, fetch_remote, _) do
Logger.debug(
"Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!"
)
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
warn_on_no_object_preloaded(ap_id)
normalize(ap_id, fetch_remote)
end
# No preloaded object
def normalize(%Activity{data: %{"object" => ap_id}}, fetch_remote, _) do
Logger.debug(
"Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!"
)
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
warn_on_no_object_preloaded(ap_id)
normalize(ap_id, fetch_remote)
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
alias Pleroma.HTTP
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
@enforce_keys [:id, :formerType, :deleted]
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
@moduledoc """
Implements Mastodon-compatible pagination.
@ -10,16 +14,28 @@ defmodule Pleroma.Pagination do
@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)
query
|> paginate(options)
|> paginate(options, :keyset)
|> Repo.all()
|> enforce_order(options)
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
|> restrict(:min_id, options)
|> restrict(:since_id, options)
@ -28,11 +44,18 @@ defmodule Pleroma.Pagination do
|> restrict(:limit, options)
end
def paginate(query, options, :offset) do
query
|> restrict(:offset, options)
|> restrict(:limit, options)
end
defp cast_params(params) do
param_types = %{
min_id: :string,
since_id: :string,
max_id: :string,
offset: :integer,
limit: :integer
}
@ -66,6 +89,10 @@ defmodule Pleroma.Pagination do
order_by(query, [u], fragment("? desc nulls last", u.id))
end
defp restrict(query, :offset, %{offset: offset}) do
offset(query, ^offset)
end
defp restrict(query, :limit, options) do
limit = Map.get(options, :limit, @default_limit)

View File

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

View File

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

View File

@ -4,6 +4,7 @@
defmodule Pleroma.Plugs.OAuthScopesPlug do
import Plug.Conn
import Pleroma.Web.Gettext
@behaviour Plug
@ -30,11 +31,14 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
true ->
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
|> put_resp_content_type("application/json")
|> send_resp(403, Jason.encode!(%{error: error_message}))
|> send_resp(:forbidden, Jason.encode!(%{error: error_message}))
|> halt()
end
end

View File

@ -44,8 +44,7 @@ defmodule Pleroma.Plugs.RateLimiter do
...
end
"""
import Phoenix.Controller, only: [json: 2]
import Pleroma.Web.TranslationHelpers
import Plug.Conn
alias Pleroma.User
@ -63,7 +62,7 @@ defmodule Pleroma.Plugs.RateLimiter do
def call(conn, opts) do
case check_rate(conn, opts) do
{:ok, _count} -> conn
{:error, _count} -> render_error(conn)
{:error, _count} -> render_throttled_error(conn)
end
end
@ -85,10 +84,9 @@ defmodule Pleroma.Plugs.RateLimiter do
|> Enum.join(".")
end
defp render_error(conn) do
defp render_throttled_error(conn) do
conn
|> put_status(:too_many_requests)
|> json(%{error: "Throttled"})
|> render_error(:too_many_requests, "Throttled")
|> halt()
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 Pleroma.Web.Gettext
require Logger
@behaviour Plug
@ -45,7 +46,7 @@ defmodule Pleroma.Plugs.UploadedMedia do
else
_ ->
conn
|> send_resp(500, "Failed")
|> send_resp(:internal_server_error, dgettext("errors", "Failed"))
|> halt()
end
end
@ -64,7 +65,7 @@ defmodule Pleroma.Plugs.UploadedMedia do
conn
else
conn
|> send_resp(404, "Not found")
|> send_resp(:not_found, dgettext("errors", "Not found"))
|> halt()
end
end
@ -84,7 +85,7 @@ defmodule Pleroma.Plugs.UploadedMedia do
Logger.error("#{__MODULE__}: Unknown get startegy: #{inspect(unknown)}")
conn
|> send_resp(500, "Internal Error")
|> send_resp(:internal_server_error, dgettext("errors", "Internal Error"))
|> halt()
end
end

View File

@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.UserIsAdminPlug do
import Pleroma.Web.TranslationHelpers
import Plug.Conn
alias Pleroma.User
@ -16,8 +17,7 @@ defmodule Pleroma.Plugs.UserIsAdminPlug do
def call(conn, _) do
conn
|> put_resp_content_type("application/json")
|> send_resp(403, Jason.encode!(%{error: "User is not admin."}))
|> render_error(:forbidden, "User is not admin.")
|> halt
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
@callback request(atom(), String.t(), [tuple()], String.t(), list()) ::
{:ok, pos_integer(), [tuple()], reference() | map()}

View File

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

View File

@ -52,6 +52,7 @@ defmodule Pleroma.User do
field(:avatar, :map)
field(:local, :boolean, default: true)
field(:follower_address, :string)
field(:following_address, :string)
field(:search_rank, :float, virtual: true)
field(:search_type, :integer, virtual: true)
field(:tags, {:array, :string}, default: [])
@ -108,6 +109,10 @@ defmodule Pleroma.User do
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
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
following_count =
if args[:following_count], do: args[:following_count], else: following_count(user)
@ -129,6 +134,7 @@ defmodule Pleroma.User do
Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user, args))
end
@spec restrict_deactivated(Ecto.Query.t()) :: Ecto.Query.t()
def restrict_deactivated(query) do
from(u in query,
where: not fragment("? \\? 'deactivated' AND ?->'deactivated' @> 'true'", u.info, u.info)
@ -163,9 +169,10 @@ defmodule Pleroma.User do
if changes.valid? do
case info_cng.changes[:source_data] do
%{"followers" => followers} ->
%{"followers" => followers, "following" => following} ->
changes
|> put_change(:follower_address, followers)
|> put_change(:following_address, following)
_ ->
followers = User.ap_followers(%User{nickname: changes.changes[:nickname]})
@ -197,7 +204,14 @@ defmodule Pleroma.User do
|> User.Info.user_upgrade(params[:info])
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)
|> validate_format(:nickname, local_nickname_regex())
|> validate_length(:bio, max: 5000)
@ -938,6 +952,8 @@ defmodule Pleroma.User do
@spec perform(atom(), User.t()) :: {:ok, User.t()}
def perform(:delete, %User{} = user) do
{:ok, _user} = ActivityPub.delete(user)
# Remove all relationships
{:ok, followers} = User.get_followers(user)
@ -954,8 +970,8 @@ defmodule Pleroma.User do
end)
delete_user_activities(user)
{:ok, _user} = Repo.delete(user)
invalidate_cache(user)
Repo.delete(user)
end
@spec perform(atom(), User.t()) :: {:ok, User.t()}
@ -1011,42 +1027,20 @@ defmodule Pleroma.User do
)
end
@spec sync_follow_counter() :: :ok
def sync_follow_counter,
do: PleromaJobQueue.enqueue(:background, __MODULE__, [:sync_follow_counters])
@spec perform(:sync_follow_counters) :: :ok
def perform(:sync_follow_counters) do
{: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
@spec external_users_query() :: Ecto.Query.t()
def external_users_query do
User.Query.build(%{
external: true,
active: true,
order_by: :id
})
end
@spec external_users(keyword()) :: [User.t()]
def external_users(opts \\ []) do
query =
User.Query.build(%{
external: true,
active: true,
order_by: :id,
select: [:id, :ap_id, :info]
})
external_users_query()
|> select([u], struct(u, [:id, :ap_id, :info]))
query =
if opts[:max_id],

View File

@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.Search do
alias Pleroma.Pagination
alias Pleroma.Repo
alias Pleroma.User
import Ecto.Query
@ -18,8 +19,7 @@ defmodule Pleroma.User.Search do
for_user = Keyword.get(opts, :for_user)
# Strip the beginning @ off if there is a query
query_string = String.trim_leading(query_string, "@")
query_string = format_query(query_string)
maybe_resolve(resolve, for_user, query_string)
@ -33,13 +33,24 @@ defmodule Pleroma.User.Search do
query_string
|> search_query(for_user, following)
|> paginate(result_limit, offset)
|> Repo.all()
|> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset)
end)
results
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
for_user
|> base_query(following)
@ -76,10 +87,6 @@ defmodule Pleroma.User.Search do
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
from(s in trigram_subquery, union_all: ^fts_subquery)
end
@ -151,7 +158,7 @@ defmodule Pleroma.User.Search do
defp fts_search_subquery(query, term) do
processed_query =
String.trim_trailing(term, "@" <> local_domain())
|> String.replace(~r/\W+/, " ")
|> String.replace(~r/[!-\/|@|[-`|{-~|:-?]+/, " ")
|> String.trim()
|> String.split()
|> 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
alias Pleroma.User
alias Pleroma.Web.CommonAPI

View File

@ -405,6 +405,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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
user = User.get_cached_by_ap_id(actor)
to = (object.data["to"] || []) ++ (object.data["cc"] || [])
@ -981,6 +994,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
avatar: avatar,
name: data["name"],
follower_address: data["followers"],
following_address: data["following"],
bio: data["summary"]
}

View File

@ -31,9 +31,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
conn
else
conn
|> put_status(404)
|> json(%{error: "not found"})
|> halt
|> render_error(:not_found, "not found")
|> halt()
end
end
@ -190,7 +189,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
Logger.info(inspect(conn.req_headers))
end
json(conn, "error")
json(conn, dgettext("errors", "error"))
end
def relay(conn, _params) do
@ -218,9 +217,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("inbox.json", %{user: user, max_id: params["max_id"]}))
else
err =
dgettext("errors", "can't read inbox of %{nickname} as %{as_nickname}",
nickname: nickname,
as_nickname: user.nickname
)
conn
|> put_status(:forbidden)
|> json("can't read inbox of #{nickname} as #{user.nickname}")
|> json(err)
end
end
@ -246,7 +251,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
{:ok, delete} <- ActivityPub.delete(object) do
{:ok, delete}
else
_ -> {:error, "Can't delete object"}
_ -> {:error, dgettext("errors", "Can't delete object")}
end
end
@ -255,12 +260,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
{:ok, activity, _object} <- ActivityPub.like(user, object) do
{:ok, activity}
else
_ -> {:error, "Can't like object"}
_ -> {:error, dgettext("errors", "Can't like object")}
end
end
def handle_user_activity(_, _) do
{:error, "Unhandled activity type"}
{:error, dgettext("errors", "Unhandled activity type")}
end
def update_outbox(
@ -288,22 +293,28 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|> json(message)
end
else
err =
dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",
nickname: nickname,
as_nickname: user.nickname
)
conn
|> put_status(:forbidden)
|> json("can't update outbox of #{nickname} as #{user.nickname}")
|> json(err)
end
end
def errors(conn, {:error, :not_found}) do
conn
|> put_status(404)
|> json("Not found")
|> put_status(:not_found)
|> json(dgettext("errors", "Not found"))
end
def errors(conn, _e) do
conn
|> put_status(500)
|> json("error")
|> put_status(:internal_server_error)
|> json(dgettext("errors", "error"))
end
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
@reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
def filter_by_summary(
%{"summary" => parent_summary} = _parent,
%{data: %{"summary" => parent_summary}} = _in_reply_to,
%{"summary" => child_summary} = child
)
when not is_nil(child_summary) and byte_size(child_summary) > 0 and
@ -24,17 +25,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
end
end
def filter_by_summary(_parent, 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_by_summary(_in_reply_to, child), do: child
def filter(%{"type" => "Create", "object" => child_object} = object) do
child =
if(in_reply_to,
do: filter_by_summary(in_reply_to.data, child),
else: child
)
child_object["inReplyTo"]
|> Object.normalize(child_object["inReplyTo"])
|> filter_by_summary(child_object)
object = Map.put(object, "object", child)

View File

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

View File

@ -8,18 +8,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
@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])
child = object["object"]
content =
child["content"]
child_object["content"]
|> HTML.filter_tags(scrub_policy)
child = Map.put(child, "content", content)
object = Map.put(object, "object", child)
object = put_in(object, ["object", "content"], content)
{:ok, object}
end

View File

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

View File

@ -19,12 +19,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
- `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(_), do: []
defp process_tag(
"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
tags = (object["tag"] || []) ++ ["nsfw"]
@ -41,7 +46,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
defp process_tag(
"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
object = Map.delete(object, "attachment")
@ -52,19 +60,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
defp process_tag(
"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
user = User.get_cached_by_ap_id(actor)
if Enum.member?(to, "https://www.w3.org/ns/activitystreams#Public") do
to =
List.delete(to, "https://www.w3.org/ns/activitystreams#Public") ++ [user.follower_address]
cc =
List.delete(cc, user.follower_address) ++ ["https://www.w3.org/ns/activitystreams#Public"]
if Enum.member?(to, @public) do
to = List.delete(to, @public) ++ [user.follower_address]
cc = List.delete(cc, user.follower_address) ++ [@public]
object =
message["object"]
object
|> Map.put("to", to)
|> Map.put("cc", cc)
@ -82,19 +93,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
defp process_tag(
"mrf_tag:sandbox",
%{"type" => "Create", "to" => to, "cc" => cc, "actor" => actor} = message
%{
"type" => "Create",
"to" => to,
"cc" => cc,
"actor" => actor,
"object" => object
} = message
) do
user = User.get_cached_by_ap_id(actor)
if Enum.member?(to, "https://www.w3.org/ns/activitystreams#Public") or
Enum.member?(cc, "https://www.w3.org/ns/activitystreams#Public") do
to =
List.delete(to, "https://www.w3.org/ns/activitystreams#Public") ++ [user.follower_address]
cc = List.delete(cc, "https://www.w3.org/ns/activitystreams#Public")
if Enum.member?(to, @public) or Enum.member?(cc, @public) do
to = List.delete(to, @public) ++ [user.follower_address]
cc = List.delete(cc, @public)
object =
message["object"]
object
|> Map.put("to", to)
|> Map.put("cc", cc)
@ -123,7 +137,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
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}

View File

@ -21,7 +21,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
@impl true
def filter(%{"actor" => actor} = object) do
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)
end

View File

@ -641,7 +641,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
# an error or a tombstone. This would allow us to verify that a deletion actually took
# place.
def handle_incoming(
%{"type" => "Delete", "object" => object_id, "actor" => _actor, "id" => _id} = data,
%{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = data,
_options
) do
object_id = Utils.get_ap_id(object_id)
@ -653,7 +653,30 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, activity} <- ActivityPub.delete(object, false) do
{:ok, activity}
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
@ -1064,6 +1087,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
PleromaJobQueue.enqueue(:transmogrifier, __MODULE__, [:user_upgrade, user])
end
if Pleroma.Config.get([:instance, :external_user_synchronization]) do
update_following_followers_counters(user)
end
{:ok, user}
else
%User{} = user -> {:ok, user}
@ -1096,4 +1123,27 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
data
|> maybe_fix_user_url
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

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
alias Pleroma.Activity
alias Pleroma.Object

View File

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

View File

@ -5,6 +5,7 @@
defmodule Pleroma.Web.AdminAPI.Config do
use Ecto.Schema
import Ecto.Changeset
import Pleroma.Web.Gettext
alias __MODULE__
alias Pleroma.Repo
@ -57,104 +58,95 @@ defmodule Pleroma.Web.AdminAPI.Config do
with %Config{} = config <- Config.get_by_params(params) do
Repo.delete(config)
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
@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()
def from_binary_to_map(binary) do
@spec from_binary_with_convert(binary()) :: any()
def from_binary_with_convert(binary) do
from_binary(binary)
|> do_convert()
end
defp do_convert([{k, v}] = value) when is_list(value) and length(value) == 1,
do: %{k => do_convert(v)}
defp do_convert(entity) when is_list(entity) do
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),
do: %{k => do_convert(v)}
defp do_convert({:dispatch, [entity]}), do: %{"tuple" => [":dispatch", [inspect(entity)]]}
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
string = to_string(value)
defp do_convert(entity) when is_atom(entity) do
string = to_string(entity)
if String.starts_with?(string, "Elixir."),
do: String.trim_leading(string, "Elixir."),
else: value
do: do_convert(string),
else: ":" <> string
end
defp do_convert("Elixir." <> module_name), do: module_name
defp do_convert(entity) when is_binary(entity), do: entity
@spec transform(any()) :: binary()
def transform(%{"tuple" => _} = 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)
def transform(entity) when is_binary(entity) or is_map(entity) or is_list(entity) do
:erlang.term_to_binary(do_transform(entity))
end
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
{do_transform(k), do_transform(values)}
defp do_transform(%{"tuple" => [":dispatch", [entity]]}) do
cleaned_string = String.replace(entity, ~r/[^\w|^{:,[|^,|^[|^\]^}|^\/|^\.|^"]^\s/, "")
{dispatch_settings, []} = Code.eval_string(cleaned_string, [], requires: [], macros: [])
{:dispatch, [dispatch_settings]}
end
defp do_transform(%{"tuple" => values}) do
Enum.reduce(values, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end)
defp do_transform(%{"tuple" => entity}) do
Enum.reduce(entity, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end)
end
defp do_transform(value) when is_map(value) do
values = for {key, val} <- value, into: [], do: {String.to_atom(key), do_transform(val)}
Enum.sort(values)
defp do_transform(entity) when is_map(entity) do
for {k, v} <- entity, into: %{}, do: {do_transform(k), do_transform(v)}
end
defp do_transform(value) when is_list(value) do
Enum.map(value, &do_transform(&1))
defp do_transform(entity) when is_list(entity) do
for v <- entity, into: [], do: do_transform(v)
end
defp do_transform(entity) when is_list(entity) and length(entity) == 1, do: hd(entity)
defp do_transform(value) when is_binary(value) do
String.trim(value)
defp do_transform(entity) when is_binary(entity) do
String.trim(entity)
|> do_transform_string()
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
cond do
String.starts_with?(value, "Pleroma") or String.starts_with?(value, "Phoenix") ->
String.to_existing_atom("Elixir." <> 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
if String.starts_with?(value, "Pleroma") or String.starts_with?(value, "Phoenix"),
do: String.to_existing_atom("Elixir." <> value),
else: value
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
use Pleroma.Web, :view
@ -11,7 +15,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigView do
%{
key: config.key,
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

View File

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

View File

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

View File

@ -7,13 +7,9 @@ defmodule Pleroma.Web.Endpoint do
socket("/socket", Pleroma.Web.UserSocket)
# 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(Pleroma.Plugs.SetLocalePlug)
plug(CORSPlug)
plug(Pleroma.Plugs.HTTPSecurityPlug)
plug(Pleroma.Plugs.UploadedMedia)
@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.Static,
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
import Ecto.Query
import Ecto.Changeset

View File

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

View File

@ -4,61 +4,18 @@
defmodule Pleroma.Web.MastodonAPI.SearchController do
use Pleroma.Web, :controller
alias Pleroma.Activity
alias Pleroma.Plugs.RateLimiter
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web
alias Pleroma.Web.ControllerHelper
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.ControllerHelper
require Logger
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
plug(RateLimiter, :search when action in [:search, :search2, :account_search])
def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = User.search(query, search_options(params, user))
@ -67,17 +24,86 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
json(conn, res)
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
[
resolve: params["resolve"] == "true",
following: params["following"] == "true",
limit: ControllerHelper.fetch_integer_param(params, "limit"),
offset: ControllerHelper.fetch_integer_param(params, "offset"),
type: params["type"],
author: get_author(params),
for_user: user
]
|> Enum.filter(&elem(&1, 1))
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
f.()
rescue
@ -86,4 +112,9 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
fallback
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

View File

@ -59,13 +59,13 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
#
def errors(conn, {:error, :not_found}) do
conn
|> put_status(404)
|> json("Not found")
|> put_status(:not_found)
|> json(dgettext("errors", "Not found"))
end
def errors(conn, _) do
conn
|> put_status(500)
|> json("Something went wrong")
|> put_status(:internal_server_error)
|> json(dgettext("errors", "Something went wrong"))
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
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]
# TODO: Add cached version.
defp get_replied_to_activities([]), do: %{}
defp get_replied_to_activities(activities) do
activities
|> Enum.map(fn
@ -147,8 +149,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
tags = object.data["tag"] || []
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 =
activity.recipients
(object.data["to"] ++ tag_mentions)
|> Enum.uniq()
|> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end)
|> Enum.filter(& &1)
|> 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
use Pleroma.Web, :view
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
alias Pleroma.Web.Metadata.Providers.Provider
@behaviour Provider

View File

@ -29,7 +29,7 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do
else
false ->
conn
|> put_status(403)
|> put_status(:forbidden)
|> json(false)
_ ->

View File

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

View File

@ -9,21 +9,24 @@ defmodule Pleroma.Web.OAuth.FallbackController do
def call(conn, {:register, :generic_error}) do
conn
|> 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)
end
def call(conn, {:register, _error}) do
conn
|> put_status(:unauthorized)
|> put_flash(:error, "Invalid Username/Password")
|> put_flash(:error, dgettext("errors", "Invalid Username/Password"))
|> OAuthController.registration_details(conn.params)
end
def call(conn, _error) do
conn
|> put_status(:unauthorized)
|> put_flash(:error, "Invalid Username/Password")
|> put_flash(:error, dgettext("errors", "Invalid Username/Password"))
|> OAuthController.authorize(conn.params)
end
end

View File

@ -90,7 +90,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
redirect(conn, external: url)
else
conn
|> put_flash(:error, "Unlisted redirect_uri.")
|> put_flash(:error, dgettext("errors", "Unlisted redirect_uri."))
|> redirect(external: redirect_uri(conn, redirect_uri))
end
end
@ -128,7 +128,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
redirect(conn, external: url)
else
conn
|> put_flash(:error, "Unlisted redirect_uri.")
|> put_flash(:error, dgettext("errors", "Unlisted redirect_uri."))
|> redirect(external: redirect_uri(conn, redirect_uri))
end
end
@ -142,7 +142,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
# Per https://github.com/tootsuite/mastodon/blob/
# 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L39
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)
|> authorize(params)
end
@ -155,7 +155,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
# Per https://github.com/tootsuite/mastodon/blob/
# 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76
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)
|> authorize(params)
end
@ -176,9 +176,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
json(conn, Token.Response.build(user, token, response_attrs))
else
_error ->
put_status(conn, 400)
|> json(%{error: "Invalid credentials"})
_error -> render_invalid_credentials_error(conn)
end
end
@ -192,9 +190,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
json(conn, Token.Response.build(user, token, response_attrs))
else
_error ->
put_status(conn, 400)
|> json(%{error: "Invalid credentials"})
_error -> render_invalid_credentials_error(conn)
end
end
@ -214,18 +210,13 @@ defmodule Pleroma.Web.OAuth.OAuthController do
{:auth_active, false} ->
# Per https://github.com/tootsuite/mastodon/blob/
# 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76
conn
|> put_status(:forbidden)
|> json(%{error: "Your login is missing a confirmed e-mail address"})
render_error(conn, :forbidden, "Your login is missing a confirmed e-mail address")
{:user_active, false} ->
conn
|> put_status(:forbidden)
|> json(%{error: "Your account is currently disabled"})
render_error(conn, :forbidden, "Your account is currently disabled")
_error ->
put_status(conn, 400)
|> json(%{error: "Invalid credentials"})
render_invalid_credentials_error(conn)
end
end
@ -247,9 +238,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
{:ok, token} <- Token.exchange_token(app, auth) do
json(conn, Token.Response.build_for_client_credentials(token))
else
_error ->
put_status(conn, 400)
|> json(%{error: "Invalid credentials"})
_error -> render_invalid_credentials_error(conn)
end
end
@ -271,9 +260,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
# Response for bad request
defp bad_request(%Plug.Conn{} = conn, _) do
conn
|> put_status(500)
|> json(%{error: "Bad request"})
render_error(conn, :internal_server_error, "Bad request")
end
@doc "Prepares OAuth request to provider for Ueberauth"
@ -304,9 +291,11 @@ defmodule Pleroma.Web.OAuth.OAuthController do
def request(%Plug.Conn{} = conn, params) do
message =
if params["provider"] do
"Unsupported OAuth provider: #{params["provider"]}."
dgettext("errors", "Unsupported OAuth provider: %{provider}.",
provider: params["provider"]
)
else
"Bad OAuth request."
dgettext("errors", "Bad OAuth request.")
end
conn
@ -320,7 +309,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do
message = Enum.join(messages, "; ")
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"]))
end
@ -350,7 +342,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
Logger.debug(inspect(["OAUTH_ERROR", error, conn.assigns]))
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"]))
end
end
@ -468,4 +460,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|> String.split()
|> Enum.at(0)
end
defp render_invalid_credentials_error(conn) do
render_error(conn, :bad_request, "Invalid credentials")
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
@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
@moduledoc """
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
@moduledoc """
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
@moduledoc """
Auxiliary functions for dealing with tokens.

View File

@ -245,14 +245,10 @@ defmodule Pleroma.Web.OStatus.OStatusController do
end
def errors(conn, {:error, :not_found}) do
conn
|> put_status(404)
|> text("Not found")
render_error(conn, :not_found, "Not found")
end
def errors(conn, _) do
conn
|> put_status(500)
|> text("Something went wrong")
render_error(conn, :internal_server_error, "Something went wrong")
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
def parse(html, data, prefix, error_message, key_name, value_name \\ "content") do
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
def parse(html, _data) do
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
def parse(html, data) do
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
def parse(html, data) do
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
use Pleroma.Web, :controller
@ -8,7 +12,7 @@ defmodule Pleroma.Web.UploaderController do
end
def callbacks(conn, _) do
send_resp(conn, 400, "bad request")
render_error(conn, :bad_request, "bad request")
end
defp process_callback(conn, pid, params) when is_pid(pid) do
@ -20,6 +24,6 @@ defmodule Pleroma.Web.UploaderController do
end
defp process_callback(conn, _, _) do
send_resp(conn, 400, "bad request")
render_error(conn, :bad_request, "bad request")
end
end

View File

@ -23,9 +23,11 @@ defmodule Pleroma.Web do
def controller do
quote do
use Phoenix.Controller, namespace: Pleroma.Web
import Plug.Conn
import Pleroma.Web.Gettext
import Pleroma.Web.Router.Helpers
import Pleroma.Web.TranslationHelpers
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
import Plug.Conn,
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
def to_xml({tag, attributes, content}) do
open_tag = make_open_tag(tag, attributes)

View File

@ -125,7 +125,7 @@ defmodule Pleroma.Mixfile do
{:cors_plug, "~> 1.5"},
{:ex_doc, "~> 0.20.2", only: :dev, runtime: false},
{:web_push_encryption, "~> 0.2.1"},
{:swoosh, "~> 0.20"},
{:swoosh, "~> 0.23.2"},
{:phoenix_swoosh, "~> 0.2"},
{:gen_smtp, "~> 0.13"},
{: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"},
"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"},
"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"},
"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"},
@ -34,10 +34,8 @@
"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"},
"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_stage": {:hex, :gen_stage, "0.14.1", "9d46723fda072d4f4bb31a102560013f7960f5d80ea44dcb96fd6304ed61e7a4", [:mix], [], "hexpm"},
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
"gettext": {:hex, :gettext, "0.16.1", "e2130b25eebcbe02bb343b119a07ae2c7e28bd4b146c4a154da2ffb2b3507af2", [:mix], [], "hexpm"},
"gen_smtp": {:hex, :gen_smtp, "0.14.0", "39846a03522456077c6429b4badfd1d55e5e7d0fdfb65e935b7c5e38549d9202", [:rebar3], [], "hexpm"},
"gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [: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"},
"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"},
@ -72,19 +70,20 @@
"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"},
"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"},
"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_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_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"},
"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"},
"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"},
"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.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"},
"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"},
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]},
"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"},

View File

@ -91,3 +91,375 @@ msgstr ""
msgid "must be equal to %{number}"
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
## date. Leave `msgstr`s empty as changing them here as no
## effect: edit them in PO (`.po`) files instead.
## From Ecto.Changeset.cast/4
msgid "can't be blank"
msgstr ""
@ -89,3 +88,375 @@ msgstr ""
msgid "must be equal to %{number}"
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,
url: [host: "<%= domain %>", scheme: "https", port: <%= port %>],
http: [ip: {<%= String.replace(listen_ip, ".", ", ") %>}, port: <%= listen_port %>],
secret_key_base: "<%= secret %>",
signing_salt: "<%= signing_salt %>"

View File

@ -6,6 +6,7 @@ defmodule Pleroma.ActivityTest do
use Pleroma.DataCase
alias Pleroma.Activity
alias Pleroma.Bookmark
alias Pleroma.Object
alias Pleroma.ThreadMute
import Pleroma.Factory
@ -18,15 +19,18 @@ defmodule Pleroma.ActivityTest do
test "returns activities by it's objects AP ids" do
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
end
test "returns the activity that created an object" do
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
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
use Pleroma.DataCase
alias Pleroma.Activity
@ -59,6 +63,7 @@ defmodule Pleroma.BBS.HandlerTest do
another_user = insert(:user)
{:ok, activity} = CommonAPI.post(another_user, %{"status" => "this is a test post"})
activity_object = Object.normalize(activity)
output =
capture_io(fn ->
@ -76,8 +81,9 @@ defmodule Pleroma.BBS.HandlerTest do
)
assert reply.actor == user.ap_id
object = Object.normalize(reply)
assert object.data["content"] == "this is a reply"
assert object.data["inReplyTo"] == activity.data["object"]
reply_object_data = Object.normalize(reply).data
assert reply_object_data["content"] == "this is a reply"
assert reply_object_data["inReplyTo"] == activity_object.data["id"]
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
use Pleroma.DataCase
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
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