Merge branch 'develop' into ci/build-armv7-docker-images
This commit is contained in:
commit
b1d72e16a3
74 changed files with 2676 additions and 216 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
when:
|
when:
|
||||||
- event: pull_request
|
- event: pull_request
|
||||||
|
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
|
||||||
|
|
||||||
labels:
|
labels:
|
||||||
platform: linux/amd64
|
platform: linux/amd64
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
when:
|
when:
|
||||||
- event: pull_request
|
- event: pull_request
|
||||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
|
||||||
- event: push
|
- event: push
|
||||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
when:
|
when:
|
||||||
- event: pull_request
|
- event: pull_request
|
||||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
|
||||||
- event: push
|
- event: push
|
||||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
when:
|
when:
|
||||||
- event: pull_request
|
- event: pull_request
|
||||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
|
||||||
- event: push
|
- event: push
|
||||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
|
|
||||||
1
changelog.d/captcha-package.fix
Normal file
1
changelog.d/captcha-package.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Switch native captcha to the published pleroma_captcha Hex package.
|
||||||
1
changelog.d/context-cleanup.skip
Normal file
1
changelog.d/context-cleanup.skip
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
litepub-0.1.jsonld cleanup
|
||||||
1
changelog.d/host-header-verification.security
Normal file
1
changelog.d/host-header-verification.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Ensure Host header is present and matches instance URI
|
||||||
1
changelog.d/http-signatures-0.1.3.fix
Normal file
1
changelog.d/http-signatures-0.1.3.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Fix compatibility with timestamped HTTP Signatures used by GoToSocial
|
||||||
1
changelog.d/iceshrimpnet-reports-fix.fix
Normal file
1
changelog.d/iceshrimpnet-reports-fix.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Handle reports with just actor ap id as the object
|
||||||
1
changelog.d/majic-1.2.0.change
Normal file
1
changelog.d/majic-1.2.0.change
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update majic to 1.2.0.
|
||||||
1
changelog.d/mastodon-websocket-protocol.fix
Normal file
1
changelog.d/mastodon-websocket-protocol.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Echo Mastodon-style `Sec-WebSocket-Protocol` tokens in streaming WebSocket handshakes.
|
||||||
1
changelog.d/mfm-backend.add
Normal file
1
changelog.d/mfm-backend.add
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Add backend support for Misskey Markdown (MFM) posts
|
||||||
0
changelog.d/mfm-extend.skip
Normal file
0
changelog.d/mfm-extend.skip
Normal file
1
changelog.d/oban-plugins-lazarus-hex.change
Normal file
1
changelog.d/oban-plugins-lazarus-hex.change
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Use the Hex package for oban_plugins_lazarus.
|
||||||
1
changelog.d/phoenix-sec-websocket-headers.change
Normal file
1
changelog.d/phoenix-sec-websocket-headers.change
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Switch patched Phoenix 1.7.14 back to upstream with Phoenix 1.8.0+
|
||||||
1
changelog.d/pleroma-fe-link.fix
Normal file
1
changelog.d/pleroma-fe-link.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Updated Pleroma-FE build URL after Forgejo migration
|
||||||
1
changelog.d/poll-voters-count-inflation.fix
Normal file
1
changelog.d/poll-voters-count-inflation.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Fix votersCount inflation when same voter picks multiple options
|
||||||
1
changelog.d/reject-third-party-reports.fix
Normal file
1
changelog.d/reject-third-party-reports.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Reject incoming reports when both the reporter and reported account are remote
|
||||||
1
changelog.d/remote-ip-hex.change
Normal file
1
changelog.d/remote-ip-hex.change
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Use upstream Hex releases for the `remote_ip` dependency and expose client IP ranges for remote IP resolution.
|
||||||
1
changelog.d/user-search-sorting.change
Normal file
1
changelog.d/user-search-sorting.change
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Improve user search / autocompletion ordering.
|
||||||
0
changelog.d/weblate-ci.skip
Normal file
0
changelog.d/weblate-ci.skip
Normal file
1
changelog.d/wss-necroposts.fix
Normal file
1
changelog.d/wss-necroposts.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
RichMedia: Fix backfill causing old posts to show up on timelines by disabling it in MastoAPI StatusView
|
||||||
|
|
@ -203,7 +203,8 @@ config :pleroma, :instance,
|
||||||
"text/plain",
|
"text/plain",
|
||||||
"text/html",
|
"text/html",
|
||||||
"text/markdown",
|
"text/markdown",
|
||||||
"text/bbcode"
|
"text/bbcode",
|
||||||
|
"text/x.misskeymarkdown"
|
||||||
],
|
],
|
||||||
autofollowed_nicknames: [],
|
autofollowed_nicknames: [],
|
||||||
autofollowing_nicknames: [],
|
autofollowing_nicknames: [],
|
||||||
|
|
@ -737,6 +738,7 @@ config :pleroma, Pleroma.Workers.PurgeExpiredActivity, enabled: true, min_lifeti
|
||||||
config :pleroma, Pleroma.Web.Plugs.RemoteIp,
|
config :pleroma, Pleroma.Web.Plugs.RemoteIp,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
headers: ["x-forwarded-for"],
|
headers: ["x-forwarded-for"],
|
||||||
|
clients: [],
|
||||||
proxies: [],
|
proxies: [],
|
||||||
reserved: [
|
reserved: [
|
||||||
"127.0.0.0/8",
|
"127.0.0.0/8",
|
||||||
|
|
@ -775,7 +777,7 @@ config :pleroma, :frontends,
|
||||||
"name" => "pleroma-fe",
|
"name" => "pleroma-fe",
|
||||||
"git" => "https://git.pleroma.social/pleroma/pleroma-fe",
|
"git" => "https://git.pleroma.social/pleroma/pleroma-fe",
|
||||||
"build_url" =>
|
"build_url" =>
|
||||||
"https://git.pleroma.social/pleroma/pleroma-fe/-/jobs/artifacts/${ref}/download?job=build",
|
"https://git.pleroma.social/api/packages/pleroma/generic/pleroma-fe-builds/${ref}/latest.zip",
|
||||||
"ref" => "develop"
|
"ref" => "develop"
|
||||||
},
|
},
|
||||||
"fedi-fe" => %{
|
"fedi-fe" => %{
|
||||||
|
|
|
||||||
|
|
@ -815,7 +815,8 @@ config :pleroma, :config_description, [
|
||||||
"text/plain",
|
"text/plain",
|
||||||
"text/html",
|
"text/html",
|
||||||
"text/markdown",
|
"text/markdown",
|
||||||
"text/bbcode"
|
"text/bbcode",
|
||||||
|
"text/x.misskeymarkdown"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
|
|
@ -1394,7 +1395,13 @@ config :pleroma, :config_description, [
|
||||||
label: "Post Content Type",
|
label: "Post Content Type",
|
||||||
type: {:dropdown, :atom},
|
type: {:dropdown, :atom},
|
||||||
description: "Default post formatting option",
|
description: "Default post formatting option",
|
||||||
suggestions: ["text/plain", "text/html", "text/markdown", "text/bbcode"]
|
suggestions: [
|
||||||
|
"text/plain",
|
||||||
|
"text/html",
|
||||||
|
"text/markdown",
|
||||||
|
"text/bbcode",
|
||||||
|
"text/x.misskeymarkdown"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :redirectRootNoLogin,
|
key: :redirectRootNoLogin,
|
||||||
|
|
@ -2903,7 +2910,7 @@ config :pleroma, :config_description, [
|
||||||
key: Pleroma.Web.Plugs.RemoteIp,
|
key: Pleroma.Web.Plugs.RemoteIp,
|
||||||
type: :group,
|
type: :group,
|
||||||
description: """
|
description: """
|
||||||
`Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
|
`Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://hex.pm/packages/remote_ip) but with runtime configuration.
|
||||||
**If your instance is not behind at least one reverse proxy, you should not enable this plug.**
|
**If your instance is not behind at least one reverse proxy, you should not enable this plug.**
|
||||||
""",
|
""",
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -2919,6 +2926,12 @@ config :pleroma, :config_description, [
|
||||||
A list of strings naming the HTTP headers to use when deriving the true client IP. Default: `["x-forwarded-for"]`.
|
A list of strings naming the HTTP headers to use when deriving the true client IP. Default: `["x-forwarded-for"]`.
|
||||||
"""
|
"""
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
key: :clients,
|
||||||
|
type: {:list, :string},
|
||||||
|
description:
|
||||||
|
"A list of client IPs or subnets in CIDR notation. These will not be treated as proxies or reserved ranges. Defaults to `[]`. IPv4 entries without a bitmask will be assumed to be /32 and IPv6 /128."
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
key: :proxies,
|
key: :proxies,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,13 @@ defmodule Pleroma.Formatter do
|
||||||
Earmark.as_html!(text, %Earmark.Options{compact_output: true, smartypants: false})
|
Earmark.as_html!(text, %Earmark.Options{compact_output: true, smartypants: false})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def markdown_to_html(text, opts) do
|
||||||
|
Earmark.as_html!(
|
||||||
|
text,
|
||||||
|
%Earmark.Options{compact_output: true, smartypants: false} |> Map.merge(opts)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def html_escape({text, mentions, hashtags}, type) do
|
def html_escape({text, mentions, hashtags}, type) do
|
||||||
{html_escape(text, type), mentions, hashtags}
|
{html_escape(text, type), mentions, hashtags}
|
||||||
end
|
end
|
||||||
|
|
@ -135,6 +142,10 @@ defmodule Pleroma.Formatter do
|
||||||
HTML.filter_tags(text)
|
HTML.filter_tags(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def html_escape(text, "text/x.misskeymarkdown") do
|
||||||
|
HTML.filter_tags(text)
|
||||||
|
end
|
||||||
|
|
||||||
def html_escape(text, "text/plain") do
|
def html_escape(text, "text/plain") do
|
||||||
Regex.split(@link_regex, text, include_captures: true)
|
Regex.split(@link_regex, text, include_captures: true)
|
||||||
|> Enum.map_every(2, fn chunk ->
|
|> Enum.map_every(2, fn chunk ->
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,8 @@ defmodule Pleroma.Frontend do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp download_build(frontend_info, dest) do
|
defp download_build(frontend_info, dest) do
|
||||||
Logger.info("Downloading pre-built bundle for #{frontend_info["name"]}")
|
|
||||||
url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"])
|
url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"])
|
||||||
|
Logger.info("Downloading pre-built bundle for #{frontend_info["name"]} from #{url}")
|
||||||
|
|
||||||
with {:ok, %{status: 200, body: zip_body}} <-
|
with {:ok, %{status: 200, body: zip_body}} <-
|
||||||
Pleroma.HTTP.get(url, [], pool: :media, recv_timeout: 120_000) do
|
Pleroma.HTTP.get(url, [], pool: :media, recv_timeout: 120_000) do
|
||||||
|
|
|
||||||
|
|
@ -372,12 +372,20 @@ defmodule Pleroma.Object do
|
||||||
option
|
option
|
||||||
end)
|
end)
|
||||||
|
|
||||||
voters = [actor | object.data["voters"] || []] |> Enum.uniq()
|
existing_voters = object.data["voters"] || []
|
||||||
|
voters = [actor | existing_voters] |> Enum.uniq()
|
||||||
|
new_voter? = actor not in existing_voters
|
||||||
|
existing_voters_count = object.data["votersCount"]
|
||||||
|
|
||||||
voters_count =
|
voters_count =
|
||||||
if Map.has_key?(object.data, "votersCount") do
|
cond do
|
||||||
object.data["votersCount"] + 1
|
is_integer(existing_voters_count) and new_voter? ->
|
||||||
else
|
existing_voters_count + 1
|
||||||
|
|
||||||
|
is_integer(existing_voters_count) ->
|
||||||
|
existing_voters_count
|
||||||
|
|
||||||
|
true ->
|
||||||
length(voters)
|
length(voters)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ defmodule Pleroma.Signature do
|
||||||
|> put_req_header("(request-target)", request_target)
|
|> put_req_header("(request-target)", request_target)
|
||||||
|> put_req_header("@request-target", request_target)
|
|> put_req_header("@request-target", request_target)
|
||||||
|
|
||||||
@http_signatures_impl.validate_conn(conn)
|
@http_signatures_impl.validate_conn(conn) == true
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec validate_signature(Plug.Conn.t()) :: boolean()
|
@spec validate_signature(Plug.Conn.t()) :: boolean()
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.User.Search do
|
defmodule Pleroma.User.Search do
|
||||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators.Uri, as: UriType
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators.Uri, as: UriType
|
||||||
|
alias Pleroma.Instances.Instance
|
||||||
alias Pleroma.Pagination
|
alias Pleroma.Pagination
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
|
|
@ -88,12 +89,13 @@ defmodule Pleroma.User.Search do
|
||||||
|> filter_invisible_users()
|
|> filter_invisible_users()
|
||||||
|> filter_internal_users()
|
|> filter_internal_users()
|
||||||
|> filter_blocked_domains(for_user)
|
|> filter_blocked_domains(for_user)
|
||||||
|
|> filter_unreachable_users()
|
||||||
|> fts_search(query_string)
|
|> fts_search(query_string)
|
||||||
|> select_top_users(top_user_ids)
|
|> select_top_users(top_user_ids)
|
||||||
|> trigram_rank(query_string)
|
|> trigram_rank(query_string)
|
||||||
|> boost_search_rank(for_user, top_user_ids)
|
|> boost_search_rank(for_user, top_user_ids)
|
||||||
|> subquery()
|
|> subquery()
|
||||||
|> order_by(desc: :search_rank)
|
|> order_by_search_rank(for_user)
|
||||||
|> maybe_restrict_local(for_user)
|
|> maybe_restrict_local(for_user)
|
||||||
|> maybe_restrict_accepting_chat_messages(capabilities)
|
|> maybe_restrict_accepting_chat_messages(capabilities)
|
||||||
|> filter_deactivated_users()
|
|> filter_deactivated_users()
|
||||||
|
|
@ -196,6 +198,14 @@ defmodule Pleroma.User.Search do
|
||||||
|
|
||||||
defp filter_blocked_domains(query, _), do: query
|
defp filter_blocked_domains(query, _), do: query
|
||||||
|
|
||||||
|
defp filter_unreachable_users(query) do
|
||||||
|
from(u in query,
|
||||||
|
left_join: i in Instance,
|
||||||
|
on: i.host == fragment("substring(? from '.*://([^/]*)')", u.ap_id),
|
||||||
|
where: is_nil(i.unreachable_since)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
defp maybe_resolve(true, user, query) do
|
defp maybe_resolve(true, user, query) do
|
||||||
case {limit(), user} do
|
case {limit(), user} do
|
||||||
{:all, _} -> :noop
|
{:all, _} -> :noop
|
||||||
|
|
@ -236,6 +246,16 @@ defmodule Pleroma.User.Search do
|
||||||
|
|
||||||
from(u in subquery(query),
|
from(u in subquery(query),
|
||||||
select_merge: %{
|
select_merge: %{
|
||||||
|
search_type:
|
||||||
|
fragment(
|
||||||
|
"""
|
||||||
|
CASE WHEN (?) THEN 2
|
||||||
|
WHEN (?) THEN 1
|
||||||
|
ELSE 0 END
|
||||||
|
""",
|
||||||
|
u.id in ^top_user_ids,
|
||||||
|
u.id in ^friends_ids or u.id in ^followers_ids
|
||||||
|
),
|
||||||
search_rank:
|
search_rank:
|
||||||
fragment(
|
fragment(
|
||||||
"""
|
"""
|
||||||
|
|
@ -261,6 +281,14 @@ defmodule Pleroma.User.Search do
|
||||||
defp boost_search_rank(query, _for_user, top_user_ids) do
|
defp boost_search_rank(query, _for_user, top_user_ids) do
|
||||||
from(u in subquery(query),
|
from(u in subquery(query),
|
||||||
select_merge: %{
|
select_merge: %{
|
||||||
|
search_type:
|
||||||
|
fragment(
|
||||||
|
"""
|
||||||
|
CASE WHEN (?) THEN 2
|
||||||
|
ELSE 0 END
|
||||||
|
""",
|
||||||
|
u.id in ^top_user_ids
|
||||||
|
),
|
||||||
search_rank:
|
search_rank:
|
||||||
fragment(
|
fragment(
|
||||||
"""
|
"""
|
||||||
|
|
@ -273,4 +301,22 @@ defmodule Pleroma.User.Search do
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp order_by_search_rank(query, %User{}) do
|
||||||
|
order_by(
|
||||||
|
query,
|
||||||
|
[u],
|
||||||
|
desc: u.search_type,
|
||||||
|
desc_nulls_last:
|
||||||
|
fragment(
|
||||||
|
"CASE WHEN ? = 1 THEN COALESCE(?, ?) ELSE NULL END",
|
||||||
|
u.search_type,
|
||||||
|
u.last_status_at,
|
||||||
|
u.last_active_at
|
||||||
|
),
|
||||||
|
desc: u.search_rank
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp order_by_search_rank(query, _), do: order_by(query, desc: :search_rank)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ defmodule Pleroma.Web do
|
||||||
|
|
||||||
def controller do
|
def controller do
|
||||||
quote do
|
quote do
|
||||||
use Phoenix.Controller, namespace: Pleroma.Web
|
use Phoenix.Controller, formats: [json: "View", html: "View"]
|
||||||
|
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
|
|
@ -42,7 +42,10 @@ defmodule Pleroma.Web do
|
||||||
plug(:set_put_layout)
|
plug(:set_put_layout)
|
||||||
|
|
||||||
defp set_put_layout(conn, _) do
|
defp set_put_layout(conn, _) do
|
||||||
put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
|
case Pleroma.Config.get(:app_layout, "app.html") do
|
||||||
|
false -> put_layout(conn, false)
|
||||||
|
layout -> put_layout(conn, {Pleroma.Web.LayoutView, layout})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Marks plugs intentionally skipped and blocks their execution if present in plugs chain
|
# Marks plugs intentionally skipped and blocks their execution if present in plugs chain
|
||||||
|
|
|
||||||
|
|
@ -303,7 +303,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do
|
def inbox(
|
||||||
|
%{
|
||||||
|
assigns: %{valid_signature: true, valid_host_header: true}
|
||||||
|
} = conn,
|
||||||
|
%{"nickname" => nickname} = params
|
||||||
|
) do
|
||||||
with {:recipient_exists, %User{} = recipient} <-
|
with {:recipient_exists, %User{} = recipient} <-
|
||||||
{:recipient_exists, User.get_cached_by_nickname(nickname)},
|
{:recipient_exists, User.get_cached_by_nickname(nickname)},
|
||||||
{:sender_exists, {:ok, %User{} = actor}} <-
|
{:sender_exists, {:ok, %User{} = actor}} <-
|
||||||
|
|
@ -342,7 +347,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def inbox(%{assigns: %{valid_signature: true}} = conn, params) do
|
def inbox(%{assigns: %{valid_signature: true, valid_host_header: true}} = conn, params) do
|
||||||
Federator.incoming_ap_doc(params)
|
Federator.incoming_ap_doc(params)
|
||||||
json(conn, "ok")
|
json(conn, "ok")
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||||
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
|
@ -26,6 +29,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
||||||
end
|
end
|
||||||
|
|
||||||
field(:replies, {:array, ObjectValidators.ObjectID}, default: [])
|
field(:replies, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
|
field(:source, :map)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast_and_apply(data) do
|
def cast_and_apply(data) do
|
||||||
|
|
@ -80,6 +84,113 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
||||||
|
|
||||||
def fix_attachments(data), do: data
|
def fix_attachments(data), do: data
|
||||||
|
|
||||||
|
defp remote_mention_resolver(
|
||||||
|
%{"id" => ap_id, "tag" => tags},
|
||||||
|
"@" <> nickname = mention,
|
||||||
|
buffer,
|
||||||
|
opts,
|
||||||
|
acc
|
||||||
|
)
|
||||||
|
when is_binary(ap_id) and is_list(tags) do
|
||||||
|
initial_host =
|
||||||
|
ap_id
|
||||||
|
|> URI.parse()
|
||||||
|
|> Map.get(:host)
|
||||||
|
|
||||||
|
with mention_tag when not is_nil(mention_tag) <-
|
||||||
|
Enum.find(tags, &mention_tag?(&1, mention, initial_host)),
|
||||||
|
href when is_binary(href) <- mention_tag["href"],
|
||||||
|
%User{} = user <- User.get_cached_by_ap_id(href) do
|
||||||
|
link = Pleroma.Formatter.mention_from_user(user, opts)
|
||||||
|
{link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}
|
||||||
|
else
|
||||||
|
_ -> {buffer, acc}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp remote_mention_resolver(_object, _mention, buffer, _opts, acc), do: {buffer, acc}
|
||||||
|
|
||||||
|
defp mention_tag?(%{"type" => "Mention", "name" => name}, mention, initial_host)
|
||||||
|
when is_binary(name) do
|
||||||
|
name == mention || mention == "#{name}@#{initial_host}"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp mention_tag?(_tag, _mention, _initial_host), do: false
|
||||||
|
|
||||||
|
defp scrub_content(%{"content" => content} = object) when is_binary(content) do
|
||||||
|
Map.put(object, "content", HTML.filter_tags(content))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp scrub_content(object), do: object
|
||||||
|
|
||||||
|
defp mfm_parse_limit do
|
||||||
|
min(Pleroma.Config.get([:instance, :limit]), Pleroma.Config.get([:instance, :remote_limit]))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_source(%{"source" => source} = object) when is_binary(source) do
|
||||||
|
object
|
||||||
|
|> Map.put("source", %{"content" => source})
|
||||||
|
|> normalize_source()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_source(%{"source" => source} = object) when is_map(source) do
|
||||||
|
source =
|
||||||
|
case source["content"] do
|
||||||
|
content when is_binary(content) ->
|
||||||
|
if String.length(content) <= mfm_parse_limit() do
|
||||||
|
source
|
||||||
|
else
|
||||||
|
Map.delete(source, "content")
|
||||||
|
end
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
source
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Map.delete(source, "content")
|
||||||
|
end
|
||||||
|
|
||||||
|
Map.put(object, "source", source)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_source(object), do: object
|
||||||
|
|
||||||
|
defp fix_misskey_content(%{"htmlMfm" => true, "content" => content} = object)
|
||||||
|
when is_binary(content) do
|
||||||
|
Map.put(object, "content", HTML.filter_tags(content))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fix_misskey_content(%{"htmlMfm" => true} = object), do: object
|
||||||
|
|
||||||
|
defp fix_misskey_content(
|
||||||
|
%{"source" => %{"mediaType" => "text/x.misskeymarkdown", "content" => content}} = object
|
||||||
|
)
|
||||||
|
when is_binary(content) do
|
||||||
|
mention_handler = fn nick, buffer, opts, acc ->
|
||||||
|
remote_mention_resolver(object, nick, buffer, opts, acc)
|
||||||
|
end
|
||||||
|
|
||||||
|
{linked, _mentions, _tags} =
|
||||||
|
Utils.format_input(content, "text/x.misskeymarkdown", mention_handler: mention_handler)
|
||||||
|
|
||||||
|
Map.put(object, "content", linked)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fix_misskey_content(%{"source" => %{"mediaType" => "text/x.misskeymarkdown"}} = object),
|
||||||
|
do: scrub_content(object)
|
||||||
|
|
||||||
|
defp fix_misskey_content(%{"_misskey_content" => content} = object) when is_binary(content) do
|
||||||
|
object
|
||||||
|
|> Map.put("source", %{
|
||||||
|
"content" => content,
|
||||||
|
"mediaType" => "text/x.misskeymarkdown"
|
||||||
|
})
|
||||||
|
|> Map.delete("_misskey_content")
|
||||||
|
|> fix_misskey_content()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fix_misskey_content(object), do: object
|
||||||
|
|
||||||
defp fix(data) do
|
defp fix(data) do
|
||||||
data
|
data
|
||||||
|> CommonFixes.fix_actor()
|
|> CommonFixes.fix_actor()
|
||||||
|
|
@ -88,6 +199,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
||||||
|> fix_tag()
|
|> fix_tag()
|
||||||
|> fix_replies()
|
|> fix_replies()
|
||||||
|> fix_attachments()
|
|> fix_attachments()
|
||||||
|
|> normalize_source()
|
||||||
|
|> fix_misskey_content()
|
||||||
|> CommonFixes.fix_quote_url()
|
|> CommonFixes.fix_quote_url()
|
||||||
|> CommonFixes.fix_likes()
|
|> CommonFixes.fix_likes()
|
||||||
|> Transmogrifier.fix_emoji()
|
|> Transmogrifier.fix_emoji()
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do
|
||||||
quote bind_quoted: binding() do
|
quote bind_quoted: binding() do
|
||||||
field(:content, :string)
|
field(:content, :string)
|
||||||
field(:contentMap, ObjectValidators.ContentLanguageMap)
|
field(:contentMap, ObjectValidators.ContentLanguageMap)
|
||||||
|
field(:htmlMfm, :boolean)
|
||||||
|
|
||||||
field(:published, ObjectValidators.DateTime)
|
field(:published, ObjectValidators.DateTime)
|
||||||
field(:updated, ObjectValidators.DateTime)
|
field(:updated, ObjectValidators.DateTime)
|
||||||
|
|
|
||||||
|
|
@ -430,6 +430,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp reject_third_party_report(%User{local: false}, %User{local: false} = account) do
|
||||||
|
{:reject, "[Transmogrifier] third-party report: #{account.ap_id}"}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp reject_third_party_report(_, _), do: :ok
|
||||||
|
|
||||||
def handle_incoming(data, options \\ []) do
|
def handle_incoming(data, options \\ []) do
|
||||||
data
|
data
|
||||||
|> fix_recursive(&strip_internal_fields/1)
|
|> fix_recursive(&strip_internal_fields/1)
|
||||||
|
|
@ -444,9 +450,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
) do
|
) do
|
||||||
with context <- data["context"] || Utils.generate_context_id(),
|
with context <- data["context"] || Utils.generate_context_id(),
|
||||||
content <- data["content"] || "",
|
content <- data["content"] || "",
|
||||||
|
objects <- List.wrap(objects),
|
||||||
%User{} = actor <- User.get_cached_by_ap_id(actor),
|
%User{} = actor <- User.get_cached_by_ap_id(actor),
|
||||||
# Reduce the object list to find the reported user.
|
# Reduce the object list to find the reported user.
|
||||||
%User{} = account <- get_reported(objects),
|
%User{} = account <- get_reported(objects),
|
||||||
|
:ok <- reject_third_party_report(actor, account),
|
||||||
# Remove the reported user from the object list.
|
# Remove the reported user from the object list.
|
||||||
statuses <- Enum.filter(objects, fn ap_id -> ap_id != account.ap_id end) do
|
statuses <- Enum.filter(objects, fn ap_id -> ap_id != account.ap_id end) do
|
||||||
%{
|
%{
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
"#{Endpoint.url()}/schemas/litepub-0.1.jsonld",
|
"#{Endpoint.url()}/schemas/litepub-0.1.jsonld",
|
||||||
%{
|
%{
|
||||||
"@language" => get_language(data)
|
"@language" => get_language(data),
|
||||||
|
"htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -317,6 +317,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
|
|
||||||
emoji = Map.merge(emoji, summary_emoji)
|
emoji = Map.merge(emoji, summary_emoji)
|
||||||
|
|
||||||
|
media_type = Utils.get_content_type(draft.params[:content_type])
|
||||||
{:ok, note_data, _meta} = Builder.note(draft)
|
{:ok, note_data, _meta} = Builder.note(draft)
|
||||||
|
|
||||||
object =
|
object =
|
||||||
|
|
@ -324,14 +325,18 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
|> Map.put("emoji", emoji)
|
|> Map.put("emoji", emoji)
|
||||||
|> Map.put("source", %{
|
|> Map.put("source", %{
|
||||||
"content" => draft.status,
|
"content" => draft.status,
|
||||||
"mediaType" => Utils.get_content_type(draft.params[:content_type])
|
"mediaType" => media_type
|
||||||
})
|
})
|
||||||
|
|> maybe_put("htmlMfm", true, media_type == "text/x.misskeymarkdown")
|
||||||
|> Map.put("generator", draft.params[:generator])
|
|> Map.put("generator", draft.params[:generator])
|
||||||
|> Map.put("language", draft.language)
|
|> Map.put("language", draft.language)
|
||||||
|
|
||||||
%{draft | object: object}
|
%{draft | object: object}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp maybe_put(map, key, value, true), do: Map.put(map, key, value)
|
||||||
|
defp maybe_put(map, _key, _value, _condition), do: map
|
||||||
|
|
||||||
defp preview?(%__MODULE__{} = draft) do
|
defp preview?(%__MODULE__{} = draft) do
|
||||||
preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview])
|
preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview])
|
||||||
%{draft | preview?: preview?}
|
%{draft | preview?: preview?}
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,14 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
|> Formatter.linkify(options)
|
|> Formatter.linkify(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def format_input(text, "text/x.misskeymarkdown", options) do
|
||||||
|
text
|
||||||
|
|> Formatter.markdown_to_html(%{breaks: true})
|
||||||
|
|> safe_mfm_to_html()
|
||||||
|
|> Formatter.linkify(options)
|
||||||
|
|> Formatter.html_escape("text/x.misskeymarkdown")
|
||||||
|
end
|
||||||
|
|
||||||
def format_input(text, "text/markdown", options) do
|
def format_input(text, "text/markdown", options) do
|
||||||
text
|
text
|
||||||
|> Formatter.mentions_escape(options)
|
|> Formatter.mentions_escape(options)
|
||||||
|
|
@ -330,6 +338,16 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
|> Formatter.html_escape("text/html")
|
|> Formatter.html_escape("text/html")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp safe_mfm_to_html(html) do
|
||||||
|
html
|
||||||
|
|> MfmParser.Parser.parse()
|
||||||
|
|> MfmParser.Encoder.to_html()
|
||||||
|
rescue
|
||||||
|
_ -> html
|
||||||
|
catch
|
||||||
|
_, _ -> html
|
||||||
|
end
|
||||||
|
|
||||||
def format_naive_asctime(date) do
|
def format_naive_asctime(date) do
|
||||||
date |> DateTime.from_naive!("Etc/UTC") |> format_asctime
|
date |> DateTime.from_naive!("Etc/UTC") |> format_asctime
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
|
||||||
socket("/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler,
|
plug(Pleroma.Web.MastodonAPI.WebsocketPlug,
|
||||||
longpoll: false,
|
path: "/api/v1/streaming",
|
||||||
websocket: [
|
websocket: [
|
||||||
path: "/",
|
path: "/",
|
||||||
compress: false,
|
compress: false,
|
||||||
connect_info: [:sec_websocket_protocol],
|
connect_info: [:sec_websocket_headers],
|
||||||
error_handler: {Pleroma.Web.MastodonAPI.WebsocketHandler, :handle_error, []},
|
error_handler: {Pleroma.Web.MastodonAPI.WebsocketHandler, :handle_error, []},
|
||||||
fullsweep_after: 20
|
fullsweep_after: 20
|
||||||
]
|
]
|
||||||
|
|
@ -169,8 +169,7 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
else: "pleroma_key"
|
else: "pleroma_key"
|
||||||
|
|
||||||
extra =
|
extra =
|
||||||
Config.get([__MODULE__, :extra_cookie_attrs])
|
Enum.join(Config.get([__MODULE__, :extra_cookie_attrs]), ";")
|
||||||
|> Enum.join(";")
|
|
||||||
|
|
||||||
# The session will be stored in the cookie and signed,
|
# The session will be stored in the cookie and signed,
|
||||||
# this means its contents can be read but not tampered with.
|
# this means its contents can be read but not tampered with.
|
||||||
|
|
|
||||||
|
|
@ -71,12 +71,12 @@ defmodule Pleroma.Web.MastodonAPI.PollView do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp voters_count(%{data: %{"votersCount" => voters}}) when is_integer(voters), do: voters
|
||||||
|
|
||||||
defp voters_count(%{data: %{"voters" => [_ | _] = voters}}) do
|
defp voters_count(%{data: %{"voters" => [_ | _] = voters}}) do
|
||||||
length(voters)
|
length(voters)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp voters_count(%{data: %{"votersCount" => voters}}), do: voters
|
|
||||||
|
|
||||||
defp voters_count(_), do: 0
|
defp voters_count(_), do: 0
|
||||||
|
|
||||||
defp voted_and_own_votes(%{object: object} = params, options) do
|
defp voted_and_own_votes(%{object: object} = params, options) do
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||||
# This is a naive way to do this, just spawning a process per activity
|
# This is a naive way to do this, just spawning a process per activity
|
||||||
# to fetch the preview. However it should be fine considering
|
# to fetch the preview. However it should be fine considering
|
||||||
# pagination is restricted to 40 activities at a time
|
# pagination is restricted to 40 activities at a time
|
||||||
defp fetch_rich_media_for_activities(activities) do
|
# Force disable Websockets streaming for backfill jobs,
|
||||||
|
# otherwise old posts can show up on timelines.
|
||||||
|
defp fetch_rich_media_for_activities(activities, opts) do
|
||||||
|
opts = Map.put(opts, :stream, false)
|
||||||
|
|
||||||
Enum.each(activities, fn activity ->
|
Enum.each(activities, fn activity ->
|
||||||
Card.get_by_activity(activity)
|
Card.get_by_activity(activity, opts)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -113,7 +117,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||||
activities = Enum.filter(opts.activities, & &1)
|
activities = Enum.filter(opts.activities, & &1)
|
||||||
|
|
||||||
# Start prefetching rich media before doing anything else
|
# Start prefetching rich media before doing anything else
|
||||||
fetch_rich_media_for_activities(activities)
|
fetch_rich_media_for_activities(activities, opts)
|
||||||
|
|
||||||
replied_to_activities = get_replied_to_activities(activities)
|
replied_to_activities = get_replied_to_activities(activities)
|
||||||
quoted_activities = get_quoted_activities(activities)
|
quoted_activities = get_quoted_activities(activities)
|
||||||
|
|
||||||
|
|
@ -361,8 +366,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||||
|
|
||||||
summary = object.data["summary"] || ""
|
summary = object.data["summary"] || ""
|
||||||
|
|
||||||
|
# Force disable Websockets streaming for backfill jobs which the below call will create,
|
||||||
|
# otherwise old posts can show up on timelines.
|
||||||
card =
|
card =
|
||||||
case Card.get_by_activity(activity) do
|
case Card.get_by_activity(activity, Map.put(opts, :stream, false)) do
|
||||||
%Card{} = result -> render("card.json", result)
|
%Card{} = result -> render("card.json", result)
|
||||||
_ -> nil
|
_ -> nil
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -67,9 +67,10 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
||||||
|
|
||||||
@impl Phoenix.Socket.Transport
|
@impl Phoenix.Socket.Transport
|
||||||
def handle_in({text, [opcode: :text]}, state) do
|
def handle_in({text, [opcode: :text]}, state) do
|
||||||
with {:ok, %{} = event} <- Jason.decode(text) do
|
case Jason.decode(text) do
|
||||||
|
{:ok, %{} = event} ->
|
||||||
handle_client_event(event, state)
|
handle_client_event(event, state)
|
||||||
else
|
|
||||||
_ ->
|
_ ->
|
||||||
Logger.error("#{__MODULE__} received non-JSON event: #{inspect(text)}")
|
Logger.error("#{__MODULE__} received non-JSON event: #{inspect(text)}")
|
||||||
{:ok, state}
|
{:ok, state}
|
||||||
|
|
@ -85,11 +86,11 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
||||||
def handle_info({:render_with_user, view, template, item, topic}, state) do
|
def handle_info({:render_with_user, view, template, item, topic}, state) do
|
||||||
user = %User{} = User.get_cached_by_ap_id(state.user.ap_id)
|
user = %User{} = User.get_cached_by_ap_id(state.user.ap_id)
|
||||||
|
|
||||||
unless Streamer.filtered_by_user?(user, item) do
|
if Streamer.filtered_by_user?(user, item) do
|
||||||
|
{:ok, state}
|
||||||
|
else
|
||||||
message = view.render(template, item, user, topic)
|
message = view.render(template, item, user, topic)
|
||||||
{:push, {:text, message}, %{state | user: user}}
|
{:push, {:text, message}, %{state | user: user}}
|
||||||
else
|
|
||||||
{:ok, state}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -245,12 +246,20 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
||||||
Plug.Conn.send_resp(conn, 404, "Not Found")
|
Plug.Conn.send_resp(conn, 404, "Not Found")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp find_access_token(%{
|
defp find_access_token(%{connect_info: %{sec_websocket_headers: sec_headers}} = transport_info) do
|
||||||
connect_info: %{sec_websocket_protocol: [token]}
|
find_sec_websocket_protocol(sec_headers) || find_access_token_from_params(transport_info)
|
||||||
}),
|
end
|
||||||
do: token
|
|
||||||
|
defp find_access_token(transport_info), do: find_access_token_from_params(transport_info)
|
||||||
defp find_access_token(%{params: %{"access_token" => token}}), do: token
|
|
||||||
|
defp find_sec_websocket_protocol(sec_headers) do
|
||||||
defp find_access_token(_), do: nil
|
Enum.find_value(sec_headers, fn
|
||||||
|
{"sec-websocket-protocol", protocols} -> protocols |> Plug.Conn.Utils.list() |> List.first()
|
||||||
|
_ -> nil
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp find_access_token_from_params(%{params: %{"access_token" => token}}), do: token
|
||||||
|
|
||||||
|
defp find_access_token_from_params(_), do: nil
|
||||||
end
|
end
|
||||||
|
|
|
||||||
104
lib/pleroma/web/mastodon_api/websocket_plug.ex
Normal file
104
lib/pleroma/web/mastodon_api/websocket_plug.ex
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.WebsocketPlug do
|
||||||
|
@moduledoc """
|
||||||
|
A Phoenix 1.8 compatible WebSocket transport for Mastodon streaming.
|
||||||
|
|
||||||
|
It mirrors Phoenix.Transports.WebSocket, but echoes a successfully authenticated
|
||||||
|
Mastodon-style Sec-WebSocket-Protocol token so browser clients accept the handshake.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@behaviour Plug
|
||||||
|
|
||||||
|
import Plug.Conn
|
||||||
|
|
||||||
|
alias Phoenix.Socket.Transport
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
|
alias Pleroma.Web.MastodonAPI.WebsocketHandler
|
||||||
|
|
||||||
|
@connect_info_opts [:check_csrf]
|
||||||
|
|
||||||
|
@impl Plug
|
||||||
|
def init(opts) do
|
||||||
|
path = String.split(Keyword.fetch!(opts, :path), "/", trim: true)
|
||||||
|
websocket = Keyword.fetch!(opts, :websocket)
|
||||||
|
config = Transport.load_config(websocket, Phoenix.Transports.WebSocket)
|
||||||
|
|
||||||
|
{path, config}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Plug
|
||||||
|
def call(%{method: "GET", path_info: path} = conn, {path, opts}) do
|
||||||
|
conn
|
||||||
|
|> fetch_query_params()
|
||||||
|
|> Transport.code_reload(Endpoint, opts)
|
||||||
|
|> Transport.transport_log(opts[:transport_log])
|
||||||
|
|> Transport.check_origin(WebsocketHandler, Endpoint, opts)
|
||||||
|
|> connect(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(%{path_info: path} = conn, {path, _opts}) do
|
||||||
|
conn
|
||||||
|
|> send_resp(400, "")
|
||||||
|
|> halt()
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(conn, _opts), do: conn
|
||||||
|
|
||||||
|
defp connect(%{halted: true} = conn, _opts), do: conn
|
||||||
|
|
||||||
|
defp connect(%{params: params} = conn, opts) do
|
||||||
|
keys = Keyword.get(opts, :connect_info, [])
|
||||||
|
|
||||||
|
connect_info =
|
||||||
|
Transport.connect_info(conn, Endpoint, keys, Keyword.take(opts, @connect_info_opts))
|
||||||
|
|
||||||
|
config = %{
|
||||||
|
endpoint: Endpoint,
|
||||||
|
transport: :websocket,
|
||||||
|
options: opts,
|
||||||
|
params: params,
|
||||||
|
connect_info: connect_info
|
||||||
|
}
|
||||||
|
|
||||||
|
case WebsocketHandler.connect(config) do
|
||||||
|
{:ok, arg} ->
|
||||||
|
try do
|
||||||
|
conn
|
||||||
|
|> echo_sec_websocket_protocol()
|
||||||
|
|> WebSockAdapter.upgrade(WebsocketHandler, arg, opts)
|
||||||
|
|> halt()
|
||||||
|
rescue
|
||||||
|
e in WebSockAdapter.UpgradeError ->
|
||||||
|
conn
|
||||||
|
|> send_resp(400, e.message)
|
||||||
|
|> halt()
|
||||||
|
end
|
||||||
|
|
||||||
|
:error ->
|
||||||
|
conn
|
||||||
|
|> send_resp(403, "")
|
||||||
|
|> halt()
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
{m, f, args} = opts[:error_handler]
|
||||||
|
|
||||||
|
halt(apply(m, f, [conn, reason | args]))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp echo_sec_websocket_protocol(conn) do
|
||||||
|
case get_req_header(conn, "sec-websocket-protocol") do
|
||||||
|
[protocols | _] ->
|
||||||
|
case Plug.Conn.Utils.list(protocols) do
|
||||||
|
[protocol | _] -> put_resp_header(conn, "sec-websocket-protocol", protocol)
|
||||||
|
[] -> conn
|
||||||
|
end
|
||||||
|
|
||||||
|
[] ->
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
63
lib/pleroma/web/plugs/ensure_host_matches_plug.ex
Normal file
63
lib/pleroma/web/plugs/ensure_host_matches_plug.ex
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Plugs.EnsureHostMatchesPlug do
|
||||||
|
@moduledoc "Ensures Host header matches instance"
|
||||||
|
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
|
|
||||||
|
import Plug.Conn
|
||||||
|
|
||||||
|
def init(options), do: options
|
||||||
|
|
||||||
|
@spec call(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t()
|
||||||
|
def call(%Plug.Conn{assigns: %{valid_signature: true}} = conn, _opts) do
|
||||||
|
# Host header is scheme-less, URI.parse needs the //
|
||||||
|
host_header = get_req_header(conn, "host")
|
||||||
|
host_uri = URI.parse("//#{host_header}")
|
||||||
|
instance_uri = URI.parse(Endpoint.url())
|
||||||
|
|
||||||
|
case host_header do
|
||||||
|
[host] ->
|
||||||
|
cond do
|
||||||
|
host == "" ->
|
||||||
|
resp(conn, 400, "Host header not provided") |> halt()
|
||||||
|
|
||||||
|
true ->
|
||||||
|
if host_matches?(host_uri, instance_uri),
|
||||||
|
do: assign(conn, :valid_host_header, true),
|
||||||
|
else: resp(conn, 400, "Host header does not match this instance") |> halt()
|
||||||
|
end
|
||||||
|
|
||||||
|
[_head | _rest] ->
|
||||||
|
conn
|
||||||
|
|> resp(400, "More than one Host header provided")
|
||||||
|
|> halt()
|
||||||
|
|
||||||
|
[] ->
|
||||||
|
conn
|
||||||
|
|> resp(400, "Host header not provided")
|
||||||
|
|> halt()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Host header may not be provided, but signature verification failed anyway
|
||||||
|
def call(conn, _opts), do: conn
|
||||||
|
|
||||||
|
defp case_insensitive_compare(checked, authority) do
|
||||||
|
String.downcase(checked) == String.downcase(authority)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Host header did not provide port
|
||||||
|
# Host header is scheme-less, URI.parse does not provide default port
|
||||||
|
defp host_matches?(%URI{host: req_host, port: nil}, %URI{host: instance_host}),
|
||||||
|
do: case_insensitive_compare(req_host, instance_host)
|
||||||
|
|
||||||
|
# Host header provided a port
|
||||||
|
# Any port specified in the Endpoint url configuration is valid here
|
||||||
|
defp host_matches?(%URI{host: req_host, port: port}, %URI{host: instance_host, port: port}),
|
||||||
|
do: case_insensitive_compare(req_host, instance_host)
|
||||||
|
|
||||||
|
defp host_matches?(_, _), do: false
|
||||||
|
end
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.Plugs.RemoteIp do
|
defmodule Pleroma.Web.Plugs.RemoteIp do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
This is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
|
This is a shim to call [`RemoteIp`](https://hex.pm/packages/remote_ip) but with runtime configuration.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
|
@ -17,15 +17,29 @@ defmodule Pleroma.Web.Plugs.RemoteIp do
|
||||||
|
|
||||||
def call(%{remote_ip: original_remote_ip} = conn, _) do
|
def call(%{remote_ip: original_remote_ip} = conn, _) do
|
||||||
if Config.get([__MODULE__, :enabled]) do
|
if Config.get([__MODULE__, :enabled]) do
|
||||||
%{remote_ip: new_remote_ip} = conn = RemoteIp.call(conn, remote_ip_opts())
|
new_remote_ip = remote_ip(conn) || original_remote_ip
|
||||||
|
|
||||||
|
conn = %{conn | remote_ip: new_remote_ip}
|
||||||
assign(conn, :remote_ip_found, original_remote_ip != new_remote_ip)
|
assign(conn, :remote_ip_found, original_remote_ip != new_remote_ip)
|
||||||
else
|
else
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp remote_ip(conn) do
|
||||||
|
opts = remote_ip_opts()
|
||||||
|
|
||||||
|
# Do not use RemoteIp.from/2 here: upstream remote_ip always applies its
|
||||||
|
# built-in reserved ranges. Pleroma keeps :reserved configurable, so reuse
|
||||||
|
# only the header parsing and apply Pleroma's own block classification.
|
||||||
|
conn.req_headers
|
||||||
|
|> RemoteIp.Headers.take(opts[:headers])
|
||||||
|
|> RemoteIp.Headers.parse()
|
||||||
|
|> Enum.reverse()
|
||||||
|
|> Enum.find(&client?(&1, opts))
|
||||||
|
end
|
||||||
|
|
||||||
defp remote_ip_opts do
|
defp remote_ip_opts do
|
||||||
headers = Config.get([__MODULE__, :headers], []) |> MapSet.new()
|
|
||||||
reserved = Config.get([__MODULE__, :reserved], [])
|
reserved = Config.get([__MODULE__, :reserved], [])
|
||||||
|
|
||||||
proxies =
|
proxies =
|
||||||
|
|
@ -33,6 +47,26 @@ defmodule Pleroma.Web.Plugs.RemoteIp do
|
||||||
|> Enum.concat(reserved)
|
|> Enum.concat(reserved)
|
||||||
|> Enum.map(&InetHelper.parse_cidr/1)
|
|> Enum.map(&InetHelper.parse_cidr/1)
|
||||||
|
|
||||||
{headers, proxies}
|
clients =
|
||||||
|
Config.get([__MODULE__, :clients], [])
|
||||||
|
|> Enum.map(&InetHelper.parse_cidr/1)
|
||||||
|
|
||||||
|
[
|
||||||
|
headers: Config.get([__MODULE__, :headers], []),
|
||||||
|
clients: clients,
|
||||||
|
proxies: proxies
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp client?(ip, opts) do
|
||||||
|
client_ip?(ip, opts[:clients]) || !proxy_ip?(ip, opts[:proxies])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp client_ip?(ip, clients) do
|
||||||
|
Enum.any?(clients, &InetCidr.contains?(&1, ip))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp proxy_ip?(ip, proxies) do
|
||||||
|
Enum.any?(proxies, &InetCidr.contains?(&1, ip))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ defmodule Pleroma.Web.RichMedia.Backfill do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@callback run(map()) :: :ok | Parser.parse_errors() | Helpers.get_errors()
|
||||||
|
|
||||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||||
@stream_out_impl Pleroma.Config.get(
|
@stream_out_impl Pleroma.Config.get(
|
||||||
[__MODULE__, :stream_out],
|
[__MODULE__, :stream_out],
|
||||||
|
|
@ -26,11 +28,7 @@ defmodule Pleroma.Web.RichMedia.Backfill do
|
||||||
{:ok, card} = Card.create(url, fields)
|
{:ok, card} = Card.create(url, fields)
|
||||||
|
|
||||||
maybe_schedule_expiration(url, fields)
|
maybe_schedule_expiration(url, fields)
|
||||||
|
maybe_update_stream(args)
|
||||||
with %{"activity_id" => activity_id} <- args,
|
|
||||||
false <- is_nil(activity_id) do
|
|
||||||
stream_update(args)
|
|
||||||
end
|
|
||||||
|
|
||||||
warm_cache(url_hash, card)
|
warm_cache(url_hash, card)
|
||||||
:ok
|
:ok
|
||||||
|
|
@ -55,12 +53,17 @@ defmodule Pleroma.Web.RichMedia.Backfill do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp stream_update(%{"activity_id" => activity_id}) do
|
defp maybe_update_stream(%{"activity_id" => activity_id, "stream" => true})
|
||||||
|
when is_binary(activity_id) do
|
||||||
Pleroma.Activity.get_by_id(activity_id)
|
Pleroma.Activity.get_by_id(activity_id)
|
||||||
|> Pleroma.Activity.normalize()
|
|> Pleroma.Activity.normalize()
|
||||||
|> @stream_out_impl.stream_out()
|
|> @stream_out_impl.stream_out()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Streamer.stream_out returns noop when unsupported activity type is requested to be streamed.
|
||||||
|
# Do the same here for unwanted streaming
|
||||||
|
defp maybe_update_stream(_), do: :noop
|
||||||
|
|
||||||
defp warm_cache(key, val), do: @cachex.put(:rich_media_cache, key, val)
|
defp warm_cache(key, val), do: @cachex.put(:rich_media_cache, key, val)
|
||||||
|
|
||||||
defp negative_cache(key, ttl \\ :timer.minutes(15)),
|
defp negative_cache(key, ttl \\ :timer.minutes(15)),
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,18 @@ defmodule Pleroma.Web.RichMedia.Card do
|
||||||
nil ->
|
nil ->
|
||||||
activity_id = Keyword.get(opts, :activity_id, nil)
|
activity_id = Keyword.get(opts, :activity_id, nil)
|
||||||
|
|
||||||
RichMediaWorker.new(%{"op" => "backfill", "url" => url, "activity_id" => activity_id})
|
# Nested opts, first layer comes from get_by_activity/2 as Keyword,
|
||||||
|
# second from API views/Federation as Map.
|
||||||
|
# Provide default Map when called directly.
|
||||||
|
opts = Keyword.get(opts, :opts, %{})
|
||||||
|
stream = Map.get(opts, :stream, true)
|
||||||
|
|
||||||
|
RichMediaWorker.new(%{
|
||||||
|
"op" => "backfill",
|
||||||
|
"url" => url,
|
||||||
|
"activity_id" => activity_id,
|
||||||
|
"stream" => stream
|
||||||
|
})
|
||||||
|> Oban.insert()
|
|> Oban.insert()
|
||||||
|
|
||||||
nil
|
nil
|
||||||
|
|
@ -112,9 +123,11 @@ defmodule Pleroma.Web.RichMedia.Card do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_by_activity(Activity.t()) :: t() | nil | :error
|
@spec get_by_activity(Activity.t(), %{}) :: t() | nil | :error
|
||||||
|
def get_by_activity(activity, opts \\ %{})
|
||||||
|
|
||||||
# Fake/Draft activity
|
# Fake/Draft activity
|
||||||
def get_by_activity(%Activity{id: "pleroma:fakeid"} = activity) do
|
def get_by_activity(%Activity{id: "pleroma:fakeid"} = activity, _opts) do
|
||||||
with {_, true} <- {:config, @config_impl.get([:rich_media, :enabled])},
|
with {_, true} <- {:config, @config_impl.get([:rich_media, :enabled])},
|
||||||
%Object{} = object <- Object.normalize(activity, fetch: false),
|
%Object{} = object <- Object.normalize(activity, fetch: false),
|
||||||
url when not is_nil(url) <- HTML.extract_first_external_url_from_object(object) do
|
url when not is_nil(url) <- HTML.extract_first_external_url_from_object(object) do
|
||||||
|
|
@ -138,13 +151,13 @@ defmodule Pleroma.Web.RichMedia.Card do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_by_activity(activity) do
|
def get_by_activity(activity, opts) do
|
||||||
with %Object{} = object <- Object.normalize(activity, fetch: false),
|
with %Object{} = object <- Object.normalize(activity, fetch: false),
|
||||||
{_, nil} <- {:cached, get_cached_url(object, activity.id)} do
|
{_, nil} <- {:cached, get_cached_url(object, activity.id)} do
|
||||||
nil
|
nil
|
||||||
else
|
else
|
||||||
{:cached, url} ->
|
{:cached, url} ->
|
||||||
get_or_backfill_by_url(url, activity_id: activity.id)
|
get_or_backfill_by_url(url, activity_id: activity.id, opts: opts)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
:error
|
:error
|
||||||
|
|
|
||||||
|
|
@ -216,6 +216,7 @@ defmodule Pleroma.Web.Router do
|
||||||
pipeline :http_signature do
|
pipeline :http_signature do
|
||||||
plug(Pleroma.Web.Plugs.HTTPSignaturePlug)
|
plug(Pleroma.Web.Plugs.HTTPSignaturePlug)
|
||||||
plug(Pleroma.Web.Plugs.MappedSignatureToIdentityPlug)
|
plug(Pleroma.Web.Plugs.MappedSignatureToIdentityPlug)
|
||||||
|
plug(Pleroma.Web.Plugs.EnsureHostMatchesPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :inbox_guard do
|
pipeline :inbox_guard do
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Workers.SignatureRetryWorker do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
|
alias Pleroma.Web.Plugs.EnsureHostMatchesPlug
|
||||||
alias Pleroma.Web.Plugs.MappedSignatureToIdentityPlug
|
alias Pleroma.Web.Plugs.MappedSignatureToIdentityPlug
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
@ -48,6 +49,7 @@ defmodule Pleroma.Workers.SignatureRetryWorker do
|
||||||
{:ok, _public_key} <- Signature.refetch_public_key(conn_data),
|
{:ok, _public_key} <- Signature.refetch_public_key(conn_data),
|
||||||
{:signature, true} <- {:signature, validate_signature(conn_data)},
|
{:signature, true} <- {:signature, validate_signature(conn_data)},
|
||||||
{:same_actor, true} <- {:same_actor, validate_same_actor(conn_data)},
|
{:same_actor, true} <- {:same_actor, validate_same_actor(conn_data)},
|
||||||
|
{:host_header, true} <- {:host_header, validate_host_header(conn_data)},
|
||||||
{:ok, res} <- Federator.perform(:incoming_ap_doc, params) do
|
{:ok, res} <- Federator.perform(:incoming_ap_doc, params) do
|
||||||
unless Instances.reachable?(params["actor"]) do
|
unless Instances.reachable?(params["actor"]) do
|
||||||
domain = URI.parse(params["actor"]).host
|
domain = URI.parse(params["actor"]).host
|
||||||
|
|
@ -103,6 +105,16 @@ defmodule Pleroma.Workers.SignatureRetryWorker do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp validate_host_header(conn_data) do
|
||||||
|
case EnsureHostMatchesPlug.call(conn_data, []) do
|
||||||
|
%Plug.Conn{assigns: %{valid_signature: true, valid_host_header: true}} ->
|
||||||
|
true
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp validate_same_actor(conn_data) do
|
defp validate_same_actor(conn_data) do
|
||||||
case MappedSignatureToIdentityPlug.call(conn_data, []) do
|
case MappedSignatureToIdentityPlug.call(conn_data, []) do
|
||||||
%Plug.Conn{assigns: %{valid_signature: true}} ->
|
%Plug.Conn{assigns: %{valid_signature: true}} ->
|
||||||
|
|
@ -170,6 +182,10 @@ defmodule Pleroma.Workers.SignatureRetryWorker do
|
||||||
{:same_actor, false} ->
|
{:same_actor, false} ->
|
||||||
{:cancel, :actor_signature_mismatch}
|
{:cancel, :actor_signature_mismatch}
|
||||||
|
|
||||||
|
# Host header from request not for us
|
||||||
|
{:host_header, false} ->
|
||||||
|
{:cancel, :host_header_mismatch}
|
||||||
|
|
||||||
# Origin / URL validation failed somewhere possibly due to spoofing
|
# Origin / URL validation failed somewhere possibly due to spoofing
|
||||||
{:error, :origin_containment_failed} ->
|
{:error, :origin_containment_failed} ->
|
||||||
{:cancel, :origin_containment_failed}
|
{:cancel, :origin_containment_failed}
|
||||||
|
|
@ -234,6 +250,7 @@ defmodule Pleroma.Workers.SignatureRetryWorker do
|
||||||
defp log_signature_retry_rejection({:cancel, reason}, context)
|
defp log_signature_retry_rejection({:cancel, reason}, context)
|
||||||
when reason in [
|
when reason in [
|
||||||
:actor_signature_mismatch,
|
:actor_signature_mismatch,
|
||||||
|
:host_header_mismatch,
|
||||||
:invalid_signature,
|
:invalid_signature,
|
||||||
:invalid_signature_retry_metadata,
|
:invalid_signature_retry_metadata,
|
||||||
:missing_signature_retry_metadata,
|
:missing_signature_retry_metadata,
|
||||||
|
|
|
||||||
21
mix.exs
21
mix.exs
|
|
@ -123,8 +123,7 @@ defmodule Pleroma.Mixfile do
|
||||||
# Type `mix help deps` for examples and options.
|
# Type `mix help deps` for examples and options.
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:phoenix,
|
{:phoenix, "~> 1.8.0"},
|
||||||
git: "https://github.com/feld/phoenix", branch: "v1.7.14-websocket-headers", override: true},
|
|
||||||
{:phoenix_ecto, "~> 4.6"},
|
{:phoenix_ecto, "~> 4.6"},
|
||||||
{:ecto_sql, "~> 3.13"},
|
{:ecto_sql, "~> 3.13"},
|
||||||
{:ecto_enum, "~> 1.4"},
|
{:ecto_enum, "~> 1.4"},
|
||||||
|
|
@ -137,9 +136,7 @@ defmodule Pleroma.Mixfile do
|
||||||
{:tzdata, "~> 1.0.5"},
|
{:tzdata, "~> 1.0.5"},
|
||||||
{:plug_cowboy, "~> 2.7"},
|
{:plug_cowboy, "~> 2.7"},
|
||||||
{:oban, "~> 2.19.4"},
|
{:oban, "~> 2.19.4"},
|
||||||
{:oban_plugins_lazarus,
|
{:oban_plugins_lazarus, "~> 0.1.1"},
|
||||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/oban_plugins_lazarus.git",
|
|
||||||
ref: "e49fc355baaf0e435208bf5f534d31e26e897711"},
|
|
||||||
{:oban_web, "~> 2.11"},
|
{:oban_web, "~> 2.11"},
|
||||||
{:gettext, "~> 0.24"},
|
{:gettext, "~> 0.24"},
|
||||||
{:bcrypt_elixir, "~> 2.3"},
|
{:bcrypt_elixir, "~> 2.3"},
|
||||||
|
|
@ -160,6 +157,7 @@ defmodule Pleroma.Mixfile do
|
||||||
{:sweet_xml, "~> 0.7.5"},
|
{:sweet_xml, "~> 0.7.5"},
|
||||||
{:earmark, "1.4.46"},
|
{:earmark, "1.4.46"},
|
||||||
{:bbcode_pleroma, "~> 0.2.0"},
|
{:bbcode_pleroma, "~> 0.2.0"},
|
||||||
|
{:pleroma_mfm_parser, "~> 0.2.1"},
|
||||||
{:cors_plug, "~> 2.0"},
|
{:cors_plug, "~> 2.0"},
|
||||||
{:web_push_encryption, "~> 0.3.1"},
|
{:web_push_encryption, "~> 0.3.1"},
|
||||||
{:swoosh, "~> 1.16.12"},
|
{:swoosh, "~> 1.16.12"},
|
||||||
|
|
@ -172,7 +170,7 @@ defmodule Pleroma.Mixfile do
|
||||||
{:timex, "~> 3.7"},
|
{:timex, "~> 3.7"},
|
||||||
{:ueberauth, "~> 0.10"},
|
{:ueberauth, "~> 0.10"},
|
||||||
{:linkify, "~> 0.5.3"},
|
{:linkify, "~> 0.5.3"},
|
||||||
{:http_signatures, "~> 0.1.2"},
|
{:http_signatures, "~> 0.1.3"},
|
||||||
{:telemetry, "~> 1.0.0", override: true},
|
{:telemetry, "~> 1.0.0", override: true},
|
||||||
{:poolboy, "~> 1.5"},
|
{:poolboy, "~> 1.5"},
|
||||||
{:prom_ex, "~> 1.9"},
|
{:prom_ex, "~> 1.9"},
|
||||||
|
|
@ -183,14 +181,11 @@ defmodule Pleroma.Mixfile do
|
||||||
{:plug_static_index_html, "~> 1.0.0"},
|
{:plug_static_index_html, "~> 1.0.0"},
|
||||||
{:flake_id, "~> 0.1.0"},
|
{:flake_id, "~> 0.1.0"},
|
||||||
{:concurrent_limiter, "~> 0.1.1"},
|
{:concurrent_limiter, "~> 0.1.1"},
|
||||||
{:remote_ip,
|
{:remote_ip, "~> 1.2.0"},
|
||||||
git: "https://git.pleroma.social/pleroma/remote_ip.git",
|
{:inet_cidr, "~> 1.0"},
|
||||||
ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"},
|
{:captcha, "~> 1.0.3", hex: :pleroma_captcha},
|
||||||
{:captcha,
|
|
||||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git",
|
|
||||||
ref: "e7b7cc34cc16b383461b966484c297e4ec9aeef6"},
|
|
||||||
{:restarter, path: "./restarter"},
|
{:restarter, path: "./restarter"},
|
||||||
{:majic, "~> 1.1"},
|
{:majic, "~> 1.2"},
|
||||||
{:open_api_spex, "~> 3.22"},
|
{:open_api_spex, "~> 3.22"},
|
||||||
{:ecto_psql_extras, "~> 0.8"},
|
{:ecto_psql_extras, "~> 0.8"},
|
||||||
{:vix, "~> 0.36"},
|
{:vix, "~> 0.36"},
|
||||||
|
|
|
||||||
15
mix.lock
15
mix.lock
|
|
@ -10,7 +10,7 @@
|
||||||
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
||||||
"cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"},
|
"cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"},
|
||||||
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.1.201603 or ~> 0.5.20 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
|
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.1.201603 or ~> 0.5.20 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
|
||||||
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e7b7cc34cc16b383461b966484c297e4ec9aeef6", [ref: "e7b7cc34cc16b383461b966484c297e4ec9aeef6"]},
|
"captcha": {:hex, :pleroma_captcha, "1.0.3", "6cc1440e91a092653fe909f028cc4c5d83ea858b1439e3b9a85e446382e2b9a3", [:make, :mix], [], "hexpm", "477f62c1a845a9458c546658223295f958f98136acef89c05beb278b1b6f4a14"},
|
||||||
"castore": {:hex, :castore, "1.0.15", "8aa930c890fe18b6fe0a0cff27b27d0d4d231867897bd23ea772dee561f032a3", [:mix], [], "hexpm", "96ce4c69d7d5d7a0761420ef743e2f4096253931a3ba69e5ff8ef1844fe446d3"},
|
"castore": {:hex, :castore, "1.0.15", "8aa930c890fe18b6fe0a0cff27b27d0d4d231867897bd23ea772dee561f032a3", [:mix], [], "hexpm", "96ce4c69d7d5d7a0761420ef743e2f4096253931a3ba69e5ff8ef1844fe446d3"},
|
||||||
"cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"},
|
"cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"},
|
||||||
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
|
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
|
||||||
|
|
@ -62,10 +62,10 @@
|
||||||
"hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"},
|
"hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"},
|
||||||
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
|
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
|
||||||
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
|
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
|
||||||
"http_signatures": {:hex, :http_signatures, "0.1.2", "ed1cc7043abcf5bb4f30d68fb7bad9d618ec1a45c4ff6c023664e78b67d9c406", [:mix], [], "hexpm", "f08aa9ac121829dae109d608d83c84b940ef2f183ae50f2dd1e9a8bc619d8be7"},
|
"http_signatures": {:hex, :http_signatures, "0.1.3", "19f26aee35b4684e37efdce3ac4638605e6e41a04368186bd39d2b6138a60ea9", [:mix], [{:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "20313a65516db88006f85b090f6f76cc5b04e9609b45943657e6781eb91174f4"},
|
||||||
"httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},
|
"httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},
|
||||||
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
|
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
|
||||||
"inet_cidr": {:hex, :inet_cidr, "1.0.8", "d26bb7bdbdf21ae401ead2092bf2bb4bf57fe44a62f5eaa5025280720ace8a40", [:mix], [], "hexpm", "d5b26da66603bb56c933c65214c72152f0de9a6ea53618b56d63302a68f6a90e"},
|
"inet_cidr": {:hex, :inet_cidr, "1.0.9", "e0ef72a2942529da78c8e4147d53f2ef5f6f5293335c3637b0fdf83c012cc816", [:mix], [], "hexpm", "172da15ff7cf635b1feaf14f5818be28c811b37cc5fb7c5f7c01058c1c1066cc"},
|
||||||
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
|
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
|
||||||
"joken": {:hex, :joken, "2.6.2", "5daaf82259ca603af4f0b065475099ada1b2b849ff140ccd37f4b6828ca6892a", [:mix], [{:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5134b5b0a6e37494e46dbf9e4dad53808e5e787904b7c73972651b51cce3d72b"},
|
"joken": {:hex, :joken, "2.6.2", "5daaf82259ca603af4f0b065475099ada1b2b849ff140ccd37f4b6828ca6892a", [:mix], [{:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5134b5b0a6e37494e46dbf9e4dad53808e5e787904b7c73972651b51cce3d72b"},
|
||||||
"jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"},
|
"jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"},
|
||||||
|
|
@ -73,7 +73,7 @@
|
||||||
"linkify": {:hex, :linkify, "0.5.3", "5f8143d8f61f5ff08d3aeeff47ef6509492b4948d8f08007fbf66e4d2246a7f2", [:mix], [], "hexpm", "3ef35a1377d47c25506e07c1c005ea9d38d700699d92ee92825f024434258177"},
|
"linkify": {:hex, :linkify, "0.5.3", "5f8143d8f61f5ff08d3aeeff47ef6509492b4948d8f08007fbf66e4d2246a7f2", [:mix], [], "hexpm", "3ef35a1377d47c25506e07c1c005ea9d38d700699d92ee92825f024434258177"},
|
||||||
"logger_backends": {:hex, :logger_backends, "1.0.0", "09c4fad6202e08cb0fbd37f328282f16539aca380f512523ce9472b28edc6bdf", [:mix], [], "hexpm", "1faceb3e7ec3ef66a8f5746c5afd020e63996df6fd4eb8cdb789e5665ae6c9ce"},
|
"logger_backends": {:hex, :logger_backends, "1.0.0", "09c4fad6202e08cb0fbd37f328282f16539aca380f512523ce9472b28edc6bdf", [:mix], [], "hexpm", "1faceb3e7ec3ef66a8f5746c5afd020e63996df6fd4eb8cdb789e5665ae6c9ce"},
|
||||||
"mail": {:hex, :mail, "0.3.1", "cb0a14e4ed8904e4e5a08214e686ccf6f9099346885db17d8c309381f865cc5c", [:mix], [], "hexpm", "1db701e89865c1d5fa296b2b57b1cd587587cca8d8a1a22892b35ef5a8e352a6"},
|
"mail": {:hex, :mail, "0.3.1", "cb0a14e4ed8904e4e5a08214e686ccf6f9099346885db17d8c309381f865cc5c", [:mix], [], "hexpm", "1db701e89865c1d5fa296b2b57b1cd587587cca8d8a1a22892b35ef5a8e352a6"},
|
||||||
"majic": {:hex, :majic, "1.1.1", "16092a3a3376cc5e13d207e82ec06e05a5561170465e50cc11cc4df8a5747938", [:make, :mix], [{:elixir_make, "~> 0.8.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "7fbb0372f0447b3f777056177d6ab3f009742e68474f850521ff56b84bd85b96"},
|
"majic": {:hex, :majic, "1.2.0", "414b69c1460ece692fa892a0ed6669b8db6a44c42bb3071cb723854f22e7bd78", [:make, :mix], [{:elixir_make, "~> 0.8.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0bd0c65f8e4dcc595757d957577f6151b59246da9614ba87debfdc641ccc0514"},
|
||||||
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
|
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
|
||||||
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
||||||
"makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"},
|
"makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"},
|
||||||
|
|
@ -96,13 +96,13 @@
|
||||||
"oban": {:hex, :oban, "2.19.4", "045adb10db1161dceb75c254782f97cdc6596e7044af456a59decb6d06da73c1", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:igniter, "~> 0.5", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5fcc6219e6464525b808d97add17896e724131f498444a292071bf8991c99f97"},
|
"oban": {:hex, :oban, "2.19.4", "045adb10db1161dceb75c254782f97cdc6596e7044af456a59decb6d06da73c1", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:igniter, "~> 0.5", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5fcc6219e6464525b808d97add17896e724131f498444a292071bf8991c99f97"},
|
||||||
"oban_live_dashboard": {:hex, :oban_live_dashboard, "0.1.1", "8aa4ceaf381c818f7d5c8185cc59942b8ac82ef0cf559881aacf8d3f8ac7bdd3", [:mix], [{:oban, "~> 2.15", [hex: :oban, repo: "hexpm", optional: false]}, {:phoenix_live_dashboard, "~> 0.7", [hex: :phoenix_live_dashboard, repo: "hexpm", optional: false]}], "hexpm", "16dc4ce9c9a95aa2e655e35ed4e675652994a8def61731a18af85e230e1caa63"},
|
"oban_live_dashboard": {:hex, :oban_live_dashboard, "0.1.1", "8aa4ceaf381c818f7d5c8185cc59942b8ac82ef0cf559881aacf8d3f8ac7bdd3", [:mix], [{:oban, "~> 2.15", [hex: :oban, repo: "hexpm", optional: false]}, {:phoenix_live_dashboard, "~> 0.7", [hex: :phoenix_live_dashboard, repo: "hexpm", optional: false]}], "hexpm", "16dc4ce9c9a95aa2e655e35ed4e675652994a8def61731a18af85e230e1caa63"},
|
||||||
"oban_met": {:hex, :oban_met, "1.0.5", "bb633ab06448dab2ef9194f6688d33b3d07fc3f2ad793a1a08f4dfbb2cc9fe50", [:mix], [{:oban, "~> 2.19", [hex: :oban, repo: "hexpm", optional: false]}], "hexpm", "64664d50805bbfd3903aeada1f3c39634652a87844797ee400b0bcc95a28f5ea"},
|
"oban_met": {:hex, :oban_met, "1.0.5", "bb633ab06448dab2ef9194f6688d33b3d07fc3f2ad793a1a08f4dfbb2cc9fe50", [:mix], [{:oban, "~> 2.19", [hex: :oban, repo: "hexpm", optional: false]}], "hexpm", "64664d50805bbfd3903aeada1f3c39634652a87844797ee400b0bcc95a28f5ea"},
|
||||||
"oban_plugins_lazarus": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/oban_plugins_lazarus.git", "e49fc355baaf0e435208bf5f534d31e26e897711", [ref: "e49fc355baaf0e435208bf5f534d31e26e897711"]},
|
"oban_plugins_lazarus": {:hex, :oban_plugins_lazarus, "0.1.1", "a5141d569e5b9f3bec8f4a958231d2c538af097d3c1ad06274f096fc06956821", [:mix], [{:oban, "< 3.0.0", [hex: :oban, repo: "hexpm", optional: false]}], "hexpm", "7e9c51bc44d33f71c0a52cf0cd5c8d4c70380ede065c1042013f5123f2fc9729"},
|
||||||
"oban_web": {:hex, :oban_web, "2.11.6", "53933cb4253c4d9f1098ee311c06f07935259f0e564dcf2d66bae4cc98e317fe", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:oban, "~> 2.19", [hex: :oban, repo: "hexpm", optional: false]}, {:oban_met, "~> 1.0", [hex: :oban_met, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}], "hexpm", "576d94b705688c313694c2c114ca21aa0f8f2ad1b9ca45c052c5ba316d3e8d10"},
|
"oban_web": {:hex, :oban_web, "2.11.6", "53933cb4253c4d9f1098ee311c06f07935259f0e564dcf2d66bae4cc98e317fe", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:oban, "~> 2.19", [hex: :oban, repo: "hexpm", optional: false]}, {:oban_met, "~> 1.0", [hex: :oban_met, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}], "hexpm", "576d94b705688c313694c2c114ca21aa0f8f2ad1b9ca45c052c5ba316d3e8d10"},
|
||||||
"octo_fetch": {:hex, :octo_fetch, "0.4.0", "074b5ecbc08be10b05b27e9db08bc20a3060142769436242702931c418695b19", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "cf8be6f40cd519d7000bb4e84adcf661c32e59369ca2827c4e20042eda7a7fc6"},
|
"octo_fetch": {:hex, :octo_fetch, "0.4.0", "074b5ecbc08be10b05b27e9db08bc20a3060142769436242702931c418695b19", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "cf8be6f40cd519d7000bb4e84adcf661c32e59369ca2827c4e20042eda7a7fc6"},
|
||||||
"open_api_spex": {:hex, :open_api_spex, "3.22.0", "fbf90dc82681dc042a4ee79853c8e989efbba73d9e87439085daf849bbf8bc20", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0 or ~> 6.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "dd751ddbdd709bb4a5313e9a24530da6e66594773c7242a0c2592cbd9f589063"},
|
"open_api_spex": {:hex, :open_api_spex, "3.22.0", "fbf90dc82681dc042a4ee79853c8e989efbba73d9e87439085daf849bbf8bc20", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0 or ~> 6.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "dd751ddbdd709bb4a5313e9a24530da6e66594773c7242a0c2592cbd9f589063"},
|
||||||
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
|
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
|
||||||
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"},
|
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"},
|
||||||
"phoenix": {:git, "https://github.com/feld/phoenix", "fb6dc76c657422e49600896c64aab4253fceaef6", [branch: "v1.7.14-websocket-headers"]},
|
"phoenix": {:hex, :phoenix, "1.8.3", "49ac5e485083cb1495a905e47eb554277bdd9c65ccb4fc5100306b350151aa95", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "36169f95cc2e155b78be93d9590acc3f462f1e5438db06e6248613f27c80caec"},
|
||||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.6.5", "c4ef322acd15a574a8b1a08eff0ee0a85e73096b53ce1403b6563709f15e1cea", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "26ec3208eef407f31b748cadd044045c6fd485fbff168e35963d2f9dfff28d4b"},
|
"phoenix_ecto": {:hex, :phoenix_ecto, "4.6.5", "c4ef322acd15a574a8b1a08eff0ee0a85e73096b53ce1403b6563709f15e1cea", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "26ec3208eef407f31b748cadd044045c6fd485fbff168e35963d2f9dfff28d4b"},
|
||||||
"phoenix_html": {:hex, :phoenix_html, "3.3.4", "42a09fc443bbc1da37e372a5c8e6755d046f22b9b11343bf885067357da21cb3", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0249d3abec3714aff3415e7ee3d9786cb325be3151e6c4b3021502c585bf53fb"},
|
"phoenix_html": {:hex, :phoenix_html, "3.3.4", "42a09fc443bbc1da37e372a5c8e6755d046f22b9b11343bf885067357da21cb3", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0249d3abec3714aff3415e7ee3d9786cb325be3151e6c4b3021502c585bf53fb"},
|
||||||
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"},
|
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"},
|
||||||
|
|
@ -112,6 +112,7 @@
|
||||||
"phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.1", "b74ccaa8046fbc388a62134360ee7d9742d5a8ae74063f34eb050279de7a99e1", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "4000eeba3f9d7d1a6bf56d2bd56733d5cadf41a7f0d8ffe5bb67e7d667e204a2"},
|
"phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.1", "b74ccaa8046fbc388a62134360ee7d9742d5a8ae74063f34eb050279de7a99e1", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "4000eeba3f9d7d1a6bf56d2bd56733d5cadf41a7f0d8ffe5bb67e7d667e204a2"},
|
||||||
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
|
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
|
||||||
"phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"},
|
"phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"},
|
||||||
|
"pleroma_mfm_parser": {:hex, :pleroma_mfm_parser, "0.2.1", "87a40fec4e2e035447df34e1c98e1b816b6a448d49baea73eb2273531ac5ce66", [:mix], [], "hexpm", "ec4845461da95d63ab3d592086d65114c45057a1f2d48d768f3def45b0d7696f"},
|
||||||
"plug": {:hex, :plug, "1.19.1", "09bac17ae7a001a68ae393658aa23c7e38782be5c5c00c80be82901262c394c0", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "560a0017a8f6d5d30146916862aaf9300b7280063651dd7e532b8be168511e62"},
|
"plug": {:hex, :plug, "1.19.1", "09bac17ae7a001a68ae393658aa23c7e38782be5c5c00c80be82901262c394c0", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "560a0017a8f6d5d30146916862aaf9300b7280063651dd7e532b8be168511e62"},
|
||||||
"plug_cowboy": {:hex, :plug_cowboy, "2.7.4", "729c752d17cf364e2b8da5bdb34fb5804f56251e88bb602aff48ae0bd8673d11", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9b85632bd7012615bae0a5d70084deb1b25d2bcbb32cab82d1e9a1e023168aa3"},
|
"plug_cowboy": {:hex, :plug_cowboy, "2.7.4", "729c752d17cf364e2b8da5bdb34fb5804f56251e88bb602aff48ae0bd8673d11", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9b85632bd7012615bae0a5d70084deb1b25d2bcbb32cab82d1e9a1e023168aa3"},
|
||||||
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
|
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
|
||||||
|
|
@ -131,7 +132,7 @@
|
||||||
"quic": {:hex, :quic, "0.10.2", "4b390507a85f65ce47808f3df1a864e0baf9adb7a1b4ea9f4dcd66fe9d0cb166", [:rebar3], [], "hexpm", "7c196a66973c877a59768a5687f0a0610ff11817254d0a4e45cc4e3a16b1d00b"},
|
"quic": {:hex, :quic, "0.10.2", "4b390507a85f65ce47808f3df1a864e0baf9adb7a1b4ea9f4dcd66fe9d0cb166", [:rebar3], [], "hexpm", "7c196a66973c877a59768a5687f0a0610ff11817254d0a4e45cc4e3a16b1d00b"},
|
||||||
"ranch": {:hex, :ranch, "2.2.0", "25528f82bc8d7c6152c57666ca99ec716510fe0925cb188172f41ce93117b1b0", [:make, :rebar3], [], "hexpm", "fa0b99a1780c80218a4197a59ea8d3bdae32fbff7e88527d7d8a4787eff4f8e7"},
|
"ranch": {:hex, :ranch, "2.2.0", "25528f82bc8d7c6152c57666ca99ec716510fe0925cb188172f41ce93117b1b0", [:make, :rebar3], [], "hexpm", "fa0b99a1780c80218a4197a59ea8d3bdae32fbff7e88527d7d8a4787eff4f8e7"},
|
||||||
"recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"},
|
"recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"},
|
||||||
"remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]},
|
"remote_ip": {:hex, :remote_ip, "1.2.0", "fb078e12a44414f4cef5a75963c33008fe169b806572ccd17257c208a7bc760f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2ff91de19c48149ce19ed230a81d377186e4412552a597d6a5137373e5877cb7"},
|
||||||
"rustler": {:hex, :rustler, "0.30.0", "cefc49922132b072853fa9b0ca4dc2ffcb452f68fb73b779042b02d545e097fb", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "9ef1abb6a7dda35c47cfc649e6a5a61663af6cf842a55814a554a84607dee389"},
|
"rustler": {:hex, :rustler, "0.30.0", "cefc49922132b072853fa9b0ca4dc2ffcb452f68fb73b779042b02d545e097fb", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "9ef1abb6a7dda35c47cfc649e6a5a61663af6cf842a55814a554a84607dee389"},
|
||||||
"sleeplocks": {:hex, :sleeplocks, "1.1.3", "96a86460cc33b435c7310dbd27ec82ca2c1f24ae38e34f8edde97f756503441a", [:rebar3], [], "hexpm", "d3b3958552e6eb16f463921e70ae7c767519ef8f5be46d7696cc1ed649421321"},
|
"sleeplocks": {:hex, :sleeplocks, "1.1.3", "96a86460cc33b435c7310dbd27ec82ca2c1f24ae38e34f8edde97f756503441a", [:rebar3], [], "hexpm", "d3b3958552e6eb16f463921e70ae7c767519ef8f5be46d7696cc1ed649421321"},
|
||||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
|
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2022-07-22 02:09+0300\n"
|
"POT-Creation-Date: 2022-07-22 02:09+0300\n"
|
||||||
"PO-Revision-Date: 2024-10-13 21:03+0000\n"
|
"PO-Revision-Date: 2025-12-27 07:08+0000\n"
|
||||||
"Last-Translator: Codimp <contact@lithio.fr>\n"
|
"Last-Translator: Codimp <contact@lithio.fr>\n"
|
||||||
"Language-Team: French <https://translate.pleroma.social/projects/pleroma/"
|
"Language-Team: French <https://translate.pleroma.social/projects/pleroma/"
|
||||||
"pleroma-backend-domain-config_descriptions/fr/>\n"
|
"pleroma-backend-domain-config_descriptions/fr/>\n"
|
||||||
|
|
@ -402,7 +402,7 @@ msgstr ""
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-:shout"
|
msgctxt "config description at :pleroma-:shout"
|
||||||
msgid "Pleroma shout settings"
|
msgid "Pleroma shout settings"
|
||||||
msgstr ""
|
msgstr "Paramètre de Pleroma shout"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
|
|
@ -416,7 +416,7 @@ msgstr ""
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-:streamer"
|
msgctxt "config description at :pleroma-:streamer"
|
||||||
msgid "Settings for notifications streamer"
|
msgid "Settings for notifications streamer"
|
||||||
msgstr ""
|
msgstr "Paramètres des streamers de notifications"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
|
|
@ -429,12 +429,14 @@ msgstr "Paramètres liés au schémas d'URI"
|
||||||
msgctxt "config description at :pleroma-:web_cache_ttl"
|
msgctxt "config description at :pleroma-:web_cache_ttl"
|
||||||
msgid "The expiration time for the web responses cache. Values should be in milliseconds or `nil` to disable expiration."
|
msgid "The expiration time for the web responses cache. Values should be in milliseconds or `nil` to disable expiration."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Temps d'expiration pour le cache des réponses web. Les valeurs doivent être "
|
||||||
|
"en millisecondes ou `nil`pour désactiver l'expiration."
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-:welcome"
|
msgctxt "config description at :pleroma-:welcome"
|
||||||
msgid "Welcome messages settings"
|
msgid "Welcome messages settings"
|
||||||
msgstr ""
|
msgstr "Paramètres de messages de bienvenue"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
|
|
@ -458,133 +460,146 @@ msgstr ""
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Captcha"
|
msgctxt "config description at :pleroma-Pleroma.Captcha"
|
||||||
msgid "Captcha-related settings"
|
msgid "Captcha-related settings"
|
||||||
msgstr ""
|
msgstr "Paramètres liés au Captcha"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Captcha.Kocaptcha"
|
msgctxt "config description at :pleroma-Pleroma.Captcha.Kocaptcha"
|
||||||
msgid "Kocaptcha is a very simple captcha service with a single API endpoint, the source code is here: https://github.com/koto-bank/kocaptcha. The default endpoint (https://captcha.kotobank.ch) is hosted by the developer."
|
msgid "Kocaptcha is a very simple captcha service with a single API endpoint, the source code is here: https://github.com/koto-bank/kocaptcha. The default endpoint (https://captcha.kotobank.ch) is hosted by the developer."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Kocaptcha est un service de captcha très simple avec une API n'utilisant "
|
||||||
|
"qu'une seule ressource, le code source est ici : https://github.com/"
|
||||||
|
"koto-bank/kocaptcha. La ressource par défaut (https://captcha.kotobank.ch) "
|
||||||
|
"est hébergée par son développeur."
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Emails.Mailer"
|
msgctxt "config description at :pleroma-Pleroma.Emails.Mailer"
|
||||||
msgid "Mailer-related settings"
|
msgid "Mailer-related settings"
|
||||||
msgstr ""
|
msgstr "Paramètres liés à l'envoyeur d'email"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Emails.NewUsersDigestEmail"
|
msgctxt "config description at :pleroma-Pleroma.Emails.NewUsersDigestEmail"
|
||||||
msgid "New users admin email digest"
|
msgid "New users admin email digest"
|
||||||
msgstr ""
|
msgstr "Email à l'administrateur de résumé des nouveaux utilisateurs"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Emails.UserEmail"
|
msgctxt "config description at :pleroma-Pleroma.Emails.UserEmail"
|
||||||
msgid "Email template settings"
|
msgid "Email template settings"
|
||||||
msgstr ""
|
msgstr "Paramètres de template d'email"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Formatter"
|
msgctxt "config description at :pleroma-Pleroma.Formatter"
|
||||||
msgid "Configuration for Pleroma's link formatter which parses mentions, hashtags, and URLs."
|
msgid "Configuration for Pleroma's link formatter which parses mentions, hashtags, and URLs."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Configuration du formateur de lien Pleroma qui interprète les mentions, "
|
||||||
|
"hashtags et URLs."
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.ScheduledActivity"
|
msgctxt "config description at :pleroma-Pleroma.ScheduledActivity"
|
||||||
msgid "Scheduled activities settings"
|
msgid "Scheduled activities settings"
|
||||||
msgstr ""
|
msgstr "Paramètres des activités planifiées"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Upload"
|
msgctxt "config description at :pleroma-Pleroma.Upload"
|
||||||
msgid "Upload general settings"
|
msgid "Upload general settings"
|
||||||
msgstr ""
|
msgstr "Paramètres généraux d'upload"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Upload.Filter.AnonymizeFilename"
|
msgctxt "config description at :pleroma-Pleroma.Upload.Filter.AnonymizeFilename"
|
||||||
msgid "Filter replaces the filename of the upload"
|
msgid "Filter replaces the filename of the upload"
|
||||||
msgstr ""
|
msgstr "Filtre de remplacement du nom de fichier de l'upload"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Upload.Filter.Mogrify"
|
msgctxt "config description at :pleroma-Pleroma.Upload.Filter.Mogrify"
|
||||||
msgid "Uploads mogrify filter settings"
|
msgid "Uploads mogrify filter settings"
|
||||||
msgstr ""
|
msgstr "Paramètres de filtre mogrify pour les uploads"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Uploaders.Local"
|
msgctxt "config description at :pleroma-Pleroma.Uploaders.Local"
|
||||||
msgid "Local uploader-related settings"
|
msgid "Local uploader-related settings"
|
||||||
msgstr ""
|
msgstr "Paramètres liés aux uploads locaux"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Uploaders.S3"
|
msgctxt "config description at :pleroma-Pleroma.Uploaders.S3"
|
||||||
msgid "S3 uploader-related settings"
|
msgid "S3 uploader-related settings"
|
||||||
msgstr ""
|
msgstr "Paramètres liés à l'uploader S3"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.User.Backup"
|
msgctxt "config description at :pleroma-Pleroma.User.Backup"
|
||||||
msgid "Account Backup"
|
msgid "Account Backup"
|
||||||
msgstr ""
|
msgstr "Sauvegarde de Comptes"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Web.MediaProxy.Invalidation.Http"
|
msgctxt "config description at :pleroma-Pleroma.Web.MediaProxy.Invalidation.Http"
|
||||||
msgid "HTTP invalidate settings"
|
msgid "HTTP invalidate settings"
|
||||||
msgstr ""
|
msgstr "Paramètres d'invalidation HTTP"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Web.MediaProxy.Invalidation.Script"
|
msgctxt "config description at :pleroma-Pleroma.Web.MediaProxy.Invalidation.Script"
|
||||||
msgid "Invalidation script settings"
|
msgid "Invalidation script settings"
|
||||||
msgstr ""
|
msgstr "Paramètres du script d'invalidation"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Web.Metadata"
|
msgctxt "config description at :pleroma-Pleroma.Web.Metadata"
|
||||||
msgid "Metadata-related settings"
|
msgid "Metadata-related settings"
|
||||||
msgstr ""
|
msgstr "Paramètres liés aux métadonnées"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Web.Plugs.RemoteIp"
|
msgctxt "config description at :pleroma-Pleroma.Web.Plugs.RemoteIp"
|
||||||
msgid "`Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.\n**If your instance is not behind at least one reverse proxy, you should not enable this plug.**\n"
|
msgid "`Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.\n**If your instance is not behind at least one reverse proxy, you should not enable this plug.**\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"`Pleroma.Web.Plugs.RemoteIp` est un shim pour invoquer "
|
||||||
|
"[`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) mais avec une "
|
||||||
|
"configuration au runtime.\n"
|
||||||
|
"**Si votre instance n'est pas derrière au moins un proxy inverse, vous ne "
|
||||||
|
"devriez pas activer ce plug.**\n"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Web.Preload"
|
msgctxt "config description at :pleroma-Pleroma.Web.Preload"
|
||||||
msgid "Preload-related settings"
|
msgid "Preload-related settings"
|
||||||
msgstr ""
|
msgstr "Paramètres liés au préchargement"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :pleroma-Pleroma.Workers.PurgeExpiredActivity"
|
msgctxt "config description at :pleroma-Pleroma.Workers.PurgeExpiredActivity"
|
||||||
msgid "Expired activities settings"
|
msgid "Expired activities settings"
|
||||||
msgstr ""
|
msgstr "Paramètres des activités expirées"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :prometheus-Pleroma.Web.Endpoint.MetricsExporter"
|
msgctxt "config description at :prometheus-Pleroma.Web.Endpoint.MetricsExporter"
|
||||||
msgid "Prometheus app metrics endpoint configuration"
|
msgid "Prometheus app metrics endpoint configuration"
|
||||||
msgstr ""
|
msgstr "Configuration des endpoints de métriques Prometheus"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config description at :web_push_encryption-:vapid_details"
|
msgctxt "config description at :web_push_encryption-:vapid_details"
|
||||||
msgid "Web Push Notifications configuration. You can use the mix task mix web_push.gen.keypair to generate it."
|
msgid "Web Push Notifications configuration. You can use the mix task mix web_push.gen.keypair to generate it."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Configuration des notifications Web Push. Vous pouvez utiliser la tâche mix "
|
||||||
|
"web_push.gen.keypair pour le générer."
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :ex_aws-:s3"
|
msgctxt "config label at :ex_aws-:s3"
|
||||||
msgid "S3"
|
msgid "S3"
|
||||||
msgstr ""
|
msgstr "S3"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
|
|
@ -596,13 +611,13 @@ msgstr ""
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :logger-:ex_syslogger"
|
msgctxt "config label at :logger-:ex_syslogger"
|
||||||
msgid "ExSyslogger"
|
msgid "ExSyslogger"
|
||||||
msgstr ""
|
msgstr "ExSyslogger"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:activitypub"
|
msgctxt "config label at :pleroma-:activitypub"
|
||||||
msgid "ActivityPub"
|
msgid "ActivityPub"
|
||||||
msgstr ""
|
msgstr "ActivityPub"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
|
|
@ -620,151 +635,151 @@ msgstr ""
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:connections_pool"
|
msgctxt "config label at :pleroma-:connections_pool"
|
||||||
msgid "Connections pool"
|
msgid "Connections pool"
|
||||||
msgstr ""
|
msgstr "Pool de connexions"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:email_notifications"
|
msgctxt "config label at :pleroma-:email_notifications"
|
||||||
msgid "Email notifications"
|
msgid "Email notifications"
|
||||||
msgstr ""
|
msgstr "Notifications email"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:emoji"
|
msgctxt "config label at :pleroma-:emoji"
|
||||||
msgid "Emoji"
|
msgid "Emoji"
|
||||||
msgstr ""
|
msgstr "Emoji"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:features"
|
msgctxt "config label at :pleroma-:features"
|
||||||
msgid "Features"
|
msgid "Features"
|
||||||
msgstr ""
|
msgstr "Fonctionnalités"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:feed"
|
msgctxt "config label at :pleroma-:feed"
|
||||||
msgid "Feed"
|
msgid "Feed"
|
||||||
msgstr ""
|
msgstr "Flux"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:frontend_configurations"
|
msgctxt "config label at :pleroma-:frontend_configurations"
|
||||||
msgid "Frontend configurations"
|
msgid "Frontend configurations"
|
||||||
msgstr ""
|
msgstr "Configurations des frontends"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:frontends"
|
msgctxt "config label at :pleroma-:frontends"
|
||||||
msgid "Frontends"
|
msgid "Frontends"
|
||||||
msgstr ""
|
msgstr "Frontends"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:gopher"
|
msgctxt "config label at :pleroma-:gopher"
|
||||||
msgid "Gopher"
|
msgid "Gopher"
|
||||||
msgstr ""
|
msgstr "Gopher"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:hackney_pools"
|
msgctxt "config label at :pleroma-:hackney_pools"
|
||||||
msgid "Hackney pools"
|
msgid "Hackney pools"
|
||||||
msgstr ""
|
msgstr "Bacs (pools) d'Hackney"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:http"
|
msgctxt "config label at :pleroma-:http"
|
||||||
msgid "HTTP"
|
msgid "HTTP"
|
||||||
msgstr ""
|
msgstr "HTTP"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:http_security"
|
msgctxt "config label at :pleroma-:http_security"
|
||||||
msgid "HTTP security"
|
msgid "HTTP security"
|
||||||
msgstr ""
|
msgstr "Sécurité HTTP"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:instance"
|
msgctxt "config label at :pleroma-:instance"
|
||||||
msgid "Instance"
|
msgid "Instance"
|
||||||
msgstr ""
|
msgstr "Instance"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:instances_favicons"
|
msgctxt "config label at :pleroma-:instances_favicons"
|
||||||
msgid "Instances favicons"
|
msgid "Instances favicons"
|
||||||
msgstr ""
|
msgstr "Favicons des instances"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:ldap"
|
msgctxt "config label at :pleroma-:ldap"
|
||||||
msgid "LDAP"
|
msgid "LDAP"
|
||||||
msgstr ""
|
msgstr "LDAP"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:majic_pool"
|
msgctxt "config label at :pleroma-:majic_pool"
|
||||||
msgid "Majic pool"
|
msgid "Majic pool"
|
||||||
msgstr ""
|
msgstr "Bac (pool) de Majic"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:manifest"
|
msgctxt "config label at :pleroma-:manifest"
|
||||||
msgid "Manifest"
|
msgid "Manifest"
|
||||||
msgstr ""
|
msgstr "Manifeste"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:markup"
|
msgctxt "config label at :pleroma-:markup"
|
||||||
msgid "Markup Settings"
|
msgid "Markup Settings"
|
||||||
msgstr ""
|
msgstr "Paramètres des Balises"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:media_preview_proxy"
|
msgctxt "config label at :pleroma-:media_preview_proxy"
|
||||||
msgid "Media preview proxy"
|
msgid "Media preview proxy"
|
||||||
msgstr ""
|
msgstr "Proxy de prévisualisation média"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:media_proxy"
|
msgctxt "config label at :pleroma-:media_proxy"
|
||||||
msgid "Media proxy"
|
msgid "Media proxy"
|
||||||
msgstr ""
|
msgstr "Proxy média"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:modules"
|
msgctxt "config label at :pleroma-:modules"
|
||||||
msgid "Modules"
|
msgid "Modules"
|
||||||
msgstr ""
|
msgstr "Modules"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:mrf"
|
msgctxt "config label at :pleroma-:mrf"
|
||||||
msgid "MRF"
|
msgid "MRF"
|
||||||
msgstr ""
|
msgstr "MRF"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:mrf_activity_expiration"
|
msgctxt "config label at :pleroma-:mrf_activity_expiration"
|
||||||
msgid "MRF Activity Expiration Policy"
|
msgid "MRF Activity Expiration Policy"
|
||||||
msgstr ""
|
msgstr "Politique MRF d'Expiration des Activités"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:mrf_follow_bot"
|
msgctxt "config label at :pleroma-:mrf_follow_bot"
|
||||||
msgid "MRF FollowBot Policy"
|
msgid "MRF FollowBot Policy"
|
||||||
msgstr ""
|
msgstr "Politique MRF FollowBot"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:mrf_hashtag"
|
msgctxt "config label at :pleroma-:mrf_hashtag"
|
||||||
msgid "MRF Hashtag"
|
msgid "MRF Hashtag"
|
||||||
msgstr ""
|
msgstr "Politique MRF hashtag"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgctxt "config label at :pleroma-:mrf_hellthread"
|
msgctxt "config label at :pleroma-:mrf_hellthread"
|
||||||
msgid "MRF Hellthread"
|
msgid "MRF Hellthread"
|
||||||
msgstr ""
|
msgstr "MRF Hellthread"
|
||||||
|
|
||||||
#: lib/pleroma/docs/translator.ex:5
|
#: lib/pleroma/docs/translator.ex:5
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,15 @@
|
||||||
## to merge POT files into PO files.
|
## to merge POT files into PO files.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"PO-Revision-Date: 2020-05-12 15:52+0000\n"
|
"PO-Revision-Date: 2025-12-27 07:08+0000\n"
|
||||||
"Last-Translator: Haelwenn (lanodan) Monnier "
|
"Last-Translator: Codimp <contact@lithio.fr>\n"
|
||||||
"<contact+translate.pleroma.social@hacktivis.me>\n"
|
|
||||||
"Language-Team: French <https://translate.pleroma.social/projects/pleroma/"
|
"Language-Team: French <https://translate.pleroma.social/projects/pleroma/"
|
||||||
"pleroma/fr/>\n"
|
"pleroma-backend-domain-errors/fr/>\n"
|
||||||
"Language: fr\n"
|
"Language: fr\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
|
||||||
"X-Generator: Weblate 4.0.4\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
||||||
|
"X-Generator: Weblate 4.13.1\n"
|
||||||
|
|
||||||
msgid "can't be blank"
|
msgid "can't be blank"
|
||||||
msgstr "ne peut être vide"
|
msgstr "ne peut être vide"
|
||||||
|
|
@ -357,7 +356,7 @@ msgstr "Ne peut poster dans la boite d'émission de %{nickname} en tant que %{as
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:335
|
#: lib/pleroma/web/common_api/common_api.ex:335
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "conversation is already muted"
|
msgid "conversation is already muted"
|
||||||
msgstr "la conversation est déjà baillonée"
|
msgstr "la conversation est déjà silenciée"
|
||||||
|
|
||||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:192
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:192
|
||||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:317
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:317
|
||||||
|
|
@ -421,7 +420,7 @@ msgstr "Erreur interne"
|
||||||
#: lib/pleroma/web/oauth/fallback_controller.ex:29
|
#: lib/pleroma/web/oauth/fallback_controller.ex:29
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Invalid Username/Password"
|
msgid "Invalid Username/Password"
|
||||||
msgstr "Nom d'utilisateur/mot de passe invalide"
|
msgstr "Nom d'utilisateur·ice/mot de passe invalide"
|
||||||
|
|
||||||
#: lib/pleroma/captcha/captcha.ex:107
|
#: lib/pleroma/captcha/captcha.ex:107
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,16 @@ msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-08-15 15:40+0000\n"
|
"POT-Creation-Date: 2021-08-15 15:40+0000\n"
|
||||||
"PO-Revision-Date: 2021-09-03 06:45+0000\n"
|
"PO-Revision-Date: 2025-07-31 05:58+0000\n"
|
||||||
"Last-Translator: @liimee <alt3753.7@gmail.com>\n"
|
"Last-Translator: Neko Nekowazarashi <kodra@nekoweb.my.id>\n"
|
||||||
"Language-Team: Indonesian <https://translate.pleroma.social/projects/pleroma/"
|
"Language-Team: Indonesian <https://translate.pleroma.social/projects/pleroma/"
|
||||||
"pleroma/id/>\n"
|
"pleroma-backend-domain-errors/id/>\n"
|
||||||
"Language: id\n"
|
"Language: id\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
"X-Generator: Weblate 4.6.2\n"
|
"X-Generator: Weblate 4.13.1\n"
|
||||||
|
|
||||||
## This file is a PO Template file.
|
## This file is a PO Template file.
|
||||||
##
|
##
|
||||||
|
|
@ -41,7 +41,7 @@ msgstr "memiliki format yang tidak valid"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_subset/3
|
## From Ecto.Changeset.validate_subset/3
|
||||||
msgid "has an invalid entry"
|
msgid "has an invalid entry"
|
||||||
msgstr ""
|
msgstr "ada yang tidak valid"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_exclusion/3
|
## From Ecto.Changeset.validate_exclusion/3
|
||||||
msgid "is reserved"
|
msgid "is reserved"
|
||||||
|
|
@ -49,7 +49,7 @@ msgstr ""
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_confirmation/3
|
## From Ecto.Changeset.validate_confirmation/3
|
||||||
msgid "does not match confirmation"
|
msgid "does not match confirmation"
|
||||||
msgstr ""
|
msgstr "tidak sama dengan konfirmasi"
|
||||||
|
|
||||||
## From Ecto.Changeset.no_assoc_constraint/3
|
## From Ecto.Changeset.no_assoc_constraint/3
|
||||||
msgid "is still associated with this entry"
|
msgid "is still associated with this entry"
|
||||||
|
|
@ -61,19 +61,19 @@ msgstr ""
|
||||||
## From Ecto.Changeset.validate_length/3
|
## From Ecto.Changeset.validate_length/3
|
||||||
msgid "should be %{count} character(s)"
|
msgid "should be %{count} character(s)"
|
||||||
msgid_plural "should be %{count} character(s)"
|
msgid_plural "should be %{count} character(s)"
|
||||||
msgstr[0] "harus memiliki %{count} karakter"
|
msgstr[0] "harus ada %{count} karakter"
|
||||||
|
|
||||||
msgid "should have %{count} item(s)"
|
msgid "should have %{count} item(s)"
|
||||||
msgid_plural "should have %{count} item(s)"
|
msgid_plural "should have %{count} item(s)"
|
||||||
msgstr[0] "harus memiliki %{count} item"
|
msgstr[0] "harus ada %{count} item"
|
||||||
|
|
||||||
msgid "should be at least %{count} character(s)"
|
msgid "should be at least %{count} character(s)"
|
||||||
msgid_plural "should be at least %{count} character(s)"
|
msgid_plural "should be at least %{count} character(s)"
|
||||||
msgstr[0] "harus memiliki sekurang-kurangnya %{count} karakter"
|
msgstr[0] "harus ada sekurang-kurangnya %{count} karakter"
|
||||||
|
|
||||||
msgid "should have at least %{count} item(s)"
|
msgid "should have at least %{count} item(s)"
|
||||||
msgid_plural "should have at least %{count} item(s)"
|
msgid_plural "should have at least %{count} item(s)"
|
||||||
msgstr[0] "harus memiliki sekurang-kurangnya %{count} item"
|
msgstr[0] "harus ada sekurang-kurangnya %{count} item"
|
||||||
|
|
||||||
msgid "should be at most %{count} character(s)"
|
msgid "should be at most %{count} character(s)"
|
||||||
msgid_plural "should be at most %{count} character(s)"
|
msgid_plural "should be at most %{count} character(s)"
|
||||||
|
|
@ -112,7 +112,7 @@ msgstr "Sudah memilih"
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:359
|
#: lib/pleroma/web/oauth/oauth_controller.ex:359
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Bad request"
|
msgid "Bad request"
|
||||||
msgstr "Permintaan buruk (bad request)"
|
msgstr "Permintaan buruk"
|
||||||
|
|
||||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:426
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:426
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
@ -133,7 +133,7 @@ msgstr "Tidak dapat mencari pengguna"
|
||||||
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:61
|
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:61
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Can't get favorites"
|
msgid "Can't get favorites"
|
||||||
msgstr "Tidak dapat mendapatkan favorit"
|
msgstr "Tidak dapat mengambil favorit"
|
||||||
|
|
||||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:438
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:438
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
@ -143,7 +143,7 @@ msgstr "Tidak dapat menyukai objek"
|
||||||
#: lib/pleroma/web/common_api/utils.ex:563
|
#: lib/pleroma/web/common_api/utils.ex:563
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Cannot post an empty status without attachments"
|
msgid "Cannot post an empty status without attachments"
|
||||||
msgstr "Tidak dapat memposting status kosong tanpa lampiran"
|
msgstr "Tidak dapat mempos status kosong tanpa lampiran"
|
||||||
|
|
||||||
#: lib/pleroma/web/common_api/utils.ex:511
|
#: lib/pleroma/web/common_api/utils.ex:511
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
@ -206,7 +206,7 @@ msgstr "CAPTCHA tidak valid"
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:568
|
#: lib/pleroma/web/oauth/oauth_controller.ex:568
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Invalid credentials"
|
msgid "Invalid credentials"
|
||||||
msgstr "Kredensian tidak valid"
|
msgstr "Kredensial tidak valid"
|
||||||
|
|
||||||
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:38
|
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:38
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
@ -279,12 +279,12 @@ msgstr ""
|
||||||
#: lib/pleroma/web/ostatus/ostatus_controller.ex:149
|
#: lib/pleroma/web/ostatus/ostatus_controller.ex:149
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Something went wrong"
|
msgid "Something went wrong"
|
||||||
msgstr "Sesuatu yang salah terjadi"
|
msgstr "Ada sesuatu yang salah"
|
||||||
|
|
||||||
#: lib/pleroma/web/common_api/activity_draft.ex:107
|
#: lib/pleroma/web/common_api/activity_draft.ex:107
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "The message visibility must be direct"
|
msgid "The message visibility must be direct"
|
||||||
msgstr "Visibilitas pesan harus langsung"
|
msgstr "Ketampakan pesan harus langsung"
|
||||||
|
|
||||||
#: lib/pleroma/web/common_api/utils.ex:573
|
#: lib/pleroma/web/common_api/utils.ex:573
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
@ -294,7 +294,7 @@ msgstr "Status lebih dari batas karakter"
|
||||||
#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31
|
#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "This resource requires authentication."
|
msgid "This resource requires authentication."
|
||||||
msgstr ""
|
msgstr "Autentikasi diperlukan untuk hal ini."
|
||||||
|
|
||||||
#: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206
|
#: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
@ -314,7 +314,7 @@ msgstr ""
|
||||||
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:485
|
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:485
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "You can't revoke your own admin status."
|
msgid "You can't revoke your own admin status."
|
||||||
msgstr ""
|
msgstr "Anda tidak dapat mencabut status admin Anda."
|
||||||
|
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:221
|
#: lib/pleroma/web/oauth/oauth_controller.ex:221
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:308
|
#: lib/pleroma/web/oauth/oauth_controller.ex:308
|
||||||
|
|
@ -382,7 +382,7 @@ msgstr "Gagal"
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:410
|
#: lib/pleroma/web/oauth/oauth_controller.ex:410
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Failed to authenticate: %{message}."
|
msgid "Failed to authenticate: %{message}."
|
||||||
msgstr "Gagal mengotentikasi: %{message}."
|
msgstr "Gagal mengautentikasi: %{message}."
|
||||||
|
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:441
|
#: lib/pleroma/web/oauth/oauth_controller.ex:441
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
@ -392,7 +392,7 @@ msgstr "Gagal menyiapkan akun pengguna."
|
||||||
#: lib/pleroma/plugs/oauth_scopes_plug.ex:38
|
#: lib/pleroma/plugs/oauth_scopes_plug.ex:38
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Insufficient permissions: %{permissions}."
|
msgid "Insufficient permissions: %{permissions}."
|
||||||
msgstr ""
|
msgstr "Izin tidak cukup: %{permissions}."
|
||||||
|
|
||||||
#: lib/pleroma/plugs/uploaded_media.ex:104
|
#: lib/pleroma/plugs/uploaded_media.ex:104
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
@ -418,12 +418,12 @@ msgstr ""
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:172
|
#: lib/pleroma/web/oauth/oauth_controller.ex:172
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "This action is outside the authorized scopes"
|
msgid "This action is outside the authorized scopes"
|
||||||
msgstr ""
|
msgstr "Tindakan ini diluar jangkauan yang terotorisasi"
|
||||||
|
|
||||||
#: lib/pleroma/web/oauth/fallback_controller.ex:14
|
#: lib/pleroma/web/oauth/fallback_controller.ex:14
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Unknown error, please check the details and try again."
|
msgid "Unknown error, please check the details and try again."
|
||||||
msgstr "Kesalahan tidak dikenal, harap periksa keterangannya dan coba lagi."
|
msgstr "Kesalahan tidak dikenal, harap periksa detailnya dan coba lagi."
|
||||||
|
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:119
|
#: lib/pleroma/web/oauth/oauth_controller.ex:119
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:158
|
#: lib/pleroma/web/oauth/oauth_controller.ex:158
|
||||||
|
|
@ -444,7 +444,7 @@ msgstr ""
|
||||||
#: lib/pleroma/web/uploader_controller.ex:23
|
#: lib/pleroma/web/uploader_controller.ex:23
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "bad request"
|
msgid "bad request"
|
||||||
msgstr "permintaan buruk (bad request)"
|
msgstr "permintaan buruk"
|
||||||
|
|
||||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:103
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:103
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
@ -469,7 +469,7 @@ msgstr "CAPTCHA Tidak Valid (Parameter kurang: %{name})"
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:92
|
#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:92
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "List not found"
|
msgid "List not found"
|
||||||
msgstr ""
|
msgstr "Daftar tidak ditemukan"
|
||||||
|
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:123
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:123
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
@ -480,7 +480,7 @@ msgstr "Parameter kurang: %{name}"
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:321
|
#: lib/pleroma/web/oauth/oauth_controller.ex:321
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Password reset is required"
|
msgid "Password reset is required"
|
||||||
msgstr ""
|
msgstr "Diperlukan atur ulang kata sandi"
|
||||||
|
|
||||||
#: lib/pleroma/tests/auth_test_controller.ex:9
|
#: lib/pleroma/tests/auth_test_controller.ex:9
|
||||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:6
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:6
|
||||||
|
|
@ -522,7 +522,7 @@ msgstr ""
|
||||||
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:28
|
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:28
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Two-factor authentication enabled, you must use a access token."
|
msgid "Two-factor authentication enabled, you must use a access token."
|
||||||
msgstr "Otentikasi dua-faktor diaktifkan, Anda harus menggunakan token akses."
|
msgstr "Autentikasi dua langkah diaktifkan, Anda harus menggunakan token akses."
|
||||||
|
|
||||||
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:210
|
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:210
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
@ -552,7 +552,7 @@ msgstr ""
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Web push subscription is disabled on this Pleroma instance"
|
msgid "Web push subscription is disabled on this Pleroma instance"
|
||||||
msgstr ""
|
msgstr "Langganan push web dinonaktifkan untuk peladen Pleroma ini"
|
||||||
|
|
||||||
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:451
|
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:451
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
@ -562,7 +562,7 @@ msgstr "Anda tidak bisa mencabut status admin/moderator Anda sendiri."
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:126
|
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:126
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "authorization required for timeline view"
|
msgid "authorization required for timeline view"
|
||||||
msgstr ""
|
msgstr "Otorisasi diperlukan untuk tampilan linimasa"
|
||||||
|
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:24
|
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:24
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
@ -572,7 +572,7 @@ msgstr "Akses ditolak"
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:282
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:282
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "This API requires an authenticated user"
|
msgid "This API requires an authenticated user"
|
||||||
msgstr ""
|
msgstr "API ini memerlukan pengguna terautentikasi"
|
||||||
|
|
||||||
#: lib/pleroma/plugs/user_is_admin_plug.ex:21
|
#: lib/pleroma/plugs/user_is_admin_plug.ex:21
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
|
||||||
163
priv/gettext/id/LC_MESSAGES/posix_errors.po
Normal file
163
priv/gettext/id/LC_MESSAGES/posix_errors.po
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-08-11 18:40+0300\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: Automatically generated\n"
|
||||||
|
"Language-Team: none\n"
|
||||||
|
"Language: id\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Generator: Translate Toolkit 3.7.2\n"
|
||||||
|
|
||||||
|
## This file is a PO Template file.
|
||||||
|
##
|
||||||
|
## `msgid`s here are often extracted from source code.
|
||||||
|
## Add new translations manually only if they're dynamic
|
||||||
|
## translations that can't be statically extracted.
|
||||||
|
##
|
||||||
|
## Run `mix gettext.extract` to bring this file up to
|
||||||
|
## date. Leave `msgstr`s empty as changing them here as no
|
||||||
|
## effect: edit them in PO (`.po`) files instead.
|
||||||
|
msgid "eperm"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "eacces"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "eagain"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ebadf"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ebadmsg"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ebusy"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "edeadlk"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "edeadlock"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "edquot"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "eexist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "efault"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "efbig"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "eftype"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "eintr"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "einval"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "eio"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "eisdir"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "eloop"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "emfile"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "emlink"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "emultihop"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enametoolong"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enfile"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enobufs"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enodev"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enolck"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enolink"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enoent"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enomem"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enospc"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enosr"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enostr"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enosys"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enotblk"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enotdir"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enotsup"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "enxio"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "eopnotsupp"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "eoverflow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "epipe"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "erange"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "erofs"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "espipe"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "esrch"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "estale"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "etxtbsy"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "exdev"
|
||||||
|
msgstr ""
|
||||||
574
priv/gettext/id/LC_MESSAGES/static_pages.po
Normal file
574
priv/gettext/id/LC_MESSAGES/static_pages.po
Normal file
|
|
@ -0,0 +1,574 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-08-11 18:40+0300\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: Automatically generated\n"
|
||||||
|
"Language-Team: none\n"
|
||||||
|
"Language: id\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Generator: Translate Toolkit 3.7.2\n"
|
||||||
|
|
||||||
|
## This file is a PO Template file.
|
||||||
|
##
|
||||||
|
## "msgid"s here are often extracted from source code.
|
||||||
|
## Add new translations manually only if they're dynamic
|
||||||
|
## translations that can't be statically extracted.
|
||||||
|
##
|
||||||
|
## Run "mix gettext.extract" to bring this file up to
|
||||||
|
## date. Leave "msgstr"s empty as changing them here as no
|
||||||
|
## effect: edit them in PO (.po) files instead.
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:9
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow authorization button"
|
||||||
|
msgid "Authorize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:2
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow error"
|
||||||
|
msgid "Error fetching user"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:4
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow header"
|
||||||
|
msgid "Remote follow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:8
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "placeholder text for auth code entry"
|
||||||
|
msgid "Authentication code"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:10
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "placeholder text for password entry"
|
||||||
|
msgid "Password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:8
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "placeholder text for username entry"
|
||||||
|
msgid "Username"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:13
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow authorization button for login"
|
||||||
|
msgid "Authorize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:12
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow authorization button for mfa"
|
||||||
|
msgid "Authorize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex:2
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow error"
|
||||||
|
msgid "Error following account"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:4
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow header, need login"
|
||||||
|
msgid "Log in to follow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:4
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow mfa header"
|
||||||
|
msgid "Two-factor authentication"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex:4
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow success"
|
||||||
|
msgid "Account followed!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:7
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:7
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "placeholder text for account id"
|
||||||
|
msgid "Your account ID, e.g. lain@quitter.se"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:8
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow authorization button for following with a remote account"
|
||||||
|
msgid "Follow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:2
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow error"
|
||||||
|
msgid "Error: %{error}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:4
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow header"
|
||||||
|
msgid "Remotely follow %{nickname}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:12
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "password reset button"
|
||||||
|
msgid "Reset"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex:4
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "password reset failed homepage link"
|
||||||
|
msgid "Homepage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex:1
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "password reset failed message"
|
||||||
|
msgid "Password reset failed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:8
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "password reset form confirm password prompt"
|
||||||
|
msgid "Confirmation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:4
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "password reset form password prompt"
|
||||||
|
msgid "Password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex:1
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "password reset invalid token message"
|
||||||
|
msgid "Invalid Token"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex:2
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "password reset successful homepage link"
|
||||||
|
msgid "Homepage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex:1
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "password reset successful message"
|
||||||
|
msgid "Password changed!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/feed/feed/tag.atom.eex:12
|
||||||
|
#: lib/pleroma/web/templates/feed/feed/tag.rss.eex:8
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "tag feed description"
|
||||||
|
msgid "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex:1
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth authorization exists page title"
|
||||||
|
msgid "Authorization exists"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:32
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth authorize approve button"
|
||||||
|
msgid "Approve"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:30
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth authorize cancel button"
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:23
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth authorize message"
|
||||||
|
msgid "Application <strong>%{client_name}</strong> is requesting access to your account."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex:1
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth authorized page title"
|
||||||
|
msgid "Successfully authorized"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex:1
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth external provider page title"
|
||||||
|
msgid "Sign in with external provider"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex:13
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth external provider sign in button"
|
||||||
|
msgid "Sign in with %{strategy}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:54
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth login button"
|
||||||
|
msgid "Log In"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:51
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth login password prompt"
|
||||||
|
msgid "Password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:47
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth login username prompt"
|
||||||
|
msgid "Username"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:39
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth register nickname prompt"
|
||||||
|
msgid "Pleroma Handle"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:37
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth register nickname unchangeable warning"
|
||||||
|
msgid "Choose carefully! You won't be able to change this later. You will be able to change your display name, though."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:18
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth register page email prompt"
|
||||||
|
msgid "Email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:10
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth register page fill form prompt"
|
||||||
|
msgid "If you'd like to register a new account, please provide the details below."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:35
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth register page login button"
|
||||||
|
msgid "Proceed as existing user"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:31
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth register page login password prompt"
|
||||||
|
msgid "Password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:24
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth register page login prompt"
|
||||||
|
msgid "Alternatively, sign in to connect to existing account."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:27
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth register page login username prompt"
|
||||||
|
msgid "Name or email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:14
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth register page nickname prompt"
|
||||||
|
msgid "Nickname"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:22
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth register page register button"
|
||||||
|
msgid "Proceed as new user"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:8
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth register page title"
|
||||||
|
msgid "Registration Details"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:36
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth register page title"
|
||||||
|
msgid "This is the first time you visit! Please enter your Pleroma handle."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex:2
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth scopes message"
|
||||||
|
msgid "The following permissions will be granted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex:2
|
||||||
|
#: lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex:2
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "oauth token code message"
|
||||||
|
msgid "Token code is <br>%{token}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:12
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "mfa auth code prompt"
|
||||||
|
msgid "Authentication code"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:8
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "mfa auth page title"
|
||||||
|
msgid "Two-factor authentication"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:23
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "mfa auth page use recovery code link"
|
||||||
|
msgid "Enter a two-factor recovery code"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:20
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "mfa auth verify code button"
|
||||||
|
msgid "Verify"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:8
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "mfa recover page title"
|
||||||
|
msgid "Two-factor recovery"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:12
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "mfa recover recovery code prompt"
|
||||||
|
msgid "Recovery code"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:23
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "mfa recover use 2fa code link"
|
||||||
|
msgid "Enter a two-factor code"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:20
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "mfa recover verify recovery code button"
|
||||||
|
msgid "Verify"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex:8
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "static fe profile page remote follow button"
|
||||||
|
msgid "Remote follow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/email/digest.html.eex:163
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "digest email header line"
|
||||||
|
msgid "Hey %{nickname}, here is what you've missed!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/email/digest.html.eex:544
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "digest email receiver address"
|
||||||
|
msgid "The email address you are subscribed as is <a href='mailto:%{@user.email}' style='color: %{color};text-decoration: none;'>%{email}</a>. "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/email/digest.html.eex:538
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "digest email sending reason"
|
||||||
|
msgid "You have received this email because you have signed up to receive digest emails from <b>%{instance}</b> Pleroma instance."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/email/digest.html.eex:547
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "digest email unsubscribe action"
|
||||||
|
msgid "To unsubscribe, please go %{here}."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/email/digest.html.eex:547
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "digest email unsubscribe action link text"
|
||||||
|
msgid "here"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex:1
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "mailer unsubscribe failed message"
|
||||||
|
msgid "UNSUBSCRIBE FAILURE"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex:1
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "mailer unsubscribe successful message"
|
||||||
|
msgid "UNSUBSCRIBE SUCCESSFUL"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/email/digest.html.eex:385
|
||||||
|
#, elixir-format
|
||||||
|
msgctxt "new followers count header"
|
||||||
|
msgid "%{count} New Follower"
|
||||||
|
msgid_plural "%{count} New Followers"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:356
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "account archive email body - self-requested"
|
||||||
|
msgid "<p>You requested a full backup of your Pleroma account. It's ready for download:</p>\n<p><a href=\"%{download_url}\">%{download_url}</a></p>\n"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:384
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "account archive email subject"
|
||||||
|
msgid "Your account archive is ready"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:188
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "approval pending email body"
|
||||||
|
msgid "<h3>Awaiting Approval</h3>\n<p>Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.</p>\n"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:202
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "approval pending email subject"
|
||||||
|
msgid "Your account is awaiting approval"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:158
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "confirmation email body"
|
||||||
|
msgid "<h3>Thank you for registering on %{instance_name}</h3>\n<p>Email confirmation is required to activate the account.</p>\n<p>Please click the following link to <a href=\"%{confirmation_url}\">activate your account</a>.</p>\n"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:174
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "confirmation email subject"
|
||||||
|
msgid "%{instance_name} account confirmation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:310
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "digest email subject"
|
||||||
|
msgid "Your digest from %{instance_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:81
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "password reset email body"
|
||||||
|
msgid "<h3>Reset your password at %{instance_name}</h3>\n<p>Someone has requested password change for your account at %{instance_name}.</p>\n<p>If it was you, visit the following link to proceed: <a href=\"%{password_reset_url}\">reset password</a>.</p>\n<p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p>\n"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:98
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "password reset email subject"
|
||||||
|
msgid "Password reset"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:215
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "successful registration email body"
|
||||||
|
msgid "<h3>Hello @%{nickname},</h3>\n<p>Your account at %{instance_name} has been registered successfully.</p>\n<p>No further action is required to activate your account.</p>\n"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:231
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "successful registration email subject"
|
||||||
|
msgid "Account registered on %{instance_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:119
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "user invitation email body"
|
||||||
|
msgid "<h3>You are invited to %{instance_name}</h3>\n<p>%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.</p>\n<p>Click the following link to register: <a href=\"%{registration_url}\">accept invitation</a>.</p>\n"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:136
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "user invitation email subject"
|
||||||
|
msgid "Invitation to %{instance_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:53
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "welcome email html body"
|
||||||
|
msgid "Welcome to %{instance_name}!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:41
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "welcome email subject"
|
||||||
|
msgid "Welcome to %{instance_name}!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:65
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "welcome email text body"
|
||||||
|
msgid "Welcome to %{instance_name}!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/emails/user_email.ex:368
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "account archive email body - admin requested"
|
||||||
|
msgid "<p>Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:</p>\n<p><a href=\"%{download_url}\">%{download_url}</a></p>\n"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:123
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow error message - unknown error"
|
||||||
|
msgid "Something went wrong."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:67
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "remote follow error message - user not found"
|
||||||
|
msgid "Could not find user"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:8
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "status interact authorization button"
|
||||||
|
msgid "Interact"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:2
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "status interact error"
|
||||||
|
msgid "Error: %{error}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:95
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "status interact error message - status not found"
|
||||||
|
msgid "Could not find status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:144
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "status interact error message - unknown error"
|
||||||
|
msgid "Something went wrong."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:4
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "status interact header"
|
||||||
|
msgid "Interacting with %{nickname}'s %{status_link}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:4
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgctxt "status interact header - status link text"
|
||||||
|
msgid "status"
|
||||||
|
msgstr ""
|
||||||
|
|
@ -82,12 +82,59 @@ defmodule Pleroma.HTML.Scrubber.Default do
|
||||||
"recipients-inline",
|
"recipients-inline",
|
||||||
"quote-inline",
|
"quote-inline",
|
||||||
"invisible",
|
"invisible",
|
||||||
"ellipsis"
|
"ellipsis",
|
||||||
|
"mfm-tada",
|
||||||
|
"mfm-jelly",
|
||||||
|
"mfm-twitch",
|
||||||
|
"mfm-shake",
|
||||||
|
"mfm-spin",
|
||||||
|
"mfm-jump",
|
||||||
|
"mfm-bounce",
|
||||||
|
"mfm-flip",
|
||||||
|
"mfm-x2",
|
||||||
|
"mfm-x3",
|
||||||
|
"mfm-x4",
|
||||||
|
"mfm-scale",
|
||||||
|
"mfm-position",
|
||||||
|
"mfm-fg",
|
||||||
|
"mfm-bg",
|
||||||
|
"mfm-border",
|
||||||
|
"mfm-font",
|
||||||
|
"mfm-blur",
|
||||||
|
"mfm-rainbow",
|
||||||
|
"mfm-sparkle",
|
||||||
|
"mfm-rotate",
|
||||||
|
"mfm-ruby",
|
||||||
|
"mfm-unixtime",
|
||||||
|
# Exists in Akkoma but not Misskey?
|
||||||
|
"mfm-center"
|
||||||
])
|
])
|
||||||
|
|
||||||
Meta.allow_tag_with_this_attribute_values(:p, "class", ["quote-inline"])
|
Meta.allow_tag_with_this_attribute_values(:p, "class", ["quote-inline"])
|
||||||
|
|
||||||
Meta.allow_tag_with_these_attributes(:span, ["lang"])
|
Meta.allow_tag_with_these_attributes(:span, [
|
||||||
|
"lang",
|
||||||
|
"data-mfm-speed",
|
||||||
|
"data-mfm-delay",
|
||||||
|
"data-mfm-left",
|
||||||
|
"data-mfm-alternate",
|
||||||
|
"data-mfm-x",
|
||||||
|
"data-mfm-y",
|
||||||
|
"data-mfm-h",
|
||||||
|
"data-mfm-v",
|
||||||
|
"data-mfm-color",
|
||||||
|
"data-mfm-width",
|
||||||
|
"data-mfm-style",
|
||||||
|
"data-mfm-radius",
|
||||||
|
"data-mfm-noclip",
|
||||||
|
"data-mfm-serif",
|
||||||
|
"data-mfm-monospace",
|
||||||
|
"data-mfm-cursive",
|
||||||
|
"data-mfm-fantasy",
|
||||||
|
"data-mfm-emoji",
|
||||||
|
"data-mfm-math",
|
||||||
|
"data-mfm-deg"
|
||||||
|
])
|
||||||
|
|
||||||
Meta.allow_tag_with_this_attribute_values(:code, "class", ["inline"])
|
Meta.allow_tag_with_this_attribute_values(:code, "class", ["inline"])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,46 +4,55 @@
|
||||||
"https://w3id.org/security/v1",
|
"https://w3id.org/security/v1",
|
||||||
"https://purl.archive.org/socialweb/webfinger",
|
"https://purl.archive.org/socialweb/webfinger",
|
||||||
{
|
{
|
||||||
"Emoji": "toot:Emoji",
|
"as": "https://www.w3.org/ns/activitystreams#",
|
||||||
|
"ostatus": "http://ostatus.org#",
|
||||||
|
"schema": "http://schema.org#",
|
||||||
|
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||||
|
|
||||||
|
"fedibird": "http://fedibird.com/ns#",
|
||||||
|
"litepub": "http://litepub.social/ns#",
|
||||||
|
"sm": "http://smithereen.software/ns#",
|
||||||
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
|
|
||||||
|
"alsoKnownAs": {
|
||||||
|
"@id": "as:alsoKnownAs",
|
||||||
|
"@type": "@id"
|
||||||
|
},
|
||||||
"Hashtag": "as:Hashtag",
|
"Hashtag": "as:Hashtag",
|
||||||
"PropertyValue": "schema:PropertyValue",
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"quoteUrl": "as:quoteUrl",
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
|
||||||
"atomUri": "ostatus:atomUri",
|
"atomUri": "ostatus:atomUri",
|
||||||
"conversation": {
|
"conversation": {
|
||||||
"@id": "ostatus:conversation",
|
"@id": "ostatus:conversation",
|
||||||
"@type": "@id"
|
"@type": "@id"
|
||||||
},
|
},
|
||||||
"discoverable": "toot:discoverable",
|
|
||||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
"PropertyValue": "schema:PropertyValue",
|
||||||
"capabilities": "litepub:capabilities",
|
|
||||||
"ostatus": "http://ostatus.org#",
|
|
||||||
"schema": "http://schema.org#",
|
|
||||||
"toot": "http://joinmastodon.org/ns#",
|
|
||||||
"fedibird": "http://fedibird.com/ns#",
|
|
||||||
"value": "schema:value",
|
"value": "schema:value",
|
||||||
"sensitive": "as:sensitive",
|
|
||||||
"litepub": "http://litepub.social/ns#",
|
"quoteUri": "fedibird:quoteUri",
|
||||||
"invisible": "litepub:invisible",
|
|
||||||
|
"capabilities": "litepub:capabilities",
|
||||||
|
"ChatMessage": "litepub:ChatMessage",
|
||||||
"directMessage": "litepub:directMessage",
|
"directMessage": "litepub:directMessage",
|
||||||
|
"EmojiReact": "litepub:EmojiReact",
|
||||||
|
"formerRepresentations": "litepub:formerRepresentations",
|
||||||
|
"invisible": "litepub:invisible",
|
||||||
"listMessage": {
|
"listMessage": {
|
||||||
"@id": "litepub:listMessage",
|
"@id": "litepub:listMessage",
|
||||||
"@type": "@id"
|
"@type": "@id"
|
||||||
},
|
},
|
||||||
"quoteUrl": "as:quoteUrl",
|
|
||||||
"quoteUri": "fedibird:quoteUri",
|
|
||||||
"oauthRegistrationEndpoint": {
|
"oauthRegistrationEndpoint": {
|
||||||
"@id": "litepub:oauthRegistrationEndpoint",
|
"@id": "litepub:oauthRegistrationEndpoint",
|
||||||
"@type": "@id"
|
"@type": "@id"
|
||||||
},
|
},
|
||||||
"EmojiReact": "litepub:EmojiReact",
|
|
||||||
"ChatMessage": "litepub:ChatMessage",
|
"nonAnonymous": "sm:nonAnonymous",
|
||||||
"alsoKnownAs": {
|
|
||||||
"@id": "as:alsoKnownAs",
|
"discoverable": "toot:discoverable",
|
||||||
"@type": "@id"
|
"Emoji": "toot:Emoji"
|
||||||
},
|
|
||||||
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
|
||||||
"formerRepresentations": "litepub:formerRepresentations",
|
|
||||||
"sm": "http://smithereen.software/ns#",
|
|
||||||
"nonAnonymous": "sm:nonAnonymous"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -144,7 +144,13 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
|
||||||
quarantined_instances: [],
|
quarantined_instances: [],
|
||||||
managed_config: true,
|
managed_config: true,
|
||||||
static_dir: "instance/static/",
|
static_dir: "instance/static/",
|
||||||
allowed_post_formats: ["text/plain", "text/html", "text/markdown", "text/bbcode"],
|
allowed_post_formats: [
|
||||||
|
"text/plain",
|
||||||
|
"text/html",
|
||||||
|
"text/markdown",
|
||||||
|
"text/bbcode",
|
||||||
|
"text/x.misskeymarkdown"
|
||||||
|
],
|
||||||
autofollowed_nicknames: [],
|
autofollowed_nicknames: [],
|
||||||
max_pinned_statuses: 1,
|
max_pinned_statuses: 1,
|
||||||
attachment_links: false,
|
attachment_links: false,
|
||||||
|
|
|
||||||
16
test/pleroma/dependency_version_test.exs
Normal file
16
test/pleroma/dependency_version_test.exs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.DependencyVersionTest do
|
||||||
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
|
test "uses majic 1.2" do
|
||||||
|
majic_version =
|
||||||
|
:majic
|
||||||
|
|> Application.spec(:vsn)
|
||||||
|
|> to_string()
|
||||||
|
|
||||||
|
assert Version.match?(majic_version, "~> 1.2")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
||||||
|
|
||||||
alias Pleroma.Integration.WebsocketClient
|
alias Pleroma.Integration.WebsocketClient
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
alias Pleroma.Web.OAuth
|
alias Pleroma.Web.OAuth
|
||||||
|
|
||||||
@moduletag needs_streamer: true, capture_log: true
|
@moduletag needs_streamer: true, capture_log: true
|
||||||
|
|
@ -31,6 +32,48 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
||||||
WebsocketClient.start_link(self(), path, headers)
|
WebsocketClient.start_link(self(), path, headers)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp raw_websocket_handshake(qs, headers) do
|
||||||
|
uri = URI.parse(@path <> qs)
|
||||||
|
port = uri.port || 80
|
||||||
|
path = uri.path <> if(uri.query, do: "?" <> uri.query, else: "")
|
||||||
|
|
||||||
|
default_headers = [
|
||||||
|
{"host", "#{uri.host}:#{port}"},
|
||||||
|
{"upgrade", "websocket"},
|
||||||
|
{"connection", "Upgrade"},
|
||||||
|
{"sec-websocket-key", Base.encode64(:crypto.strong_rand_bytes(16))},
|
||||||
|
{"sec-websocket-version", "13"}
|
||||||
|
]
|
||||||
|
|
||||||
|
request = [
|
||||||
|
"GET #{path} HTTP/1.1\r\n",
|
||||||
|
Enum.map(default_headers ++ headers, fn {name, value} -> "#{name}: #{value}\r\n" end),
|
||||||
|
"\r\n"
|
||||||
|
]
|
||||||
|
|
||||||
|
with {:ok, socket} <-
|
||||||
|
:gen_tcp.connect(String.to_charlist(uri.host), port, [:binary, active: false], 1_000),
|
||||||
|
:ok <- :gen_tcp.send(socket, request),
|
||||||
|
{:ok, response} <- :gen_tcp.recv(socket, 0, 1_000) do
|
||||||
|
:gen_tcp.close(socket)
|
||||||
|
{:ok, parse_http_response(response)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_http_response(response) do
|
||||||
|
[headers | _] = String.split(response, "\r\n\r\n", parts: 2)
|
||||||
|
[status_line | header_lines] = String.split(headers, "\r\n")
|
||||||
|
[_, status | _] = String.split(status_line, " ")
|
||||||
|
|
||||||
|
headers =
|
||||||
|
Enum.map(header_lines, fn line ->
|
||||||
|
[name, value] = String.split(line, ":", parts: 2)
|
||||||
|
{String.downcase(name), String.trim(value)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
%{status: String.to_integer(status), headers: headers}
|
||||||
|
end
|
||||||
|
|
||||||
defp decode_json(json) do
|
defp decode_json(json) do
|
||||||
with {:ok, %{"event" => event, "payload" => payload_text}} <- Jason.decode(json),
|
with {:ok, %{"event" => event, "payload" => payload_text}} <- Jason.decode(json),
|
||||||
{:ok, payload} <- Jason.decode(payload_text) do
|
{:ok, payload} <- Jason.decode(payload_text) do
|
||||||
|
|
@ -85,9 +128,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
||||||
assert json["payload"]
|
assert json["payload"]
|
||||||
assert {:ok, json} = Jason.decode(json["payload"])
|
assert {:ok, json} = Jason.decode(json["payload"])
|
||||||
|
|
||||||
view_json =
|
view_json = atom_key_to_string(StatusView.render("show.json", activity: activity, for: nil))
|
||||||
Pleroma.Web.MastodonAPI.StatusView.render("show.json", activity: activity, for: nil)
|
|
||||||
|> atom_key_to_string()
|
|
||||||
|
|
||||||
assert json == view_json
|
assert json == view_json
|
||||||
end
|
end
|
||||||
|
|
@ -114,10 +155,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
||||||
assert json["payload"]
|
assert json["payload"]
|
||||||
assert {:ok, json} = Jason.decode(json["payload"])
|
assert {:ok, json} = Jason.decode(json["payload"])
|
||||||
|
|
||||||
view_json =
|
view_json = atom_key_to_string(StatusView.render("show.json", activity: activity, for: nil))
|
||||||
Pleroma.Web.MastodonAPI.StatusView.render("show.json", activity: activity, for: nil)
|
|
||||||
|> Jason.encode!()
|
|
||||||
|> Jason.decode!()
|
|
||||||
|
|
||||||
assert json == view_json
|
assert json == view_json
|
||||||
end
|
end
|
||||||
|
|
@ -279,6 +317,54 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "echoes the Sec-WebSocket-Protocol token in the handshake", %{token: token} do
|
||||||
|
assert {:ok, %{status: 101, headers: headers}} =
|
||||||
|
raw_websocket_handshake("?stream=user", [
|
||||||
|
{"sec-websocket-protocol", token.token}
|
||||||
|
])
|
||||||
|
|
||||||
|
assert {"sec-websocket-protocol", token.token} in headers
|
||||||
|
end
|
||||||
|
|
||||||
|
test "echoes the selected Sec-WebSocket-Protocol token", %{token: token} do
|
||||||
|
assert {:ok, %{status: 101, headers: headers}} =
|
||||||
|
raw_websocket_handshake("?stream=user", [
|
||||||
|
{"sec-websocket-protocol", "#{token.token}, phoenix"}
|
||||||
|
])
|
||||||
|
|
||||||
|
assert {"sec-websocket-protocol", token.token} in headers
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not echo an invalid Sec-WebSocket-Protocol token", %{token: token} do
|
||||||
|
assert {:ok, %{status: 401, headers: headers}} =
|
||||||
|
raw_websocket_handshake("?stream=user", [
|
||||||
|
{"sec-websocket-protocol", "invalid"}
|
||||||
|
])
|
||||||
|
|
||||||
|
refute {"sec-websocket-protocol", token.token} in headers
|
||||||
|
refute List.keymember?(headers, "sec-websocket-protocol", 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "prefers sec-websocket-protocol token over query access_token", %{
|
||||||
|
token: token,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
assert {:ok, state} =
|
||||||
|
Pleroma.Web.MastodonAPI.WebsocketHandler.connect(%{
|
||||||
|
params: %{"stream" => "user", "access_token" => "invalid"},
|
||||||
|
connect_info: %{
|
||||||
|
sec_websocket_headers: [
|
||||||
|
{"sec-websocket-version", "13"},
|
||||||
|
{"sec-websocket-protocol", token.token}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
assert state.user.id == user.id
|
||||||
|
assert state.oauth_token.id == token.id
|
||||||
|
assert state.topics != []
|
||||||
|
end
|
||||||
|
|
||||||
test "accepts valid token on client-sent event", %{token: token} do
|
test "accepts valid token on client-sent event", %{token: token} do
|
||||||
assert {:ok, pid} = start_socket()
|
assert {:ok, pid} = start_socket()
|
||||||
|
|
||||||
|
|
@ -430,12 +516,12 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
||||||
assert {:ok, json} = Jason.decode(json["payload"])
|
assert {:ok, json} = Jason.decode(json["payload"])
|
||||||
|
|
||||||
view_json =
|
view_json =
|
||||||
Pleroma.Web.MastodonAPI.StatusView.render("show.json",
|
atom_key_to_string(
|
||||||
|
StatusView.render("show.json",
|
||||||
activity: activity,
|
activity: activity,
|
||||||
for: reading_user
|
for: reading_user
|
||||||
)
|
)
|
||||||
|> Jason.encode!()
|
)
|
||||||
|> Jason.decode!()
|
|
||||||
|
|
||||||
assert json == view_json
|
assert json == view_json
|
||||||
end
|
end
|
||||||
|
|
@ -458,12 +544,12 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
||||||
activity = Pleroma.Activity.normalize(activity)
|
activity = Pleroma.Activity.normalize(activity)
|
||||||
|
|
||||||
view_json =
|
view_json =
|
||||||
Pleroma.Web.MastodonAPI.StatusView.render("show.json",
|
atom_key_to_string(
|
||||||
|
StatusView.render("show.json",
|
||||||
activity: activity,
|
activity: activity,
|
||||||
for: reading_user
|
for: reading_user
|
||||||
)
|
)
|
||||||
|> Jason.encode!()
|
)
|
||||||
|> Jason.decode!()
|
|
||||||
|
|
||||||
assert {:ok, %{"event" => "status.update", "payload" => ^view_json}} = decode_json(raw_json)
|
assert {:ok, %{"event" => "status.update", "payload" => ^view_json}} = decode_json(raw_json)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.SignatureTest do
|
||||||
import Mock
|
import Mock
|
||||||
|
|
||||||
alias Pleroma.Signature
|
alias Pleroma.Signature
|
||||||
|
alias Pleroma.StubbedHTTPSignaturesMock, as: HTTPSignaturesMock
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
|
@ -103,6 +104,18 @@ defmodule Pleroma.SignatureTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "validate_signature/1" do
|
||||||
|
test "treats HTTP signature errors as failed validation" do
|
||||||
|
conn = %Plug.Conn{method: "GET", request_path: "/inbox", req_headers: []}
|
||||||
|
|
||||||
|
Mox.expect(HTTPSignaturesMock, :validate_conn, fn _conn ->
|
||||||
|
{:error, :request_target_header}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert Signature.validate_signature(conn) == false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "key_id_to_actor_id/1" do
|
describe "key_id_to_actor_id/1" do
|
||||||
test "it properly deduces the actor id for misskey" do
|
test "it properly deduces the actor id for misskey" do
|
||||||
assert Signature.key_id_to_actor_id("https://example.com/users/1234/publickey") ==
|
assert Signature.key_id_to_actor_id("https://example.com/users/1234/publickey") ==
|
||||||
|
|
|
||||||
195
test/pleroma/user/search_test.exs
Normal file
195
test/pleroma/user/search_test.exs
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.User.SearchTest do
|
||||||
|
use Pleroma.DataCase, async: false
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Instances
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
describe "search/2 mention suggestions" do
|
||||||
|
test "prioritizes followed/follower users before others" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
related =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
nickname: "hj@real.example",
|
||||||
|
ap_id: "https://real.example/users/hj",
|
||||||
|
last_status_at: ~N[2020-01-01 00:00:00]
|
||||||
|
)
|
||||||
|
|
||||||
|
other = insert(:user, nickname: "hj", last_status_at: ~N[2020-01-02 00:00:00])
|
||||||
|
|
||||||
|
{:ok, _related, _user} = User.follow(related, user)
|
||||||
|
|
||||||
|
results = User.search("hj", for_user: user) |> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
assert results == [related.id, other.id]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "orders followed/follower users by most recent activity" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
older =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
nickname: "ali@remote.example",
|
||||||
|
ap_id: "https://remote.example/users/ali",
|
||||||
|
last_status_at: ~N[2020-01-01 00:00:00]
|
||||||
|
)
|
||||||
|
|
||||||
|
newer =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
nickname: "alia@remote.example",
|
||||||
|
ap_id: "https://remote.example/users/alia",
|
||||||
|
last_status_at: ~N[2020-01-02 00:00:00]
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, _user, _older} = User.follow(user, older)
|
||||||
|
{:ok, _user, _newer} = User.follow(user, newer)
|
||||||
|
|
||||||
|
assert [newer.id, older.id] ==
|
||||||
|
User.search("ali", for_user: user)
|
||||||
|
|> Enum.map(& &1.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "groups followed/follower users first and sorts them by recency" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
following_newest =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
nickname: "mentiontesta@related.example",
|
||||||
|
ap_id: "https://related.example/users/mentiontesta",
|
||||||
|
last_status_at: ~N[2020-01-03 00:00:00]
|
||||||
|
)
|
||||||
|
|
||||||
|
follower_middle =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
nickname: "mentiontestb@related.example",
|
||||||
|
ap_id: "https://related.example/users/mentiontestb",
|
||||||
|
last_status_at: ~N[2020-01-02 00:00:00]
|
||||||
|
)
|
||||||
|
|
||||||
|
mutual_oldest =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
nickname: "mentiontestc@related.example",
|
||||||
|
ap_id: "https://related.example/users/mentiontestc",
|
||||||
|
last_status_at: ~N[2020-01-01 00:00:00]
|
||||||
|
)
|
||||||
|
|
||||||
|
unrelated_newer =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
nickname: "mentiontestd@unrelated.example",
|
||||||
|
ap_id: "https://unrelated.example/users/mentiontestd",
|
||||||
|
last_status_at: ~N[2020-01-04 00:00:00]
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, _user, _following_newest} = User.follow(user, following_newest)
|
||||||
|
{:ok, _follower_middle, _user} = User.follow(follower_middle, user)
|
||||||
|
|
||||||
|
{:ok, _user, _mutual_oldest} = User.follow(user, mutual_oldest)
|
||||||
|
{:ok, _mutual_oldest, _user} = User.follow(mutual_oldest, user)
|
||||||
|
|
||||||
|
results = User.search("mentiontest", for_user: user)
|
||||||
|
|
||||||
|
assert Enum.map(results, & &1.id) ==
|
||||||
|
[following_newest.id, follower_middle.id, mutual_oldest.id, unrelated_newer.id]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "uses last_active_at when last_status_at is missing" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
older =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
nickname: "activefallbacka@remote.example",
|
||||||
|
ap_id: "https://remote.example/users/activefallbacka",
|
||||||
|
last_status_at: nil,
|
||||||
|
last_active_at: ~N[2020-01-01 00:00:00]
|
||||||
|
)
|
||||||
|
|
||||||
|
newer =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
nickname: "activefallbackb@remote.example",
|
||||||
|
ap_id: "https://remote.example/users/activefallbackb",
|
||||||
|
last_status_at: nil,
|
||||||
|
last_active_at: ~N[2020-01-02 00:00:00]
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, _user, _older} = User.follow(user, older)
|
||||||
|
{:ok, _user, _newer} = User.follow(user, newer)
|
||||||
|
|
||||||
|
assert [newer.id, older.id] ==
|
||||||
|
User.search("activefallback", for_user: user)
|
||||||
|
|> Enum.map(& &1.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not return deactivated users even if related" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
active =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
nickname: "deactivatedtesta@remote.example",
|
||||||
|
ap_id: "https://remote.example/users/deactivatedtesta",
|
||||||
|
last_status_at: ~N[2020-01-02 00:00:00]
|
||||||
|
)
|
||||||
|
|
||||||
|
deactivated =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
nickname: "deactivatedtestb@remote.example",
|
||||||
|
ap_id: "https://remote.example/users/deactivatedtestb",
|
||||||
|
last_status_at: ~N[2020-01-03 00:00:00]
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, _user, _active} = User.follow(user, active)
|
||||||
|
{:ok, _user, _deactivated} = User.follow(user, deactivated)
|
||||||
|
Repo.update!(Ecto.Changeset.change(deactivated, is_active: false))
|
||||||
|
|
||||||
|
results = User.search("deactivatedtest", for_user: user) |> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
assert results == [active.id]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not return users from unreachable instances" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _instance} = Instances.set_unreachable("dead.example")
|
||||||
|
|
||||||
|
dead =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
nickname: "ali@dead.example",
|
||||||
|
ap_id: "https://dead.example/users/ali",
|
||||||
|
last_status_at: ~N[2020-01-02 00:00:00]
|
||||||
|
)
|
||||||
|
|
||||||
|
alive =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
nickname: "ali@alive.example",
|
||||||
|
ap_id: "https://alive.example/users/ali",
|
||||||
|
last_status_at: ~N[2020-01-02 00:00:00]
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, _user, _alive} = User.follow(user, alive)
|
||||||
|
{:ok, _user, _dead} = User.follow(user, dead)
|
||||||
|
|
||||||
|
results = User.search("ali", for_user: user) |> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
assert results == [alive.id]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -950,6 +950,50 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
||||||
refute Activity.get_by_ap_id(data["id"])
|
refute Activity.get_by_ap_id(data["id"])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does not process post with Host header not for us", %{conn: conn} do
|
||||||
|
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||||
|
object_id = "https://one.com/objects/inbox-forged-note"
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => alice.ap_id,
|
||||||
|
"id" => "https://one.com/activities/inbox-forged-create",
|
||||||
|
"context" => "https://one.com/contexts/inbox-forged-create",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => %{
|
||||||
|
"type" => "Note",
|
||||||
|
"id" => object_id,
|
||||||
|
"actor" => alice.ap_id,
|
||||||
|
"attributedTo" => alice.ap_id,
|
||||||
|
"context" => "https://one.com/contexts/inbox-forged-create",
|
||||||
|
"content" => "forged post",
|
||||||
|
"published" => "2024-07-25T13:33:31Z",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Plug will complain when replacing raw host header with put_req_header.
|
||||||
|
# The Plug way is updating conn.host, but that isn't the raw header
|
||||||
|
# and that isn't used in the EnsureHostMatchesPlug, because it doesn't include the port.
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign_valid_signature_for_actor(alice)
|
||||||
|
|> delete_req_header("host")
|
||||||
|
|> put_req_header("content-type", "application/activity+json")
|
||||||
|
|
||||||
|
conn = %{conn | req_headers: conn.req_headers ++ [{"host", "invalid.example.com"}]}
|
||||||
|
conn = post(conn, "/inbox", data)
|
||||||
|
|
||||||
|
assert "Host header does not match this instance" == conn.resp_body
|
||||||
|
assert 400 == conn.status
|
||||||
|
assert true == conn.halted
|
||||||
|
|
||||||
|
refute Activity.get_by_ap_id(data["id"])
|
||||||
|
refute Object.get_by_ap_id(object_id)
|
||||||
|
end
|
||||||
|
|
||||||
test "accept follow activity", %{conn: conn} do
|
test "accept follow activity", %{conn: conn} do
|
||||||
clear_config([:instance, :federating], true)
|
clear_config([:instance, :federating], true)
|
||||||
relay = Relay.get_actor()
|
relay = Relay.get_actor()
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,171 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
|
||||||
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
|
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "a Misskey MFM note is rendered from source content" do
|
||||||
|
user = insert(:user, ap_id: "https://misskey.example/users/alice")
|
||||||
|
|
||||||
|
note = %{
|
||||||
|
"id" => "https://misskey.example/notes/1",
|
||||||
|
"type" => "Note",
|
||||||
|
"actor" => user.ap_id,
|
||||||
|
"attributedTo" => user.ap_id,
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"content" => "original content",
|
||||||
|
"context" => Utils.generate_context_id(),
|
||||||
|
"source" => %{
|
||||||
|
"content" => "$[spin.speed=1s mfm goes here] <script>alert('xss')</script>",
|
||||||
|
"mediaType" => "text/x.misskeymarkdown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%{valid?: true, changes: %{content: content, source: source}} =
|
||||||
|
ArticleNotePageValidator.cast_and_validate(note)
|
||||||
|
|
||||||
|
assert source["mediaType"] == "text/x.misskeymarkdown"
|
||||||
|
assert content =~ ~s(class="mfm-spin")
|
||||||
|
assert content =~ ~s(data-mfm-speed="1s")
|
||||||
|
assert content =~ "mfm goes here"
|
||||||
|
refute content =~ "original content"
|
||||||
|
refute content =~ "<script"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "a Misskey MFM note resolves only cached AP mention tags" do
|
||||||
|
remote_user = insert(:user, ap_id: "https://misskey.example/users/carol")
|
||||||
|
local_user = insert(:user, nickname: "local_user")
|
||||||
|
|
||||||
|
note = %{
|
||||||
|
"id" => "https://misskey.example/notes/3",
|
||||||
|
"type" => "Note",
|
||||||
|
"actor" => remote_user.ap_id,
|
||||||
|
"attributedTo" => remote_user.ap_id,
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"content" => "original content",
|
||||||
|
"context" => Utils.generate_context_id(),
|
||||||
|
"tag" => [
|
||||||
|
%{
|
||||||
|
"type" => "Mention",
|
||||||
|
"name" => "@local_user",
|
||||||
|
"href" => local_user.ap_id
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
"type" => "Mention",
|
||||||
|
"name" => "@uncached",
|
||||||
|
"href" => "https://misskey.example/users/uncached"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source" => %{
|
||||||
|
"content" => "@local_user @uncached $[spin hello]",
|
||||||
|
"mediaType" => "text/x.misskeymarkdown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%{valid?: true, changes: %{content: content}} =
|
||||||
|
ArticleNotePageValidator.cast_and_validate(note)
|
||||||
|
|
||||||
|
assert content =~ local_user.ap_id
|
||||||
|
assert content =~ "@uncached"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "a Misskey MFM note drops oversized source content instead of parsing it" do
|
||||||
|
user = insert(:user, ap_id: "https://misskey.example/users/oversized")
|
||||||
|
|
||||||
|
note = %{
|
||||||
|
"id" => "https://misskey.example/notes/4",
|
||||||
|
"type" => "Note",
|
||||||
|
"actor" => user.ap_id,
|
||||||
|
"attributedTo" => user.ap_id,
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"content" => "<span class=\"mfm-spin\">safe fallback</span>",
|
||||||
|
"context" => Utils.generate_context_id(),
|
||||||
|
"source" => %{
|
||||||
|
"content" => String.duplicate("x", 5_001),
|
||||||
|
"mediaType" => "text/x.misskeymarkdown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%{valid?: true, changes: %{content: content, source: source}} =
|
||||||
|
ArticleNotePageValidator.cast_and_validate(note)
|
||||||
|
|
||||||
|
assert content == "<span class=\"mfm-spin\">safe fallback</span>"
|
||||||
|
refute Map.has_key?(source, "content")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "a note drops oversized non-MFM source content" do
|
||||||
|
user = insert(:user, ap_id: "https://example.com/users/source")
|
||||||
|
|
||||||
|
note = %{
|
||||||
|
"id" => "https://example.com/notes/1",
|
||||||
|
"type" => "Note",
|
||||||
|
"actor" => user.ap_id,
|
||||||
|
"attributedTo" => user.ap_id,
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"content" => "regular content",
|
||||||
|
"context" => Utils.generate_context_id(),
|
||||||
|
"source" => %{
|
||||||
|
"content" => String.duplicate("x", 5_001),
|
||||||
|
"mediaType" => "text/markdown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%{valid?: true, changes: %{source: source}} = ArticleNotePageValidator.cast_and_validate(note)
|
||||||
|
|
||||||
|
assert source == %{"mediaType" => "text/markdown"}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "a Misskey MFM note with legacy _misskey_content is rendered" do
|
||||||
|
user = insert(:user, ap_id: "https://misskey.example/users/legacy")
|
||||||
|
|
||||||
|
note = %{
|
||||||
|
"id" => "https://misskey.example/notes/5",
|
||||||
|
"type" => "Note",
|
||||||
|
"actor" => user.ap_id,
|
||||||
|
"attributedTo" => user.ap_id,
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"content" => "original content",
|
||||||
|
"context" => Utils.generate_context_id(),
|
||||||
|
"_misskey_content" => "$[spin legacy]"
|
||||||
|
}
|
||||||
|
|
||||||
|
%{valid?: true, changes: %{content: content, source: source}} =
|
||||||
|
ArticleNotePageValidator.cast_and_validate(note)
|
||||||
|
|
||||||
|
assert source == %{"content" => "$[spin legacy]", "mediaType" => "text/x.misskeymarkdown"}
|
||||||
|
assert content =~ ~s(class="mfm-spin")
|
||||||
|
assert content =~ "legacy"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "a Misskey MFM note with htmlMfm is scrubbed but not rendered from source content" do
|
||||||
|
user = insert(:user, ap_id: "https://misskey.example/users/bob")
|
||||||
|
|
||||||
|
note = %{
|
||||||
|
"id" => "https://misskey.example/notes/2",
|
||||||
|
"type" => "Note",
|
||||||
|
"actor" => user.ap_id,
|
||||||
|
"attributedTo" => user.ap_id,
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"content" =>
|
||||||
|
"<span class=\"mfm-spin\">already rendered</span><script>alert('xss')</script>",
|
||||||
|
"htmlMfm" => true,
|
||||||
|
"context" => Utils.generate_context_id(),
|
||||||
|
"source" => %{
|
||||||
|
"content" => String.duplicate("x", 5_001),
|
||||||
|
"mediaType" => "text/x.misskeymarkdown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%{valid?: true, changes: %{content: content, htmlMfm: true, source: source}} =
|
||||||
|
ArticleNotePageValidator.cast_and_validate(note)
|
||||||
|
|
||||||
|
assert content == "<span class=\"mfm-spin\">already rendered</span>alert('xss')"
|
||||||
|
refute Map.has_key?(source, "content")
|
||||||
|
end
|
||||||
|
|
||||||
test "a Note with validated likes collection validates" do
|
test "a Note with validated likes collection validates" do
|
||||||
insert(:user, ap_id: "https://pol.social/users/mkljczk")
|
insert(:user, ap_id: "https://pol.social/users/mkljczk")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,43 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||||
assert activity.data["cc"] == [user.ap_id]
|
assert activity.data["cc"] == [user.ap_id]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it rejects Flag activities when both reporter and reported account are remote" do
|
||||||
|
reporter = insert(:user, local: false, domain: "mastodon.cat")
|
||||||
|
reported = insert(:user, local: false, domain: "nicecrew.digital")
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"actor" => reporter.ap_id,
|
||||||
|
"content" => "blocked AND reported!!!",
|
||||||
|
"object" => [reported.ap_id, "https://nicecrew.digital/objects/report-status"],
|
||||||
|
"type" => "Flag"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:reject, reason} = Transmogrifier.handle_incoming(message)
|
||||||
|
assert reason =~ "third-party report"
|
||||||
|
refute "Flag" |> Pleroma.Activity.Queries.by_type() |> Pleroma.Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it accepts Flag activities with just actor id as object" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"cc" => [user.ap_id],
|
||||||
|
"object" => user.ap_id,
|
||||||
|
"type" => "Flag",
|
||||||
|
"content" => "blocked AND reported!!!",
|
||||||
|
"actor" => other_user.ap_id
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, activity} = Transmogrifier.handle_incoming(message)
|
||||||
|
|
||||||
|
assert activity.data["content"] == "blocked AND reported!!!"
|
||||||
|
assert activity.data["actor"] == other_user.ap_id
|
||||||
|
assert activity.data["cc"] == [user.ap_id]
|
||||||
|
end
|
||||||
|
|
||||||
test "it accepts Move activities" do
|
test "it accepts Move activities" do
|
||||||
old_user = insert(:user)
|
old_user = insert(:user)
|
||||||
new_user = insert(:user)
|
new_user = insert(:user)
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||||
%{
|
%{
|
||||||
"@language" => "und"
|
"@language" => "und",
|
||||||
|
"htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +193,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||||
%{
|
%{
|
||||||
"@language" => "pl"
|
"@language" => "pl",
|
||||||
|
"htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -709,6 +709,47 @@ defmodule Pleroma.Web.CommonAPITest do
|
||||||
assert object.data["source"]["content"] == post
|
assert object.data["source"]["content"] == post
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it renders MFM posts and marks their ActivityPub representation" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
post = "<p class='scrub-this'>$[spin.speed=1s 13:37]</p>"
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: post,
|
||||||
|
content_type: "text/x.misskeymarkdown"
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
|
||||||
|
assert object.data["htmlMfm"] == true
|
||||||
|
|
||||||
|
assert object.data["source"] == %{
|
||||||
|
"content" => post,
|
||||||
|
"mediaType" => "text/x.misskeymarkdown"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert object.data["content"] =~ ~s(class="mfm-spin")
|
||||||
|
assert object.data["content"] =~ ~s(data-mfm-speed="1s")
|
||||||
|
assert object.data["content"] =~ "13:37"
|
||||||
|
refute object.data["content"] =~ "scrub-this"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it falls back safely for malformed MFM" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "$[spin malformed",
|
||||||
|
content_type: "text/x.misskeymarkdown"
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
|
||||||
|
refute object.data["content"] =~ ~s(class="mfm-spin")
|
||||||
|
assert object.data["content"] =~ "malformed"
|
||||||
|
end
|
||||||
|
|
||||||
test "it does not allow replies to direct messages that are not direct messages themselves" do
|
test "it does not allow replies to direct messages that are not direct messages themselves" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -180,4 +180,179 @@ defmodule Pleroma.Web.MastodonAPI.PollViewTest do
|
||||||
|
|
||||||
assert result[:pleroma][:non_anonymous] == true
|
assert result[:pleroma][:non_anonymous] == true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "prefers votersCount over voters list when both are present" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "Which flavor?",
|
||||||
|
poll: %{options: ["chocolate", "vanilla"], expires_in: 20}
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
|
||||||
|
voter = insert(:user)
|
||||||
|
{:ok, _, object} = CommonAPI.vote(object, voter, [0])
|
||||||
|
|
||||||
|
assert object.data["votersCount"] == 1
|
||||||
|
assert length(object.data["voters"]) == 1
|
||||||
|
|
||||||
|
object = %{
|
||||||
|
object
|
||||||
|
| data: Map.put(object.data, "votersCount", 42)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = PollView.render("show.json", %{object: object})
|
||||||
|
|
||||||
|
assert result[:voters_count] == 42
|
||||||
|
end
|
||||||
|
|
||||||
|
test "falls back to voters list when votersCount is absent" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "Which flavor?",
|
||||||
|
poll: %{options: ["chocolate", "vanilla"], expires_in: 20}
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
|
||||||
|
voter = insert(:user)
|
||||||
|
{:ok, _, object} = CommonAPI.vote(object, voter, [0])
|
||||||
|
|
||||||
|
assert length(object.data["voters"]) == 1
|
||||||
|
|
||||||
|
data = Map.delete(object.data, "votersCount")
|
||||||
|
object = %{object | data: data}
|
||||||
|
|
||||||
|
result = PollView.render("show.json", %{object: object})
|
||||||
|
|
||||||
|
assert result[:voters_count] == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 0 when both votersCount and voters are absent" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "Which flavor?",
|
||||||
|
poll: %{options: ["chocolate", "vanilla"], expires_in: 20}
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
|
||||||
|
data =
|
||||||
|
object.data
|
||||||
|
|> Map.delete("votersCount")
|
||||||
|
|> Map.delete("voters")
|
||||||
|
|
||||||
|
object = %{object | data: data}
|
||||||
|
|
||||||
|
result = PollView.render("show.json", %{object: object})
|
||||||
|
|
||||||
|
assert result[:voters_count] == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 0 when voters list is empty" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "Which flavor?",
|
||||||
|
poll: %{options: ["chocolate", "vanilla"], expires_in: 20}
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
|
||||||
|
data =
|
||||||
|
object.data
|
||||||
|
|> Map.delete("votersCount")
|
||||||
|
|> Map.put("voters", [])
|
||||||
|
|
||||||
|
object = %{object | data: data}
|
||||||
|
|
||||||
|
result = PollView.render("show.json", %{object: object})
|
||||||
|
|
||||||
|
assert result[:voters_count] == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not inflate votersCount when same voter picks multiple options" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "Pick several",
|
||||||
|
poll: %{options: ["a", "b", "c"], expires_in: 20, multiple: true}
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
|
||||||
|
voter = insert(:user)
|
||||||
|
{:ok, _, object} = CommonAPI.vote(object, voter, [0, 2])
|
||||||
|
|
||||||
|
assert object.data["votersCount"] == 1
|
||||||
|
assert length(object.data["voters"]) == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "preserves votersCount from remote source when existing voter picks another option" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "Pick several",
|
||||||
|
poll: %{options: ["a", "b"], expires_in: 20, multiple: true}
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
|
||||||
|
voter = insert(:user)
|
||||||
|
{:ok, _, object} = CommonAPI.vote(object, voter, [0, 1])
|
||||||
|
|
||||||
|
object = %{object | data: Map.put(object.data, "votersCount", 14)}
|
||||||
|
|
||||||
|
result = PollView.render("show.json", %{object: object})
|
||||||
|
|
||||||
|
assert result[:voters_count] == 14
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 0 when votersCount is explicitly 0" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "Pick one",
|
||||||
|
poll: %{options: ["a", "b"], expires_in: 20}
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
|
||||||
|
object = %{object | data: Map.put(object.data, "votersCount", 0)}
|
||||||
|
|
||||||
|
result = PollView.render("show.json", %{object: object})
|
||||||
|
|
||||||
|
assert result[:voters_count] == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "falls back to voters list when votersCount is nil" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "Pick one",
|
||||||
|
poll: %{options: ["a", "b"], expires_in: 20}
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
|
||||||
|
voter = insert(:user)
|
||||||
|
{:ok, _, object} = CommonAPI.vote(object, voter, [0])
|
||||||
|
|
||||||
|
object = %{object | data: Map.put(object.data, "votersCount", nil)}
|
||||||
|
|
||||||
|
result = PollView.render("show.json", %{object: object})
|
||||||
|
|
||||||
|
assert result[:voters_count] == length(object.data["voters"])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
121
test/pleroma/web/plugs/ensure_host_matches_plug_test.exs
Normal file
121
test/pleroma/web/plugs/ensure_host_matches_plug_test.exs
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Plugs.EnsureHostMatchesPlugTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
|
alias Pleroma.Web.Plugs.EnsureHostMatchesPlug
|
||||||
|
|
||||||
|
import Plug.Conn
|
||||||
|
import Tesla.Mock
|
||||||
|
|
||||||
|
setup do
|
||||||
|
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
defp set_host(conn, host), do: %{conn | req_headers: conn.req_headers ++ [{"host", host}]}
|
||||||
|
|
||||||
|
describe "EnsureHostMatchesPlug" do
|
||||||
|
setup do
|
||||||
|
conn = build_conn(:post, "/cofe") |> assign(:valid_signature, true)
|
||||||
|
[conn: conn]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "gracefully handles no Host header", %{conn: conn} do
|
||||||
|
conn = EnsureHostMatchesPlug.call(conn, %{})
|
||||||
|
|
||||||
|
assert conn.status == 400
|
||||||
|
assert conn.halted == true
|
||||||
|
assert conn.resp_body == "Host header not provided"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "gracefully handles empty Host header", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> set_host("")
|
||||||
|
|> EnsureHostMatchesPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.status == 400
|
||||||
|
assert conn.halted == true
|
||||||
|
assert conn.resp_body == "Host header not provided"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it rejects Host header not matching Endpoint URL", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> set_host("invalid.example.com")
|
||||||
|
|> EnsureHostMatchesPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.status == 400
|
||||||
|
assert conn.halted == true
|
||||||
|
assert conn.resp_body == "Host header does not match this instance"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it rejects Host header not matching Endpoint with port", %{conn: conn} do
|
||||||
|
endpoint = URI.parse(Endpoint.url())
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> set_host("invalid.example.com:#{endpoint.port}")
|
||||||
|
|> EnsureHostMatchesPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.status == 400
|
||||||
|
assert conn.halted == true
|
||||||
|
assert conn.resp_body == "Host header does not match this instance"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it rejects Host header not matching Endpoint port", %{conn: conn} do
|
||||||
|
endpoint = URI.parse(Endpoint.url())
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> set_host("#{endpoint.host}:25")
|
||||||
|
|> EnsureHostMatchesPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.status == 400
|
||||||
|
assert conn.halted == true
|
||||||
|
assert conn.resp_body == "Host header does not match this instance"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it rejects multiple Host headers", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> set_host("host1.example.com")
|
||||||
|
|> set_host("host2.example.com")
|
||||||
|
|> EnsureHostMatchesPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.status == 400
|
||||||
|
assert conn.halted == true
|
||||||
|
assert conn.resp_body == "More than one Host header provided"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it works for Host header without port", %{conn: conn} do
|
||||||
|
endpoint = URI.parse(Endpoint.url())
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> set_host("#{endpoint.host}")
|
||||||
|
|> EnsureHostMatchesPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.halted == false
|
||||||
|
assert Map.get(conn.assigns, :valid_host_header, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it works for Host header with port same as Endpoint", %{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
endpoint = URI.parse(Endpoint.url())
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> set_host("#{endpoint.host}:#{endpoint.port}")
|
||||||
|
|> EnsureHostMatchesPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.halted == false
|
||||||
|
assert Map.get(conn.assigns, :valid_host_header, nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -106,4 +106,38 @@ defmodule Pleroma.Web.Plugs.RemoteIpTest do
|
||||||
|
|
||||||
assert conn.remote_ip == {1, 1, 1, 1}
|
assert conn.remote_ip == {1, 1, 1, 1}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "reserved ranges are configurable" do
|
||||||
|
clear_config([RemoteIp, :reserved], [])
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn(:get, "/")
|
||||||
|
|> put_req_header("x-forwarded-for", "1.1.1.1, 10.0.0.3")
|
||||||
|
|> RemoteIp.call(nil)
|
||||||
|
|
||||||
|
assert conn.remote_ip == {10, 0, 0, 3}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "clients override reserved ranges" do
|
||||||
|
clear_config([RemoteIp, :clients], ["10.0.0.0/8"])
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn(:get, "/")
|
||||||
|
|> put_req_header("x-forwarded-for", "1.1.1.1, 10.0.0.3")
|
||||||
|
|> RemoteIp.call(nil)
|
||||||
|
|
||||||
|
assert conn.remote_ip == {10, 0, 0, 3}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "clients override proxies" do
|
||||||
|
clear_config([RemoteIp, :clients], ["10.0.0.3"])
|
||||||
|
clear_config([RemoteIp, :proxies], ["10.0.0.0/8"])
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn(:get, "/")
|
||||||
|
|> put_req_header("x-forwarded-for", "1.1.1.1, 10.0.0.3")
|
||||||
|
|> RemoteIp.call(nil)
|
||||||
|
|
||||||
|
assert conn.remote_ip == {10, 0, 0, 3}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,22 @@
|
||||||
defmodule Pleroma.Web.RichMedia.BackfillTest do
|
defmodule Pleroma.Web.RichMedia.BackfillTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
alias Pleroma.Tests.ObanHelpers
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.RichMedia.Backfill
|
alias Pleroma.Web.RichMedia.Backfill
|
||||||
alias Pleroma.Web.RichMedia.Card
|
alias Pleroma.Web.RichMedia.Card
|
||||||
|
|
||||||
import Mox
|
import Mox
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
setup_all do: clear_config([:rich_media, :enabled], true)
|
setup do
|
||||||
|
clear_config([:rich_media, :enabled], true)
|
||||||
|
|
||||||
|
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Test.StaticConfig)
|
||||||
|
Mox.stub_with(Pleroma.CachexMock, Pleroma.NullCache)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
test "sets a negative cache entry for an error" do
|
test "sets a negative cache entry for an error" do
|
||||||
url = "https://bad.example.com/"
|
url = "https://bad.example.com/"
|
||||||
|
|
@ -23,4 +33,139 @@ defmodule Pleroma.Web.RichMedia.BackfillTest do
|
||||||
|
|
||||||
Backfill.run(%{"url" => url})
|
Backfill.run(%{"url" => url})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "sets a warm_cache entry" do
|
||||||
|
url = "https://good.example.com/"
|
||||||
|
url_hash = Card.url_to_hash(url)
|
||||||
|
|
||||||
|
Tesla.Mock.mock(fn %{url: ^url} ->
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: "<head><meta name=\"twitter:title\" content=\"Cofe\"></head>"
|
||||||
|
}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
Pleroma.CachexMock
|
||||||
|
|> expect(:put, fn :rich_media_cache,
|
||||||
|
^url_hash,
|
||||||
|
%Pleroma.Web.RichMedia.Card{url_hash: ^url_hash} ->
|
||||||
|
{:ok, true}
|
||||||
|
end)
|
||||||
|
|
||||||
|
Backfill.run(%{"url" => url})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "streams out update when stream == true" do
|
||||||
|
url = "https://example.com"
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
Tesla.Mock.mock(fn %{url: ^url} ->
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: "<head><meta name=\"twitter:title\" content=\"Cofe\"></head>"
|
||||||
|
}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{status: "#cofe #{url}"})
|
||||||
|
|
||||||
|
Pleroma.CachexMock
|
||||||
|
|> expect(:put, fn :rich_media_cache, _, _ -> {:ok, true} end)
|
||||||
|
|
||||||
|
Pleroma.Web.ActivityPub.ActivityPubMock
|
||||||
|
|> expect(:stream_out, fn %Pleroma.Activity{id: id} ->
|
||||||
|
assert id == activity.id
|
||||||
|
:ok
|
||||||
|
end)
|
||||||
|
|
||||||
|
Backfill.run(%{"url" => url, "activity_id" => activity.id, "stream" => true})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not stream out update when stream == false" do
|
||||||
|
url = "https://example.com"
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
Tesla.Mock.mock(fn %{url: ^url} ->
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: "<head><meta name=\"twitter:title\" content=\"Cofe\"></head>"
|
||||||
|
}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{status: "#cofe #{url}"})
|
||||||
|
|
||||||
|
Pleroma.CachexMock
|
||||||
|
|> expect(:put, fn :rich_media_cache, _, _ -> {:ok, true} end)
|
||||||
|
|
||||||
|
Pleroma.Web.ActivityPub.ActivityPubMock
|
||||||
|
|> deny(:stream_out, 1)
|
||||||
|
|
||||||
|
Backfill.run(%{"url" => url, "activity_id" => "#{activity.data["id"]}", "stream" => false})
|
||||||
|
end
|
||||||
|
|
||||||
|
# NOTE: Below two MastoAPI tests cover almost the same code paths.
|
||||||
|
# index.json will always prefetch rich media, while show.json will try to get the card and
|
||||||
|
# fetch it when it isn't cached (both use Card.get_by_activity in the end).
|
||||||
|
# So if index.json doesn't fetch the rich media, show.json will when it renders the post,
|
||||||
|
# hence why index.json test will only call ActivityPub.stream_out twice,
|
||||||
|
# if streaming is re-enabled for in both.
|
||||||
|
test "does not stream out in MastoAPI StatusView index" do
|
||||||
|
url = "https://example.com"
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
Tesla.Mock.mock(fn %{url: ^url} ->
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: "<head><meta name=\"twitter:title\" content=\"Cofe\"></head>"
|
||||||
|
}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
# CommonAPI federation processing will stream out once as a new post
|
||||||
|
Pleroma.Web.ActivityPub.ActivityPubMock
|
||||||
|
|> expect(:stream_out, 1, fn _ -> :ok end)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{status: "#cofe #{url}"})
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
# Clear cache to force backfill below
|
||||||
|
Pleroma.Activity.HTML.invalidate_cache_for(activity.id)
|
||||||
|
Pleroma.Web.RichMedia.Card.delete(url)
|
||||||
|
|
||||||
|
Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
|
||||||
|
activities: [activity],
|
||||||
|
as: :activity
|
||||||
|
})
|
||||||
|
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not stream out in MastoAPI StatusView show" do
|
||||||
|
url = "https://example.com"
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
Tesla.Mock.mock(fn %{url: ^url} ->
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: "<head><meta name=\"twitter:title\" content=\"Cofe\"></head>"
|
||||||
|
}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
# CommonAPI federation processing will stream out once as a new post
|
||||||
|
Pleroma.Web.ActivityPub.ActivityPubMock
|
||||||
|
|> expect(:stream_out, 1, fn _ -> :ok end)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{status: "#cofe #{url}"})
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
# Clear cache to force backfill below
|
||||||
|
Pleroma.Activity.HTML.invalidate_cache_for(activity.id)
|
||||||
|
Pleroma.Web.RichMedia.Card.delete(url)
|
||||||
|
|
||||||
|
Pleroma.Web.MastodonAPI.StatusView.render("show.json", activity: activity)
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,15 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
|
||||||
assert html_response(conn, 200) =~ user.nickname
|
assert html_response(conn, 200) =~ user.nickname
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "renders profile HTML inside the default app layout", %{conn: conn, user: user} do
|
||||||
|
conn = get(conn, "/users/#{user.nickname}")
|
||||||
|
|
||||||
|
html = html_response(conn, 200)
|
||||||
|
assert html =~ "<!DOCTYPE html>"
|
||||||
|
assert html =~ ~s(class="instance-header")
|
||||||
|
assert html =~ ~s(<link rel="stylesheet" href="/instance/static.css">)
|
||||||
|
end
|
||||||
|
|
||||||
test "404 when user not found", %{conn: conn} do
|
test "404 when user not found", %{conn: conn} do
|
||||||
conn = get(conn, "/users/limpopo")
|
conn = get(conn, "/users/limpopo")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,13 @@ defmodule Pleroma.Workers.SignatureRetryWorkerTest do
|
||||||
alias Pleroma.Signature
|
alias Pleroma.Signature
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.UserView
|
alias Pleroma.Web.ActivityPub.UserView
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
alias Pleroma.Workers.SignatureRetryWorker
|
alias Pleroma.Workers.SignatureRetryWorker
|
||||||
|
|
||||||
defp signature_headers_for(%User{} = signer) do
|
defp signature_headers_for(%User{} = signer) do
|
||||||
[
|
[
|
||||||
{"host", "local.test"},
|
{"host", "#{URI.parse(Endpoint.url()).host}"},
|
||||||
{"date", "Thu, 25 Jul 2024 13:33:31 GMT"},
|
{"date", "Thu, 25 Jul 2024 13:33:31 GMT"},
|
||||||
{"digest", "SHA-256=fake-digest"},
|
{"digest", "SHA-256=fake-digest"},
|
||||||
{"content-type", "application/activity+json"},
|
{"content-type", "application/activity+json"},
|
||||||
|
|
@ -245,6 +246,66 @@ defmodule Pleroma.Workers.SignatureRetryWorkerTest do
|
||||||
refute Activity.get_by_ap_id(create["id"])
|
refute Activity.get_by_ap_id(create["id"])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "cancels when the Host header does not match Endpoint" do
|
||||||
|
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||||
|
|
||||||
|
create = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => alice.ap_id,
|
||||||
|
"id" => "https://one.com/activities/invalid-signature-create",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => %{
|
||||||
|
"type" => "Note",
|
||||||
|
"id" => "https://one.com/objects/invalid-signature-note",
|
||||||
|
"actor" => alice.ap_id,
|
||||||
|
"attributedTo" => alice.ap_id,
|
||||||
|
"content" => "forged post",
|
||||||
|
"published" => "2024-07-25T13:33:31Z",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect_signature_from(alice)
|
||||||
|
|
||||||
|
headers =
|
||||||
|
[
|
||||||
|
{"host", "invalid.example.com"},
|
||||||
|
{"date", "Thu, 25 Jul 2024 13:33:31 GMT"},
|
||||||
|
{"digest", "SHA-256=fake-digest"},
|
||||||
|
{"content-type", "application/activity+json"},
|
||||||
|
{
|
||||||
|
"signature",
|
||||||
|
"keyId=\"#{alice.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
assert {:ok, oban_job} =
|
||||||
|
Federator.incoming_failed_signature_ap_doc(%{
|
||||||
|
method: "POST",
|
||||||
|
req_headers: headers,
|
||||||
|
request_path: "/inbox",
|
||||||
|
params: create,
|
||||||
|
query_string: ""
|
||||||
|
})
|
||||||
|
|
||||||
|
log =
|
||||||
|
capture_log([level: :warning], fn ->
|
||||||
|
assert {:cancel, :host_header_mismatch} = SignatureRetryWorker.perform(oban_job)
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert log =~ "Failed-signature inbox retry rejected"
|
||||||
|
assert log =~ "reason=:host_header_mismatch"
|
||||||
|
assert log =~ "payload_actor=\"https://one.com/users/alice\""
|
||||||
|
assert log =~ "signature_actor=\"https://one.com/users/alice\""
|
||||||
|
assert log =~ "activity_id=\"https://one.com/activities/invalid-signature-create\""
|
||||||
|
assert log =~ "type=\"Create\""
|
||||||
|
assert log =~ "request_path=\"/inbox\""
|
||||||
|
|
||||||
|
refute Activity.get_by_ap_id(create["id"])
|
||||||
|
end
|
||||||
|
|
||||||
test "processes the activity after refetching a valid matching signature" do
|
test "processes the activity after refetching a valid matching signature" do
|
||||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||||
|
|
||||||
|
|
@ -309,11 +370,11 @@ defmodule Pleroma.Workers.SignatureRetryWorkerTest do
|
||||||
"content-type" => "application/activity+json",
|
"content-type" => "application/activity+json",
|
||||||
date: date,
|
date: date,
|
||||||
digest: digest,
|
digest: digest,
|
||||||
host: "local.test"
|
host: "#{URI.parse(Endpoint.url()).host}"
|
||||||
})
|
})
|
||||||
|
|
||||||
req_headers = [
|
req_headers = [
|
||||||
["host", "local.test"],
|
["host", "#{URI.parse(Endpoint.url()).host}"],
|
||||||
["date", date],
|
["date", date],
|
||||||
["digest", digest],
|
["digest", digest],
|
||||||
["content-type", "application/activity+json"],
|
["content-type", "application/activity+json"],
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,10 @@ defmodule Pleroma.Web.ConnCase do
|
||||||
DataCase.stub_pipeline()
|
DataCase.stub_pipeline()
|
||||||
|
|
||||||
Mox.verify_on_exit!()
|
Mox.verify_on_exit!()
|
||||||
|
endpoint = URI.parse(Pleroma.Web.Endpoint.url())
|
||||||
|
conn = Phoenix.ConnTest.build_conn()
|
||||||
|
conn = %{conn | req_headers: [{"host", "#{endpoint.host}:#{endpoint.port}"}]}
|
||||||
|
|
||||||
{:ok, conn: Phoenix.ConnTest.build_conn()}
|
{:ok, conn: conn}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue