From d0c1997d483f918b46bdf45cecef82d8aabcb5f1 Mon Sep 17 00:00:00 2001 From: Sean King Date: Sat, 19 Mar 2022 23:33:37 -0600 Subject: [PATCH 01/31] Rewrite integration-test websocket client with Mint.WebSocket --- mix.exs | 3 +- mix.lock | 1 + .../integration/mastodon_websocket_test.exs | 16 +- test/support/websocket_client.ex | 195 +++++++++++++++--- 4 files changed, 178 insertions(+), 37 deletions(-) diff --git a/mix.exs b/mix.exs index 7893b8438c..f84191321e 100644 --- a/mix.exs +++ b/mix.exs @@ -208,7 +208,8 @@ defp deps do {:excoveralls, "0.12.3", only: :test}, {:hackney, "~> 1.18.0", override: true}, {:mox, "~> 1.0", only: :test}, - {:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test} + {:mint, "~> 1.4", only: :test, override: true}, + {:mint_web_socket, "~> 0.3.0", only: :test} ] ++ oauth_deps() end diff --git a/mix.lock b/mix.lock index 817240538a..81f7e43991 100644 --- a/mix.lock +++ b/mix.lock @@ -79,6 +79,7 @@ "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mint": {:hex, :mint, "1.4.0", "cd7d2451b201fc8e4a8fd86257fb3878d9e3752899eb67b0c5b25b180bde1212", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "10a99e144b815cbf8522dccbc8199d15802440fc7a64d67b6853adb6fa170217"}, + "mint_web_socket": {:hex, :mint_web_socket, "0.3.0", "c9e130dcc778d673fd713eb66434e16cf7d89cee0754e75f26f8bd9a9e592b63", [:mix], [{:mint, "~> 1.4 and >= 1.4.1", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "0605bc3fa684e1a7719b22a3f74be4de5e6a16dd43ac18ebcea72e2adc33b532"}, "mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"}, "mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"}, "mogrify": {:hex, :mogrify, "0.9.1", "a26f107c4987477769f272bd0f7e3ac4b7b75b11ba597fd001b877beffa9c068", [:mix], [], "hexpm", "134edf189337d2125c0948bf0c228fdeef975c594317452d536224069a5b7f05"}, diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs index 2d4c7f63b2..5599ce0306 100644 --- a/test/pleroma/integration/mastodon_websocket_test.exs +++ b/test/pleroma/integration/mastodon_websocket_test.exs @@ -28,21 +28,21 @@ def start_socket(qs \\ nil, headers \\ []) do qs -> @path <> qs end - WebsocketClient.start_link(self(), path, headers) + WebsocketClient.connect(self(), path, headers) end test "refuses invalid requests" do capture_log(fn -> - assert {:error, {404, _}} = start_socket() - assert {:error, {404, _}} = start_socket("?stream=ncjdk") + assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 404}} = start_socket() + assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 404}} = start_socket("?stream=ncjdk") Process.sleep(30) end) end test "requires authentication and a valid token for protected streams" do capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") - assert {:error, {401, _}} = start_socket("?stream=user") + assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") + assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = start_socket("?stream=user") Process.sleep(30) end) end @@ -102,7 +102,7 @@ test "accepts the 'user' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}") capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user") + assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = start_socket("?stream=user") Process.sleep(30) end) end @@ -111,7 +111,7 @@ test "accepts the 'user:notification' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}") capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user:notification") + assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = start_socket("?stream=user:notification") Process.sleep(30) end) end @@ -120,7 +120,7 @@ test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}]) capture_log(fn -> - assert {:error, {401, _}} = + assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) Process.sleep(30) diff --git a/test/support/websocket_client.ex b/test/support/websocket_client.ex index d149b324ec..afcd0e8c70 100644 --- a/test/support/websocket_client.ex +++ b/test/support/websocket_client.ex @@ -3,60 +3,199 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Integration.WebsocketClient do - # https://github.com/phoenixframework/phoenix/blob/master/test/support/websocket_client.exs + @moduledoc """ + A WebSocket client used to test Mastodon API streaming + + Based on Phoenix Framework's WebsocketClient + https://github.com/phoenixframework/phoenix/blob/master/test/support/websocket_client.exs + """ + + use GenServer + import Kernel, except: [send: 2] + + defstruct [ + :conn, + :request_ref, + :websocket, + :caller, + :status, + :resp_headers, + :sender, + closing?: false + ] @doc """ - Starts the WebSocket server for given ws URL. Received Socket.Message's - are forwarded to the sender pid + Starts the WebSocket client for given ws URL. `Phoenix.Socket.Message`s + received from the server are forwarded to the sender pid. """ - def start_link(sender, url, headers \\ []) do - :crypto.start() - :ssl.start() - - :websocket_client.start_link( - String.to_charlist(url), - __MODULE__, - [sender], - extra_headers: headers - ) + def connect(sender, url, headers \\ []) do + with {:ok, socket} <- GenServer.start_link(__MODULE__, {sender}), + {:ok, :connected} <- GenServer.call(socket, {:connect, url, headers}) do + {:ok, socket} + end end @doc """ Closes the socket """ def close(socket) do - send(socket, :close) + GenServer.cast(socket, :close) end @doc """ Sends a low-level text message to the client. """ def send_text(server_pid, msg) do - send(server_pid, {:text, msg}) + GenServer.call(server_pid, {:text, msg}) end @doc false - def init([sender], _conn_state) do - {:ok, %{sender: sender}} - end + def init({sender}) do + state = %__MODULE__{sender: sender} - @doc false - def websocket_handle(frame, _conn_state, state) do - send(state.sender, frame) {:ok, state} end @doc false - def websocket_info({:text, msg}, _conn_state, state) do - {:reply, {:text, msg}, state} - end + def handle_call({:connect, url, headers}, from, state) do + uri = URI.parse(url) - def websocket_info(:close, _conn_state, _state) do - {:close, <<>>, "done"} + http_scheme = + case uri.scheme do + "ws" -> :http + "wss" -> :https + end + + ws_scheme = + case uri.scheme do + "ws" -> :ws + "wss" -> :wss + end + + path = + case uri.query do + nil -> uri.path + query -> uri.path <> "?" <> query + end + + with {:ok, conn} <- Mint.HTTP.connect(http_scheme, uri.host, uri.port), + {:ok, conn, ref} <- Mint.WebSocket.upgrade(ws_scheme, conn, path, headers) do + state = %{state | conn: conn, request_ref: ref, caller: from} + {:noreply, state} + else + {:error, reason} -> + {:reply, {:error, reason}, state} + + {:error, conn, reason} -> + {:reply, {:error, reason}, put_in(state.conn, conn)} + end end @doc false - def websocket_terminate(_reason, _conn_state, _state) do - :ok + def handle_info(message, state) do + case Mint.WebSocket.stream(state.conn, message) do + {:ok, conn, responses} -> + state = put_in(state.conn, conn) |> handle_responses(responses) + if state.closing?, do: do_close(state), else: {:noreply, state} + + {:error, conn, reason, _responses} -> + state = put_in(state.conn, conn) |> reply({:error, reason}) + {:noreply, state} + + :unknown -> + {:noreply, state} + end + end + + defp do_close(state) do + # Streaming a close frame may fail if the server has already closed + # for writing. + _ = stream_frame(state, :close) + Mint.HTTP.close(state.conn) + {:stop, :normal, state} + end + + defp handle_responses(state, responses) + + defp handle_responses(%{request_ref: ref} = state, [{:status, ref, status} | rest]) do + put_in(state.status, status) + |> handle_responses(rest) + end + + defp handle_responses(%{request_ref: ref} = state, [{:headers, ref, resp_headers} | rest]) do + put_in(state.resp_headers, resp_headers) + |> handle_responses(rest) + end + + defp handle_responses(%{request_ref: ref} = state, [{:done, ref} | rest]) do + case Mint.WebSocket.new(state.conn, ref, state.status, state.resp_headers) do + {:ok, conn, websocket} -> + %{state | conn: conn, websocket: websocket, status: nil, resp_headers: nil} + |> reply({:ok, :connected}) + |> handle_responses(rest) + + {:error, conn, reason} -> + put_in(state.conn, conn) + |> reply({:error, reason}) + end + end + + defp handle_responses(%{request_ref: ref, websocket: websocket} = state, [ + {:data, ref, data} | rest + ]) + when websocket != nil do + case Mint.WebSocket.decode(websocket, data) do + {:ok, websocket, frames} -> + put_in(state.websocket, websocket) + |> handle_frames(frames) + |> handle_responses(rest) + + {:error, websocket, reason} -> + put_in(state.websocket, websocket) + |> reply({:error, reason}) + end + end + + defp handle_responses(state, [_response | rest]) do + handle_responses(state, rest) + end + + defp handle_responses(state, []), do: state + + defp handle_frames(state, frames) do + {frames, state} = + Enum.flat_map_reduce(frames, state, fn + # prepare to close the connection when a close frame is received + {:close, _code, _data}, state -> + {[], put_in(state.closing?, true)} + + frame, state -> + {[frame], state} + end) + + Enum.each(frames, &Kernel.send(state.sender, &1)) + + state + end + + defp reply(state, response) do + if state.caller, do: GenServer.reply(state.caller, response) + put_in(state.caller, nil) + end + + # Encodes a frame as a binary and sends it along the wire, keeping `conn` + # and `websocket` up to date in `state`. + defp stream_frame(state, frame) do + with {:ok, websocket, data} <- Mint.WebSocket.encode(state.websocket, frame), + state = put_in(state.websocket, websocket), + {:ok, conn} <- Mint.WebSocket.stream_request_body(state.conn, state.request_ref, data) do + {:ok, put_in(state.conn, conn)} + else + {:error, %Mint.WebSocket{} = websocket, reason} -> + {:error, put_in(state.websocket, websocket), reason} + + {:error, conn, reason} -> + {:error, put_in(state.conn, conn), reason} + end end end From 4194559ea6d3e0f219ae8e77b468782ac115d134 Mon Sep 17 00:00:00 2001 From: Sean King Date: Sun, 20 Mar 2022 17:26:07 -0600 Subject: [PATCH 02/31] Fix lint errors --- .../integration/mastodon_websocket_test.exs | 21 ++++++++++++++----- test/support/websocket_client.ex | 4 ++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs index 5599ce0306..16525c7400 100644 --- a/test/pleroma/integration/mastodon_websocket_test.exs +++ b/test/pleroma/integration/mastodon_websocket_test.exs @@ -34,15 +34,22 @@ def start_socket(qs \\ nil, headers \\ []) do test "refuses invalid requests" do capture_log(fn -> assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 404}} = start_socket() - assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 404}} = start_socket("?stream=ncjdk") + + assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 404}} = + start_socket("?stream=ncjdk") + Process.sleep(30) end) end test "requires authentication and a valid token for protected streams" do capture_log(fn -> - assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") - assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = start_socket("?stream=user") + assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = + start_socket("?stream=user&access_token=aaaaaaaaaaaa") + + assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = + start_socket("?stream=user") + Process.sleep(30) end) end @@ -102,7 +109,9 @@ test "accepts the 'user' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}") capture_log(fn -> - assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = start_socket("?stream=user") + assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = + start_socket("?stream=user") + Process.sleep(30) end) end @@ -111,7 +120,9 @@ test "accepts the 'user:notification' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}") capture_log(fn -> - assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = start_socket("?stream=user:notification") + assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = + start_socket("?stream=user:notification") + Process.sleep(30) end) end diff --git a/test/support/websocket_client.ex b/test/support/websocket_client.ex index afcd0e8c70..43f2854de4 100644 --- a/test/support/websocket_client.ex +++ b/test/support/websocket_client.ex @@ -188,8 +188,8 @@ defp reply(state, response) do defp stream_frame(state, frame) do with {:ok, websocket, data} <- Mint.WebSocket.encode(state.websocket, frame), state = put_in(state.websocket, websocket), - {:ok, conn} <- Mint.WebSocket.stream_request_body(state.conn, state.request_ref, data) do - {:ok, put_in(state.conn, conn)} + {:ok, conn} <- Mint.WebSocket.stream_request_body(state.conn, state.request_ref, data) do + {:ok, put_in(state.conn, conn)} else {:error, %Mint.WebSocket{} = websocket, reason} -> {:error, put_in(state.websocket, websocket), reason} From e606b9ab3f5028ddac1f52e855d7f6da4999dbc5 Mon Sep 17 00:00:00 2001 From: duponin Date: Wed, 18 May 2022 20:05:42 +0200 Subject: [PATCH 03/31] add missing extra application to start the SSH BBS --- mix.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 0651781ccf..1df92ed530 100644 --- a/mix.exs +++ b/mix.exs @@ -80,7 +80,8 @@ def application do :quack, :fast_sanitize, :os_mon, - :ssl + :ssl, + :esshd ], included_applications: [:ex_syslogger] ] From 39c47073a3c6fd3da068d5a4c9def18f3847ff32 Mon Sep 17 00:00:00 2001 From: duponin Date: Wed, 18 May 2022 20:06:16 +0200 Subject: [PATCH 04/31] fix Ctrl-c catch on SSH BBS --- lib/pleroma/bbs/handler.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index a3b623bdfb..47f5a920e9 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -123,7 +123,7 @@ defp wait_input(state, input) do loop(%{state | counter: state.counter + 1}) - {:error, :interrupted} -> + {:input, ^input, {:error, :interrupted}} -> IO.puts("Caught Ctrl+C...") loop(%{state | counter: state.counter + 1}) From 5086d6d5e9ff68d6a7a82fd3ad6dbc0bad0b599c Mon Sep 17 00:00:00 2001 From: duponin Date: Thu, 19 May 2022 00:56:20 +0200 Subject: [PATCH 05/31] add thread show in BBS frontend --- lib/pleroma/bbs/handler.ex | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index 47f5a920e9..f1ac0c687f 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -53,6 +53,7 @@ def handle_command(state, "help") do IO.puts("home - Show the home timeline") IO.puts("p - Post the given text") IO.puts("r - Reply to the post with the given id") + IO.puts("t - Show a thread from the given id") IO.puts("quit - Quit") state @@ -73,6 +74,33 @@ def handle_command(%{user: user} = state, "r " <> text) do state end + def handle_command(%{user: user} = state, "t " <> activity_id) do + with %Activity{} = activity <- Activity.get_by_id(activity_id) do + activities = + ActivityPub.fetch_activities_for_context(activity.data["context"], %{ + blocking_user: user, + user: user, + exclude_id: activity.id + }) + + case activities do + [] -> + activity_id + |> Activity.get_by_id() + |> puts_activity() + + _ -> + activities + |> Enum.reverse() + |> Enum.each(&puts_activity/1) + end + else + _e -> IO.puts("An error occured when trying to show the thread...") + end + + state + end + def handle_command(%{user: user} = state, "p " <> text) do text = String.trim(text) From b128e1d6c5bbc78874d05af2676550de80ae85c7 Mon Sep 17 00:00:00 2001 From: duponin Date: Thu, 19 May 2022 01:38:13 +0200 Subject: [PATCH 06/31] decode HTML to be human readable in BBS --- lib/pleroma/bbs/handler.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index f1ac0c687f..c2491a20c6 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -43,7 +43,7 @@ defp loop(state) do def puts_activity(activity) do status = Pleroma.Web.MastodonAPI.StatusView.render("show.json", %{activity: activity}) IO.puts("-- #{status.id} by #{status.account.display_name} (#{status.account.acct})") - IO.puts(HTML.strip_tags(status.content)) + IO.puts(status.content |> HTML.strip_tags() |> HtmlEntities.decode()) IO.puts("") end From 33ced2c2ed9391ec95aae2205bb30d987ceac86d Mon Sep 17 00:00:00 2001 From: duponin Date: Sat, 21 May 2022 04:17:34 +0200 Subject: [PATCH 07/31] BBS: put a new line for each HTML break in an activity Otherwise it would just put each line on the first one, which is not really readable --- lib/pleroma/bbs/handler.ex | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index c2491a20c6..d641de9ac6 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -42,9 +42,14 @@ defp loop(state) do def puts_activity(activity) do status = Pleroma.Web.MastodonAPI.StatusView.render("show.json", %{activity: activity}) + IO.puts("-- #{status.id} by #{status.account.display_name} (#{status.account.acct})") - IO.puts(status.content |> HTML.strip_tags() |> HtmlEntities.decode()) - IO.puts("") + + status.content + |> String.split("
") + |> Enum.map(&HTML.strip_tags/1) + |> Enum.map(&HtmlEntities.decode/1) + |> Enum.map(&IO.puts/1) end def handle_command(state, "help") do From c04c7f9e45eec680afc0bf6c145fa55fc3f56ea8 Mon Sep 17 00:00:00 2001 From: duponin Date: Sat, 21 May 2022 05:10:22 +0200 Subject: [PATCH 08/31] BBS: show notifactions --- lib/pleroma/bbs/handler.ex | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index d641de9ac6..e0174efe16 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -52,6 +52,40 @@ def puts_activity(activity) do |> Enum.map(&IO.puts/1) end + def puts_notification(activity, user) do + notification = + Pleroma.Web.MastodonAPI.NotificationView.render("show.json", %{ + notification: activity, + for: user + }) + + IO.puts( + "== (#{notification.type}) #{notification.status.id} by #{notification.account.display_name} (#{notification.account.acct})" + ) + + notification.status.content + |> String.split("
") + |> Enum.map(&HTML.strip_tags/1) + |> Enum.map(&HtmlEntities.decode/1) + |> (fn x -> + case x do + [content] -> + "> " <> content + + [head | _tail] -> + # "> " <> hd <> "..." + head + |> String.to_charlist() + |> Enum.take(80) + |> List.to_string() + |> (fn x -> "> " <> x <> "..." end).() + end + end).() + |> IO.puts() + + IO.puts("") + end + def handle_command(state, "help") do IO.puts("Available commands:") IO.puts("help - This help") @@ -59,6 +93,7 @@ def handle_command(state, "help") do IO.puts("p - Post the given text") IO.puts("r - Reply to the post with the given id") IO.puts("t - Show a thread from the given id") + IO.puts("n - Show notifications") IO.puts("quit - Quit") state @@ -106,6 +141,14 @@ def handle_command(%{user: user} = state, "t " <> activity_id) do state end + def handle_command(%{user: user} = state, "n") do + user + |> Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(%{}) + |> Enum.each(&puts_notification(&1, user)) + + state + end + def handle_command(%{user: user} = state, "p " <> text) do text = String.trim(text) From e3e8ff06f9c588563003ba9855f2d38b9d6e08b7 Mon Sep 17 00:00:00 2001 From: duponin Date: Sat, 21 May 2022 05:10:48 +0200 Subject: [PATCH 09/31] BBS: mark notification as read --- lib/pleroma/bbs/handler.ex | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index e0174efe16..7314453af8 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -94,6 +94,7 @@ def handle_command(state, "help") do IO.puts("r - Reply to the post with the given id") IO.puts("t - Show a thread from the given id") IO.puts("n - Show notifications") + IO.puts("n read - Mark all notifactions as read") IO.puts("quit - Quit") state @@ -141,6 +142,13 @@ def handle_command(%{user: user} = state, "t " <> activity_id) do state end + def handle_command(%{user: user} = state, "n read") do + Pleroma.Notification.clear(user) + IO.puts("All notifications are marked as read") + + state + end + def handle_command(%{user: user} = state, "n") do user |> Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(%{}) From a4659d993d1493406e9df4a26ada35cba50511c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Sat, 21 May 2022 23:23:55 +0000 Subject: [PATCH 10/31] =?UTF-8?q?Apply=20H=C3=A9l=C3=A8ne=20suggestions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pleroma/bbs/handler.ex | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index 7314453af8..a8f2fd37b4 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -75,9 +75,7 @@ def puts_notification(activity, user) do [head | _tail] -> # "> " <> hd <> "..." head - |> String.to_charlist() - |> Enum.take(80) - |> List.to_string() + |> String.slice(1, 80) |> (fn x -> "> " <> x <> "..." end).() end end).() @@ -136,7 +134,7 @@ def handle_command(%{user: user} = state, "t " <> activity_id) do |> Enum.each(&puts_activity/1) end else - _e -> IO.puts("An error occured when trying to show the thread...") + _e -> IO.puts("Could not show this thread...") end state @@ -144,7 +142,7 @@ def handle_command(%{user: user} = state, "t " <> activity_id) do def handle_command(%{user: user} = state, "n read") do Pleroma.Notification.clear(user) - IO.puts("All notifications are marked as read") + IO.puts("All notifications were marked as read") state end From fffd9059d67fb719c38dc014de1fa750dd5be8b4 Mon Sep 17 00:00:00 2001 From: duponin Date: Sun, 22 May 2022 02:39:38 +0200 Subject: [PATCH 11/31] BBS: add post favourite feature --- lib/pleroma/bbs/handler.ex | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index a8f2fd37b4..631307f025 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -93,6 +93,7 @@ def handle_command(state, "help") do IO.puts("t - Show a thread from the given id") IO.puts("n - Show notifications") IO.puts("n read - Mark all notifactions as read") + IO.puts("f - Favourites the post with the given id") IO.puts("quit - Quit") state @@ -167,6 +168,19 @@ def handle_command(%{user: user} = state, "p " <> text) do state end + def handle_command(%{user: user} = state, "f " <> id) do + id = String.trim(id) + + with %Activity{} = activity <- Activity.get_by_id(id), + {:ok, _activity} <- CommonAPI.favorite(user, activity) do + IO.puts("Favourited!") + else + _e -> IO.puts("Could not Favourite...") + end + + state + end + def handle_command(state, "home") do user = state.user From 5951d637a98402ad0e1d11d220c9374fc02d5bcd Mon Sep 17 00:00:00 2001 From: duponin Date: Sun, 22 May 2022 02:40:56 +0200 Subject: [PATCH 12/31] BBS: show post ID when posted --- lib/pleroma/bbs/handler.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index 631307f025..fecabb8782 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -159,8 +159,8 @@ def handle_command(%{user: user} = state, "n") do def handle_command(%{user: user} = state, "p " <> text) do text = String.trim(text) - with {:ok, _activity} <- CommonAPI.post(user, %{status: text}) do - IO.puts("Posted!") + with {:ok, activity} <- CommonAPI.post(user, %{status: text}) do + IO.puts("Posted! ID: #{activity.id}") else _e -> IO.puts("Could not post...") end From 5ca1ac041f011df458af7ebe057b39c1cc9548d0 Mon Sep 17 00:00:00 2001 From: duponin Date: Sun, 22 May 2022 03:19:24 +0200 Subject: [PATCH 13/31] BBS: add repeat functionality --- lib/pleroma/bbs/handler.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index fecabb8782..27799338ff 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -94,6 +94,7 @@ def handle_command(state, "help") do IO.puts("n - Show notifications") IO.puts("n read - Mark all notifactions as read") IO.puts("f - Favourites the post with the given id") + IO.puts("R - Repeat the post with the given id") IO.puts("quit - Quit") state From 06678fb4ad42fcaecb99eccc2237c3b863a2b9a5 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 11 Jul 2022 14:58:38 -0400 Subject: [PATCH 14/31] Add function to calculate associated object id --- ...2322_add_associated_object_id_function.exs | 37 ++++++++++ test/pleroma/activity_test.exs | 74 +++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 priv/repo/migrations/20220711182322_add_associated_object_id_function.exs diff --git a/priv/repo/migrations/20220711182322_add_associated_object_id_function.exs b/priv/repo/migrations/20220711182322_add_associated_object_id_function.exs new file mode 100644 index 0000000000..76348f31a3 --- /dev/null +++ b/priv/repo/migrations/20220711182322_add_associated_object_id_function.exs @@ -0,0 +1,37 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.AddAssociatedObjectIdFunction do + use Ecto.Migration + + def up do + statement = """ + CREATE OR REPLACE FUNCTION associated_object_id(data jsonb) RETURNS varchar AS $$ + DECLARE + object_data jsonb; + BEGIN + IF jsonb_typeof(data->'object') = 'array' THEN + object_data := data->'object'->0; + ELSE + object_data := data->'object'; + END IF; + + IF jsonb_typeof(object_data->'id') = 'string' THEN + RETURN object_data->>'id'; + ELSIF jsonb_typeof(object_data) = 'string' THEN + RETURN object_data#>>'{}'; + ELSE + RETURN NULL; + END IF; + END; + $$ LANGUAGE plpgsql IMMUTABLE; + """ + + execute(statement) + end + + def down do + execute("DROP FUNCTION IF EXISTS associated_object_id(data jsonb)") + end +end diff --git a/test/pleroma/activity_test.exs b/test/pleroma/activity_test.exs index b5bb4bafef..e38384c9c8 100644 --- a/test/pleroma/activity_test.exs +++ b/test/pleroma/activity_test.exs @@ -278,4 +278,78 @@ test "add_by_params_query/3" do assert Repo.aggregate(Activity, :count, :id) == 2 end + + describe "associated_object_id() sql function" do + test "with json object" do + %{rows: [[object_id]]} = + Ecto.Adapters.SQL.query!( + Pleroma.Repo, + """ + select associated_object_id('{"object": {"id":"foobar"}}'::jsonb); + """ + ) + + assert object_id == "foobar" + end + + test "with string object" do + %{rows: [[object_id]]} = + Ecto.Adapters.SQL.query!( + Pleroma.Repo, + """ + select associated_object_id('{"object": "foobar"}'::jsonb); + """ + ) + + assert object_id == "foobar" + end + + test "with array object" do + %{rows: [[object_id]]} = + Ecto.Adapters.SQL.query!( + Pleroma.Repo, + """ + select associated_object_id('{"object": ["foobar", {}]}'::jsonb); + """ + ) + + assert object_id == "foobar" + end + + test "invalid" do + %{rows: [[object_id]]} = + Ecto.Adapters.SQL.query!( + Pleroma.Repo, + """ + select associated_object_id('{"object": {}}'::jsonb); + """ + ) + + assert is_nil(object_id) + end + + test "invalid object id" do + %{rows: [[object_id]]} = + Ecto.Adapters.SQL.query!( + Pleroma.Repo, + """ + select associated_object_id('{"object": {"id": 123}}'::jsonb); + """ + ) + + assert is_nil(object_id) + end + + test "no object field" do + %{rows: [[object_id]]} = + Ecto.Adapters.SQL.query!( + Pleroma.Repo, + """ + select associated_object_id('{}'::jsonb); + """ + ) + + assert is_nil(object_id) + end + end end From 3885ee182a572a10b326ae553703ee0d38f3b66d Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 11 Jul 2022 15:49:58 -0400 Subject: [PATCH 15/31] Switch to associated_object_id index --- lib/mix/tasks/pleroma/database.ex | 3 +- lib/pleroma/activity.ex | 5 +-- lib/pleroma/activity/queries.ex | 6 +-- .../migrators/hashtags_table_migrator.ex | 2 +- lib/pleroma/notification.ex | 9 ++--- lib/pleroma/object.ex | 3 +- lib/pleroma/web/activity_pub/activity_pub.ex | 3 +- ...0_switch_to_associated_object_id_index.exs | 39 +++++++++++++++++++ 8 files changed, 50 insertions(+), 20 deletions(-) create mode 100644 priv/repo/migrations/20220711192750_switch_to_associated_object_id_index.exs diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index 6b8f0ef689..ed560c1770 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -154,9 +154,8 @@ def run(["ensure_expiration"]) do |> join(:inner, [a], o in Object, on: fragment( - "(?->>'id') = COALESCE((?)->'object'->> 'id', (?)->>'object')", + "(?->>'id') = associated_object_id((?))", o.data, - a.data, a.data ) ) diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 12c1a3b2ed..ebfd4ed45f 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -53,7 +53,7 @@ defmodule Pleroma.Activity do # # ``` # |> join(:inner, [activity], o in Object, - # on: fragment("(?->>'id') = COALESCE((?)->'object'->> 'id', (?)->>'object')", + # on: fragment("(?->>'id') = associated_object_id((?))", # o.data, activity.data, activity.data)) # |> preload([activity, object], [object: object]) # ``` @@ -69,9 +69,8 @@ def with_joined_object(query, join_type \\ :inner) do join(query, join_type, [activity], o in Object, on: fragment( - "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", + "(?->>'id') = associated_object_id(?)", o.data, - activity.data, activity.data ), as: :object diff --git a/lib/pleroma/activity/queries.ex b/lib/pleroma/activity/queries.ex index a898b2ea7a..81c44ac055 100644 --- a/lib/pleroma/activity/queries.ex +++ b/lib/pleroma/activity/queries.ex @@ -52,8 +52,7 @@ def by_object_id(query, object_ids) when is_list(object_ids) do activity in query, where: fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ANY(?)", - activity.data, + "associated_object_id((?)) = ANY(?)", activity.data, ^object_ids ) @@ -64,8 +63,7 @@ def by_object_id(query, object_id) when is_binary(object_id) do from(activity in query, where: fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ?", - activity.data, + "associated_object_id((?)) = ?", activity.data, ^object_id ) diff --git a/lib/pleroma/migrators/hashtags_table_migrator.ex b/lib/pleroma/migrators/hashtags_table_migrator.ex index fa1190b7dc..dca4bfa6ff 100644 --- a/lib/pleroma/migrators/hashtags_table_migrator.ex +++ b/lib/pleroma/migrators/hashtags_table_migrator.ex @@ -183,7 +183,7 @@ def delete_non_create_activities_hashtags do DELETE FROM hashtags_objects WHERE object_id IN (SELECT DISTINCT objects.id FROM objects JOIN hashtags_objects ON hashtags_objects.object_id = objects.id LEFT JOIN activities - ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = + ON associated_object_id(activities) = (objects.data->>'id') AND activities.data->>'type' = 'Create' WHERE activities.id IS NULL); diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 52fd2656b8..76d2d5ecec 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -117,9 +117,8 @@ def for_user_query(user, opts \\ %{}) do |> join(:left, [n, a], object in Object, on: fragment( - "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", + "(?->>'id') = associated_object_id(?)", object.data, - a.data, a.data ) ) @@ -193,13 +192,11 @@ defp exclude_visibility(query, %{exclude_visibilities: visibility}) |> join(:left, [n, a], mutated_activity in Pleroma.Activity, on: fragment( - "COALESCE((?->'object')->>'id', ?->>'object')", - a.data, + "associated_object_id(?)", a.data ) == fragment( - "COALESCE((?->'object')->>'id', ?->>'object')", - mutated_activity.data, + "associated_object_id(?)", mutated_activity.data ) and fragment("(?->>'type' = 'Like' or ?->>'type' = 'Announce')", a.data, a.data) and diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index fe264b5e0b..e7d0d52b06 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -40,8 +40,7 @@ def with_joined_activity(query, activity_type \\ "Create", join_type \\ :inner) join(query, join_type, [{object, object_position}], a in Activity, on: fragment( - "COALESCE(?->'object'->>'id', ?->>'object') = (? ->> 'id') AND (?->>'type' = ?) ", - a.data, + "associated_object_id(?) = (? ->> 'id') AND (?->>'type' = ?) ", a.data, object.data, a.data, diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index bded254c6d..07b0a92a42 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1150,8 +1150,7 @@ defp restrict_pinned(query, %{pinned: true, pinned_object_ids: ids}) do [activity, object: o] in query, where: fragment( - "(?)->>'type' = 'Create' and coalesce((?)->'object'->>'id', (?)->>'object') = any (?)", - activity.data, + "(?)->>'type' = 'Create' and associated_object_id((?)) = any (?)", activity.data, activity.data, ^ids diff --git a/priv/repo/migrations/20220711192750_switch_to_associated_object_id_index.exs b/priv/repo/migrations/20220711192750_switch_to_associated_object_id_index.exs new file mode 100644 index 0000000000..c0b89731bd --- /dev/null +++ b/priv/repo/migrations/20220711192750_switch_to_associated_object_id_index.exs @@ -0,0 +1,39 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.SwitchToAssociatedObjectIdIndex do + use Ecto.Migration + @disable_ddl_transaction true + @disable_migration_lock true + + def up do + drop_if_exists( + index(:activities, ["(coalesce(data->'object'->>'id', data->>'object'))"], + name: :activities_create_objects_index + ) + ) + + create( + index(:activities, ["associated_object_id(data)"], + name: :activities_create_objects_index, + concurrently: true + ) + ) + end + + def down do + drop_if_exists( + index(:activities, ["associated_object_id(data)"], + name: :activities_create_objects_index + ) + ) + + create( + index(:activities, ["(coalesce(data->'object'->>'id', data->>'object'))"], + name: :activities_create_objects_index, + concurrently: true + ) + ) + end +end From 4e7ed563c050e3781990e6c62ea5996d61b63d37 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 11 Jul 2022 16:24:38 -0400 Subject: [PATCH 16/31] Lint --- .../20220711192750_switch_to_associated_object_id_index.exs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/priv/repo/migrations/20220711192750_switch_to_associated_object_id_index.exs b/priv/repo/migrations/20220711192750_switch_to_associated_object_id_index.exs index c0b89731bd..75c1cd40bb 100644 --- a/priv/repo/migrations/20220711192750_switch_to_associated_object_id_index.exs +++ b/priv/repo/migrations/20220711192750_switch_to_associated_object_id_index.exs @@ -24,9 +24,7 @@ def up do def down do drop_if_exists( - index(:activities, ["associated_object_id(data)"], - name: :activities_create_objects_index - ) + index(:activities, ["associated_object_id(data)"], name: :activities_create_objects_index) ) create( From f047088a937ddf95d5fd7f84ad69fd97decbffc0 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 20 Aug 2022 21:06:12 -0400 Subject: [PATCH 17/31] Update thread visibility function --- ..._visibility_to_use_new_object_id_index.exs | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 priv/repo/migrations/20220821004840_change_thread_visibility_to_use_new_object_id_index.exs diff --git a/priv/repo/migrations/20220821004840_change_thread_visibility_to_use_new_object_id_index.exs b/priv/repo/migrations/20220821004840_change_thread_visibility_to_use_new_object_id_index.exs new file mode 100644 index 0000000000..bb56843cb9 --- /dev/null +++ b/priv/repo/migrations/20220821004840_change_thread_visibility_to_use_new_object_id_index.exs @@ -0,0 +1,156 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.ChangeThreadVisibilityToUseNewObjectIdIndex do + use Ecto.Migration + + def up do + execute(update_thread_visibility()) + end + + def down do + execute(restore_thread_visibility()) + end + + def update_thread_visibility do + """ + CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar, local_public varchar default '') RETURNS boolean AS $$ + DECLARE + public varchar := 'https://www.w3.org/ns/activitystreams#Public'; + child objects%ROWTYPE; + activity activities%ROWTYPE; + author_fa varchar; + valid_recipients varchar[]; + actor_user_following varchar[]; + BEGIN + --- Fetch actor following + SELECT array_agg(following.follower_address) INTO actor_user_following FROM following_relationships + JOIN users ON users.id = following_relationships.follower_id + JOIN users AS following ON following.id = following_relationships.following_id + WHERE users.ap_id = actor; + + --- Fetch our initial activity. + SELECT * INTO activity FROM activities WHERE activities.data->>'id' = activity_id; + + LOOP + --- Ensure that we have an activity before continuing. + --- If we don't, the thread is not satisfiable. + IF activity IS NULL THEN + RETURN false; + END IF; + + --- We only care about Create activities. + IF activity.data->>'type' != 'Create' THEN + RETURN true; + END IF; + + --- Normalize the child object into child. + SELECT * INTO child FROM objects + INNER JOIN activities ON associated_object_id(activities.data) = objects.data->>'id' + WHERE associated_object_id(activity.data) = objects.data->>'id'; + + --- Fetch the author's AS2 following collection. + SELECT COALESCE(users.follower_address, '') INTO author_fa FROM users WHERE users.ap_id = activity.actor; + + --- Prepare valid recipients array. + valid_recipients := ARRAY[actor, public]; + --- If we specified local public, add it. + IF local_public <> '' THEN + valid_recipients := valid_recipients || local_public; + END IF; + IF ARRAY[author_fa] && actor_user_following THEN + valid_recipients := valid_recipients || author_fa; + END IF; + + --- Check visibility. + IF NOT valid_recipients && activity.recipients THEN + --- activity not visible, break out of the loop + RETURN false; + END IF; + + --- If there's a parent, load it and do this all over again. + IF (child.data->'inReplyTo' IS NOT NULL) AND (child.data->'inReplyTo' != 'null'::jsonb) THEN + SELECT * INTO activity FROM activities + INNER JOIN objects ON associated_object_id(activities.data) = objects.data->>'id' + WHERE child.data->>'inReplyTo' = objects.data->>'id'; + ELSE + RETURN true; + END IF; + END LOOP; + END; + $$ LANGUAGE plpgsql IMMUTABLE; + """ + end + + # priv/repo/migrations/20220509180452_change_thread_visibility_to_be_local_only_aware.exs + def restore_thread_visibility do + """ + CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar, local_public varchar default '') RETURNS boolean AS $$ + DECLARE + public varchar := 'https://www.w3.org/ns/activitystreams#Public'; + child objects%ROWTYPE; + activity activities%ROWTYPE; + author_fa varchar; + valid_recipients varchar[]; + actor_user_following varchar[]; + BEGIN + --- Fetch actor following + SELECT array_agg(following.follower_address) INTO actor_user_following FROM following_relationships + JOIN users ON users.id = following_relationships.follower_id + JOIN users AS following ON following.id = following_relationships.following_id + WHERE users.ap_id = actor; + + --- Fetch our initial activity. + SELECT * INTO activity FROM activities WHERE activities.data->>'id' = activity_id; + + LOOP + --- Ensure that we have an activity before continuing. + --- If we don't, the thread is not satisfiable. + IF activity IS NULL THEN + RETURN false; + END IF; + + --- We only care about Create activities. + IF activity.data->>'type' != 'Create' THEN + RETURN true; + END IF; + + --- Normalize the child object into child. + SELECT * INTO child FROM objects + INNER JOIN activities ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id' + WHERE COALESCE(activity.data->'object'->>'id', activity.data->>'object') = objects.data->>'id'; + + --- Fetch the author's AS2 following collection. + SELECT COALESCE(users.follower_address, '') INTO author_fa FROM users WHERE users.ap_id = activity.actor; + + --- Prepare valid recipients array. + valid_recipients := ARRAY[actor, public]; + --- If we specified local public, add it. + IF local_public <> '' THEN + valid_recipients := valid_recipients || local_public; + END IF; + IF ARRAY[author_fa] && actor_user_following THEN + valid_recipients := valid_recipients || author_fa; + END IF; + + --- Check visibility. + IF NOT valid_recipients && activity.recipients THEN + --- activity not visible, break out of the loop + RETURN false; + END IF; + + --- If there's a parent, load it and do this all over again. + IF (child.data->'inReplyTo' IS NOT NULL) AND (child.data->'inReplyTo' != 'null'::jsonb) THEN + SELECT * INTO activity FROM activities + INNER JOIN objects ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id' + WHERE child.data->>'inReplyTo' = objects.data->>'id'; + ELSE + RETURN true; + END IF; + END LOOP; + END; + $$ LANGUAGE plpgsql IMMUTABLE; + """ + end +end From 27016287862a93b1fb4a4bebda3199e32c46d962 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 28 Dec 2021 15:01:37 -0500 Subject: [PATCH 18/31] Add remote interaction ui for posts --- .../twitter_api/util/status_interact.html.eex | 13 ++++ .../controllers/util_controller.ex | 47 ++++++++++++++ .../web/twitter_api/util_controller_test.exs | 64 +++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex diff --git a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex new file mode 100644 index 0000000000..bb3d0a0af2 --- /dev/null +++ b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex @@ -0,0 +1,13 @@ +<%= if @error do %> +

Error: <%= @error %>

+<% else %> +

Interacting with <%= @nickname %>

+
+ <%= @status_id %> +
+ <%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "status"], fn f -> %> + <%= hidden_input f, :status, value: @status_id %> + <%= text_input f, :profile, placeholder: "Your account ID, e.g. lain@quitter.se" %> + <%= submit "Interact" %> + <% end %> +<% end %> diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 5731c78a8a..ee99aab3ed 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do require Logger + alias Pleroma.Activity alias Pleroma.Config alias Pleroma.Emoji alias Pleroma.Healthcheck @@ -59,6 +60,27 @@ def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do end end + def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do + with %Activity{} = activity <- Activity.get_by_id(id), + %User{} = user <- User.get_cached_by_ap_id(activity.actor), + avatar = User.avatar_url(user) do + conn + |> render("status_interact.html", %{ + status_id: id, + nickname: user.nickname, + avatar: avatar, + error: false + }) + else + _e -> + render(conn, "status_interact.html", %{ + status_id: id, + avatar: nil, + error: "Could not find status" + }) + end + end + def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile), %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do @@ -74,6 +96,31 @@ def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profil end end + def remote_subscribe(conn, %{"status" => %{"status_id" => id, "profile" => profile}}) do + get_ap_id = fn activity -> + object = Pleroma.Object.normalize(activity, fetch: false) + + case object do + %{data: %{"id" => ap_id}} -> {:ok, ap_id} + _ -> {:no_ap_id, nil} + end + end + + with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile), + %Activity{} = activity <- Activity.get_by_id(id), + {:ok, ap_id} <- get_ap_id.(activity) do + conn + |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id)) + else + _e -> + render(conn, "status_interact.html", %{ + status_id: id, + avatar: nil, + error: "Something went wrong." + }) + end + end + def remote_interaction(%{body_params: %{ap_id: ap_id, profile: profile}} = conn, _params) do with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do conn diff --git a/test/pleroma/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs index 5dc72b1778..020a5e9a11 100644 --- a/test/pleroma/web/twitter_api/util_controller_test.exs +++ b/test/pleroma/web/twitter_api/util_controller_test.exs @@ -233,6 +233,70 @@ test "it renders form with error when user not found", %{conn: conn} do end end + describe "POST /main/ostatus - remote_subscribe/2 - with statuses" do + setup do: clear_config([:instance, :federating], true) + + test "renders subscribe form", %{conn: conn} do + user = insert(:user) + status = insert(:note_activity, %{user: user}) + status_id = status.id + + assert is_binary(status_id) + + response = + conn + |> post("/main/ostatus", %{"status_id" => status_id, "profile" => ""}) + |> response(:ok) + + refute response =~ "Could not find status" + assert response =~ "Interacting with" + end + + test "renders subscribe form with error when status not found", %{conn: conn} do + response = + conn + |> post("/main/ostatus", %{"status_id" => "somerandomid", "profile" => ""}) + |> response(:ok) + + assert response =~ "Could not find status" + refute response =~ "Interacting with" + end + + test "it redirect to webfinger url", %{conn: conn} do + user = insert(:user) + status = insert(:note_activity, %{user: user}) + status_id = status.id + status_ap_id = status.data["object"] + + assert is_binary(status_id) + assert is_binary(status_ap_id) + + user2 = insert(:user, ap_id: "shp@social.heldscal.la") + + conn = + conn + |> post("/main/ostatus", %{ + "status" => %{"status_id" => status_id, "profile" => user2.ap_id} + }) + + assert redirected_to(conn) == + "https://social.heldscal.la/main/ostatussub?profile=#{status_ap_id}" + end + + test "it renders form with error when status not found", %{conn: conn} do + user2 = insert(:user, ap_id: "shp@social.heldscal.la") + + response = + conn + |> post("/main/ostatus", %{ + "status" => %{"status_id" => "somerandomid", "profile" => user2.ap_id} + }) + |> response(:ok) + + assert response =~ "Something went wrong." + end + end + test "it returns new captcha", %{conn: conn} do with_mock Pleroma.Captcha, new: fn -> "test_captcha" end do From a243a217a7006352542a22aca605e60fc80f9ff0 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 28 Dec 2021 16:12:00 -0500 Subject: [PATCH 19/31] Fix form item name in status_interact.html --- .../web/templates/twitter_api/util/status_interact.html.eex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex index bb3d0a0af2..6354b409f7 100644 --- a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex +++ b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex @@ -6,7 +6,7 @@ <%= @status_id %> <%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "status"], fn f -> %> - <%= hidden_input f, :status, value: @status_id %> + <%= hidden_input f, :status_id, value: @status_id %> <%= text_input f, :profile, placeholder: "Your account ID, e.g. lain@quitter.se" %> <%= submit "Interact" %> <% end %> From 779457d9a4e6b3e5e8b7823119907c1eb24a3b87 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 28 Dec 2021 16:41:46 -0500 Subject: [PATCH 20/31] Add GET endpoints for remote subscription forms There are two reasons for adding a GET endpoint: 0: Barely displaying the form does not change anything on the server. 1: It makes frontend development easier as they can now use a link, instead of a form, to allow remote users to interact with local ones. --- .../operations/twitter_util_operation.ex | 10 ++++++ lib/pleroma/web/router.ex | 1 + .../controllers/util_controller.ex | 16 +++++++--- .../web/twitter_api/util_controller_test.exs | 32 +++++++++++++++++++ 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex index 1cc90990fd..29df03e34c 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -405,6 +405,16 @@ defp remote_interaction_request do } end + def show_subscribe_form_operation do + %Operation{ + tags: ["Accounts"], + summary: "Show remote subscribe form", + operationId: "UtilController.show_subscribe_form", + parameters: [], + responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})} + } + end + defp delete_account_request do %Schema{ title: "AccountDeleteRequest", diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 842596e976..846ba8363d 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -337,6 +337,7 @@ defmodule Pleroma.Web.Router do pipe_through(:pleroma_html) post("/main/ostatus", UtilController, :remote_subscribe) + get("/main/ostatus", UtilController, :show_subscribe_form) get("/ostatus_subscribe", RemoteFollowController, :follow) post("/ostatus_subscribe", RemoteFollowController, :do_follow) end diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index ee99aab3ed..049329c38e 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -17,8 +17,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.WebFinger - plug(Pleroma.Web.ApiSpec.CastAndValidate when action != :remote_subscribe) - plug(Pleroma.Web.Plugs.FederatingPlug when action == :remote_subscribe) + plug(Pleroma.Web.ApiSpec.CastAndValidate when action != :remote_subscribe and action != :show_subscribe_form) + plug(Pleroma.Web.Plugs.FederatingPlug when action == :remote_subscribe when action == :show_subscribe_form) plug( OAuthScopesPlug, @@ -45,7 +45,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TwitterUtilOperation - def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do + def show_subscribe_form(conn, %{"nickname" => nick}) do with %User{} = user <- User.get_cached_by_nickname(nick), avatar = User.avatar_url(user) do conn @@ -60,7 +60,7 @@ def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do end end - def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do + def show_subscribe_form(conn, %{"status_id" => id}) do with %Activity{} = activity <- Activity.get_by_id(id), %User{} = user <- User.get_cached_by_ap_id(activity.actor), avatar = User.avatar_url(user) do @@ -81,6 +81,14 @@ def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do end end + def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do + show_subscribe_form(conn, %{"nickname" => nick}) + end + + def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do + show_subscribe_form(conn, %{"status_id" => id}) + end + def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile), %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do diff --git a/test/pleroma/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs index 020a5e9a11..a4da236359 100644 --- a/test/pleroma/web/twitter_api/util_controller_test.exs +++ b/test/pleroma/web/twitter_api/util_controller_test.exs @@ -297,6 +297,38 @@ test "it renders form with error when status not found", %{conn: conn} do end end + describe "GET /main/ostatus - show_subscribe_form/2" do + setup do: clear_config([:instance, :federating], true) + + test "it works with users", %{conn: conn} do + user = insert(:user) + + response = + conn + |> get("/main/ostatus", %{"nickname" => user.nickname}) + |> response(:ok) + + refute response =~ "Could not find user" + assert response =~ "Remotely follow #{user.nickname}" + end + + test "it works with statuses", %{conn: conn} do + user = insert(:user) + status = insert(:note_activity, %{user: user}) + status_id = status.id + + assert is_binary(status_id) + + response = + conn + |> get("/main/ostatus", %{"status_id" => status_id}) + |> response(:ok) + + refute response =~ "Could not find status" + assert response =~ "Interacting with" + end + end + test "it returns new captcha", %{conn: conn} do with_mock Pleroma.Captcha, new: fn -> "test_captcha" end do From b7c75db0f7f2c048d45fc387dfcf00073cbf8d62 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 28 Dec 2021 16:58:08 -0500 Subject: [PATCH 21/31] Lint --- .../web/twitter_api/controllers/util_controller.ex | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 049329c38e..24b419c310 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -17,8 +17,16 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.WebFinger - plug(Pleroma.Web.ApiSpec.CastAndValidate when action != :remote_subscribe and action != :show_subscribe_form) - plug(Pleroma.Web.Plugs.FederatingPlug when action == :remote_subscribe when action == :show_subscribe_form) + plug( + Pleroma.Web.ApiSpec.CastAndValidate + when action != :remote_subscribe and action != :show_subscribe_form + ) + + plug( + Pleroma.Web.Plugs.FederatingPlug + when action == :remote_subscribe + when action == :show_subscribe_form + ) plug( OAuthScopesPlug, From 1218adacc52f1235aedb1bb102d2e9385507efa4 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 28 Dec 2021 19:37:56 -0500 Subject: [PATCH 22/31] Display status link in remote interaction form --- .../twitter_api/util/status_interact.html.eex | 5 +---- .../controllers/util_controller.ex | 22 ++++++++++--------- .../web/twitter_api/views/util_view.ex | 1 + 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex index 6354b409f7..695c5d64bb 100644 --- a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex +++ b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex @@ -1,10 +1,7 @@ <%= if @error do %>

Error: <%= @error %>

<% else %> -

Interacting with <%= @nickname %>

-
- <%= @status_id %> -
+

Interacting with <%= @nickname %>'s <%= link("status", to: @status_link) %>

<%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "status"], fn f -> %> <%= hidden_input f, :status_id, value: @status_id %> <%= text_input f, :profile, placeholder: "Your account ID, e.g. lain@quitter.se" %> diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 24b419c310..2c31031854 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -70,10 +70,12 @@ def show_subscribe_form(conn, %{"nickname" => nick}) do def show_subscribe_form(conn, %{"status_id" => id}) do with %Activity{} = activity <- Activity.get_by_id(id), + {:ok, ap_id} <- get_ap_id(activity), %User{} = user <- User.get_cached_by_ap_id(activity.actor), avatar = User.avatar_url(user) do conn |> render("status_interact.html", %{ + status_link: ap_id, status_id: id, nickname: user.nickname, avatar: avatar, @@ -113,18 +115,9 @@ def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profil end def remote_subscribe(conn, %{"status" => %{"status_id" => id, "profile" => profile}}) do - get_ap_id = fn activity -> - object = Pleroma.Object.normalize(activity, fetch: false) - - case object do - %{data: %{"id" => ap_id}} -> {:ok, ap_id} - _ -> {:no_ap_id, nil} - end - end - with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile), %Activity{} = activity <- Activity.get_by_id(id), - {:ok, ap_id} <- get_ap_id.(activity) do + {:ok, ap_id} <- get_ap_id(activity) do conn |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id)) else @@ -146,6 +139,15 @@ def remote_interaction(%{body_params: %{ap_id: ap_id, profile: profile}} = conn, end end + defp get_ap_id(activity) do + object = Pleroma.Object.normalize(activity, fetch: false) + + case object do + %{data: %{"id" => ap_id}} -> {:ok, ap_id} + _ -> {:no_ap_id, nil} + end + end + def frontend_configurations(conn, _params) do render(conn, "frontend_configurations.json") end diff --git a/lib/pleroma/web/twitter_api/views/util_view.ex b/lib/pleroma/web/twitter_api/views/util_view.ex index 69f243097c..2365a396bb 100644 --- a/lib/pleroma/web/twitter_api/views/util_view.ex +++ b/lib/pleroma/web/twitter_api/views/util_view.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilView do use Pleroma.Web, :view import Phoenix.HTML.Form + import Phoenix.HTML.Link alias Pleroma.Config alias Pleroma.Web.Endpoint alias Pleroma.Web.Gettext From ec0e912c52f9c44ef78dbb8971d39ab4ef53bf30 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 29 Dec 2021 00:29:00 -0500 Subject: [PATCH 23/31] Add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d0ef4e113..a979ff3256 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Make backend-rendered pages translatable. This includes emails. Pages returned as a HTTP response are translated using the language specified in the `userLanguage` cookie, or the `Accept-Language` header. Emails are translated using the `language` field when registering. This language can be changed by `PATCH /api/v1/accounts/update_credentials` with the `language` field. - Uploadfilter `Pleroma.Upload.Filter.Exiftool.ReadDescription` returns description values to the FE so they can pre fill the image description field - Added move account API +- Enable remote users to interact with posts ### Fixed - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies From 4ec9eeb3f8f3502841cd136ea7afe9298b477120 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 25 Mar 2022 22:05:28 -0400 Subject: [PATCH 24/31] Make remote interaction page translatable --- .../twitter_api/util/status_interact.html.eex | 8 +++--- .../controllers/util_controller.ex | 28 ++++++++++++++++--- .../web/twitter_api/views/util_view.ex | 1 + 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex index 695c5d64bb..d77174967d 100644 --- a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex +++ b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex @@ -1,10 +1,10 @@ <%= if @error do %> -

Error: <%= @error %>

+

<%= Gettext.dpgettext("static_pages", "status interact error", "Error: %{error}", error: @error) %>

<% else %> -

Interacting with <%= @nickname %>'s <%= link("status", to: @status_link) %>

+

<%= raw Gettext.dpgettext("static_pages", "status interact header", "Interacting with %{nickname}'s %{status_link}", nickname: safe_to_string(html_escape(@nickname)), status_link: safe_to_string(link(Gettext.dpgettext("static_pages", "status interact header - status link text", "status"), to: @status_link))) %>

<%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "status"], fn f -> %> <%= hidden_input f, :status_id, value: @status_id %> - <%= text_input f, :profile, placeholder: "Your account ID, e.g. lain@quitter.se" %> - <%= submit "Interact" %> + <%= text_input f, :profile, placeholder: Gettext.dpgettext("static_pages", "placeholder text for account id", "Your account ID, e.g. lain@quitter.se") %> + <%= submit Gettext.dpgettext("static_pages", "status interact authorization button", "Interact") %> <% end %> <% end %> diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 2c31031854..d5a24ae6ca 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -63,7 +63,12 @@ def show_subscribe_form(conn, %{"nickname" => nick}) do render(conn, "subscribe.html", %{ nickname: nick, avatar: nil, - error: "Could not find user" + error: + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "remote follow error message - user not found", + "Could not find user" + ) }) end end @@ -86,7 +91,12 @@ def show_subscribe_form(conn, %{"status_id" => id}) do render(conn, "status_interact.html", %{ status_id: id, avatar: nil, - error: "Could not find status" + error: + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "status interact error message - status not found", + "Could not find status" + ) }) end end @@ -109,7 +119,12 @@ def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profil render(conn, "subscribe.html", %{ nickname: nick, avatar: nil, - error: "Something went wrong." + error: + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "remote follow error message - unknown error", + "Something went wrong." + ) }) end end @@ -125,7 +140,12 @@ def remote_subscribe(conn, %{"status" => %{"status_id" => id, "profile" => profi render(conn, "status_interact.html", %{ status_id: id, avatar: nil, - error: "Something went wrong." + error: + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "status interact error message - unknown error", + "Something went wrong." + ) }) end end diff --git a/lib/pleroma/web/twitter_api/views/util_view.ex b/lib/pleroma/web/twitter_api/views/util_view.ex index 2365a396bb..31b7c0c0c8 100644 --- a/lib/pleroma/web/twitter_api/views/util_view.ex +++ b/lib/pleroma/web/twitter_api/views/util_view.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilView do use Pleroma.Web, :view + import Phoenix.HTML import Phoenix.HTML.Form import Phoenix.HTML.Link alias Pleroma.Config From c59ee1f172628d37e1396e080876f0f3aebaf730 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 20 Aug 2022 21:19:31 -0400 Subject: [PATCH 25/31] Expose availability of GET /main/ostatus via instance --- lib/pleroma/web/mastodon_api/views/instance_view.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index 62931bd41b..dc44295e58 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -98,7 +98,8 @@ def features do end, if Config.get([:instance, :profile_directory]) do "profile_directory" - end + end, + "pleroma:get:main/ostatus" ] |> Enum.filter(& &1) end From e94937847669ffe318518ddc39257d726da0affc Mon Sep 17 00:00:00 2001 From: Fristi Date: Sat, 13 Aug 2022 10:24:16 +0000 Subject: [PATCH 26/31] Added translation using Weblate (Dutch) --- priv/gettext/nl/LC_MESSAGES/static_pages.po | 525 ++++++++++++++++++++ 1 file changed, 525 insertions(+) create mode 100644 priv/gettext/nl/LC_MESSAGES/static_pages.po diff --git a/priv/gettext/nl/LC_MESSAGES/static_pages.po b/priv/gettext/nl/LC_MESSAGES/static_pages.po new file mode 100644 index 0000000000..91e2fa3c8e --- /dev/null +++ b/priv/gettext/nl/LC_MESSAGES/static_pages.po @@ -0,0 +1,525 @@ +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-08-13 13:24+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Translate Toolkit 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/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:15 +#: lib/pleroma/web/templates/feed/feed/tag.rss.eex:7 +#, 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 %{client_name} 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
%{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 %{email}. " +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 %{instance} 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 "

You requested a full backup of your Pleroma account. It's ready for download:

\n

%{download_url}

\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 "

Awaiting Approval

\n

Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.

\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 "

Thank you for registering on %{instance_name}

\n

Email confirmation is required to activate the account.

\n

Please click the following link to activate your account.

\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 "

Reset your password at %{instance_name}

\n

Someone has requested password change for your account at %{instance_name}.

\n

If it was you, visit the following link to proceed: reset password.

\n

If it was someone else, nothing to worry about: your data is secure and your password has not been changed.

\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 "

Hello @%{nickname},

\n

Your account at %{instance_name} has been registered successfully.

\n

No further action is required to activate your account.

\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 "

You are invited to %{instance_name}

\n

%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.

\n

Click the following link to register: accept invitation.

\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 "

Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:

\n

%{download_url}

\n" +msgstr "" From a6195c7127ceee372a85ea7ff4af7fb9457588cf Mon Sep 17 00:00:00 2001 From: Fristi Date: Sat, 13 Aug 2022 10:32:52 +0000 Subject: [PATCH 27/31] Added translation using Weblate (Dutch) --- priv/gettext/nl/LC_MESSAGES/posix_errors.po | 163 ++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 priv/gettext/nl/LC_MESSAGES/posix_errors.po diff --git a/priv/gettext/nl/LC_MESSAGES/posix_errors.po b/priv/gettext/nl/LC_MESSAGES/posix_errors.po new file mode 100644 index 0000000000..d64fca5fcd --- /dev/null +++ b/priv/gettext/nl/LC_MESSAGES/posix_errors.po @@ -0,0 +1,163 @@ +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-08-13 13:32+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Translate Toolkit 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 "" From 425fbce7be05f1e99bf6b31b011266d8fdb39da6 Mon Sep 17 00:00:00 2001 From: Fristi Date: Sat, 13 Aug 2022 10:30:41 +0000 Subject: [PATCH 28/31] Translated using Weblate (Dutch) Currently translated at 100.0% (106 of 106 strings) Translation: Pleroma/Pleroma Backend (domain errors) Translate-URL: http://weblate.pleroma-dev.ebin.club/projects/pleroma/pleroma-backend-domain-errors/nl/ --- priv/gettext/nl/LC_MESSAGES/errors.po | 158 ++++++++++++++------------ 1 file changed, 84 insertions(+), 74 deletions(-) diff --git a/priv/gettext/nl/LC_MESSAGES/errors.po b/priv/gettext/nl/LC_MESSAGES/errors.po index cfcb05fe67..ce1d794cfc 100644 --- a/priv/gettext/nl/LC_MESSAGES/errors.po +++ b/priv/gettext/nl/LC_MESSAGES/errors.po @@ -3,16 +3,16 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-05-15 09:37+0000\n" -"PO-Revision-Date: 2020-06-02 07:36+0000\n" +"PO-Revision-Date: 2022-08-14 11:04+0000\n" "Last-Translator: Fristi \n" -"Language-Team: Dutch \n" +"Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.0.4\n" +"X-Generator: Weblate 4.13.1\n" ## This file is a PO Template file. ## @@ -118,7 +118,7 @@ msgstr "Al gestemd" #: lib/pleroma/web/oauth/oauth_controller.ex:360 #, elixir-format msgid "Bad request" -msgstr "Bad request" +msgstr "Ongeldig request" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:425 #, elixir-format @@ -155,7 +155,7 @@ msgstr "Object kan niet geliked worden" #: lib/pleroma/web/common_api/utils.ex:556 #, elixir-format msgid "Cannot post an empty status without attachments" -msgstr "Status kan niet geplaatst worden zonder tekst of bijlagen" +msgstr "Bericht kan niet geplaatst worden zonder tekst of bijlagen" #: lib/pleroma/web/common_api/utils.ex:504 #, elixir-format @@ -165,122 +165,122 @@ msgstr "Opmerking dient maximaal %{max_size} karakters te bevatten" #: lib/pleroma/config/config_db.ex:222 #, elixir-format msgid "Config with params %{params} not found" -msgstr "" +msgstr "Instelling met parameters %{params} kon niet gevonden worden" #: lib/pleroma/web/common_api/common_api.ex:95 #, elixir-format msgid "Could not delete" -msgstr "" +msgstr "Verwijderen mislukt" #: lib/pleroma/web/common_api/common_api.ex:141 #, elixir-format msgid "Could not favorite" -msgstr "" +msgstr "Favoriet maken mislukt" #: lib/pleroma/web/common_api/common_api.ex:370 #, elixir-format msgid "Could not pin" -msgstr "" +msgstr "Vastmaken mislukt" #: lib/pleroma/web/common_api/common_api.ex:112 #, elixir-format msgid "Could not repeat" -msgstr "" +msgstr "Herhalen mislukt" #: lib/pleroma/web/common_api/common_api.ex:188 #, elixir-format msgid "Could not unfavorite" -msgstr "" +msgstr "Favoriet ongedaan maken mislukt" #: lib/pleroma/web/common_api/common_api.ex:380 #, elixir-format msgid "Could not unpin" -msgstr "" +msgstr "Vastmaken ongedaan maken mislukt" #: lib/pleroma/web/common_api/common_api.ex:126 #, elixir-format msgid "Could not unrepeat" -msgstr "" +msgstr "Herhalen ongedaan maken mislukt" #: lib/pleroma/web/common_api/common_api.ex:428 #: lib/pleroma/web/common_api/common_api.ex:437 #, elixir-format msgid "Could not update state" -msgstr "" +msgstr "Status bijwerken mislukt" #: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:202 #, elixir-format msgid "Error." -msgstr "" +msgstr "Fout." #: lib/pleroma/web/twitter_api/twitter_api.ex:106 #, elixir-format msgid "Invalid CAPTCHA" -msgstr "" +msgstr "Ongeldige CAPTCHA" #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:117 #: lib/pleroma/web/oauth/oauth_controller.ex:569 #, elixir-format msgid "Invalid credentials" -msgstr "" +msgstr "Ongeldige inloggegevens" #: lib/pleroma/plugs/ensure_authenticated_plug.ex:38 #, elixir-format msgid "Invalid credentials." -msgstr "" +msgstr "Ongeldige inloggegevens." #: lib/pleroma/web/common_api/common_api.ex:265 #, elixir-format msgid "Invalid indices" -msgstr "" +msgstr "Ongeldige indexen" #: lib/pleroma/web/admin_api/admin_api_controller.ex:1147 #, elixir-format msgid "Invalid parameters" -msgstr "" +msgstr "Ongeldige parameters" #: lib/pleroma/web/common_api/utils.ex:411 #, elixir-format msgid "Invalid password." -msgstr "" +msgstr "Ongeldig wachtwoord." #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:187 #, elixir-format msgid "Invalid request" -msgstr "" +msgstr "Ongeldig request" #: lib/pleroma/web/twitter_api/twitter_api.ex:109 #, elixir-format msgid "Kocaptcha service unavailable" -msgstr "" +msgstr "Kocaptcha service niet beschikbaar" #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:113 #, elixir-format msgid "Missing parameters" -msgstr "" +msgstr "Ontbrekende parameters" #: lib/pleroma/web/common_api/utils.ex:540 #, elixir-format msgid "No such conversation" -msgstr "" +msgstr "Gesprek niet gevonden" #: lib/pleroma/web/admin_api/admin_api_controller.ex:439 #: lib/pleroma/web/admin_api/admin_api_controller.ex:465 lib/pleroma/web/admin_api/admin_api_controller.ex:507 #, elixir-format msgid "No such permission_group" -msgstr "" +msgstr "Permission_group niet gevonden" #: lib/pleroma/plugs/uploaded_media.ex:74 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:485 lib/pleroma/web/admin_api/admin_api_controller.ex:1135 #: lib/pleroma/web/feed/user_controller.ex:73 lib/pleroma/web/ostatus/ostatus_controller.ex:143 #, elixir-format msgid "Not found" -msgstr "" +msgstr "Niet gevonden" #: lib/pleroma/web/common_api/common_api.ex:241 #, elixir-format msgid "Poll's author can't vote" -msgstr "" +msgstr "De peiling-auteur kan niet stemmen" #: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20 #: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:37 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:49 @@ -288,215 +288,215 @@ msgstr "" #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:71 #, elixir-format msgid "Record not found" -msgstr "" +msgstr "Record niet gevonden" #: lib/pleroma/web/admin_api/admin_api_controller.ex:1153 #: lib/pleroma/web/feed/user_controller.ex:79 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:32 #: lib/pleroma/web/ostatus/ostatus_controller.ex:149 #, elixir-format msgid "Something went wrong" -msgstr "" +msgstr "Er is iets misgegaan" #: lib/pleroma/web/common_api/activity_draft.ex:107 #, elixir-format msgid "The message visibility must be direct" -msgstr "" +msgstr "De zichtbaarheid van het bericht dient privé te zijn" #: lib/pleroma/web/common_api/utils.ex:566 #, elixir-format msgid "The status is over the character limit" -msgstr "" +msgstr "Het bericht is langer dan het karakter-limiet" #: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31 #, elixir-format msgid "This resource requires authentication." -msgstr "" +msgstr "Deze gegevens vereisen authenticatie." #: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206 #, elixir-format msgid "Throttled" -msgstr "" +msgstr "Geremd" #: lib/pleroma/web/common_api/common_api.ex:266 #, elixir-format msgid "Too many choices" -msgstr "" +msgstr "Teveel keuzes" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:442 #, elixir-format msgid "Unhandled activity type" -msgstr "" +msgstr "Niet-ondersteund activiteits-type" #: lib/pleroma/web/admin_api/admin_api_controller.ex:536 #, elixir-format msgid "You can't revoke your own admin status." -msgstr "" +msgstr "Je kan je eigen beheerdersrechten niet intrekken." #: lib/pleroma/web/oauth/oauth_controller.ex:218 #: lib/pleroma/web/oauth/oauth_controller.ex:309 #, elixir-format msgid "Your account is currently disabled" -msgstr "" +msgstr "Je account is momenteel uitgeschakeld" #: lib/pleroma/web/oauth/oauth_controller.ex:180 #: lib/pleroma/web/oauth/oauth_controller.ex:332 #, elixir-format msgid "Your login is missing a confirmed e-mail address" -msgstr "" +msgstr "Je login bevat geen bevestigd e-mailadres" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:389 #, elixir-format msgid "can't read inbox of %{nickname} as %{as_nickname}" -msgstr "" +msgstr "kan de inbox van %{nickname} niet lezen als %{as_nickname}" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:472 #, elixir-format msgid "can't update outbox of %{nickname} as %{as_nickname}" -msgstr "" +msgstr "kan de outbox van %{nickname} niet bijwerken als %{as_nickname}" #: lib/pleroma/web/common_api/common_api.ex:388 #, elixir-format msgid "conversation is already muted" -msgstr "" +msgstr "gesprek is al genegeerd" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:316 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:491 #, elixir-format msgid "error" -msgstr "" +msgstr "fout" #: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:29 #, elixir-format msgid "mascots can only be images" -msgstr "" +msgstr "mascottes kunnen alleen afbeeldingen zijn" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:60 #, elixir-format msgid "not found" -msgstr "" +msgstr "niet gevonden" #: lib/pleroma/web/oauth/oauth_controller.ex:395 #, elixir-format msgid "Bad OAuth request." -msgstr "" +msgstr "Ongeldig OAuth request." #: lib/pleroma/web/twitter_api/twitter_api.ex:115 #, elixir-format msgid "CAPTCHA already used" -msgstr "" +msgstr "CAPTCHA is al gebruikt" #: lib/pleroma/web/twitter_api/twitter_api.ex:112 #, elixir-format msgid "CAPTCHA expired" -msgstr "" +msgstr "CAPTCHA is verlopen" #: lib/pleroma/plugs/uploaded_media.ex:55 #, elixir-format msgid "Failed" -msgstr "" +msgstr "Mislukt" #: lib/pleroma/web/oauth/oauth_controller.ex:411 #, elixir-format msgid "Failed to authenticate: %{message}." -msgstr "" +msgstr "Authenticatie mislukt: %{message}." #: lib/pleroma/web/oauth/oauth_controller.ex:442 #, elixir-format msgid "Failed to set up user account." -msgstr "" +msgstr "Aanmaken van gebruikersaccount is mislukt." #: lib/pleroma/plugs/oauth_scopes_plug.ex:38 #, elixir-format msgid "Insufficient permissions: %{permissions}." -msgstr "" +msgstr "Niet voldoende rechten: %{permissions}." #: lib/pleroma/plugs/uploaded_media.ex:94 #, elixir-format msgid "Internal Error" -msgstr "" +msgstr "Interne Fout" #: lib/pleroma/web/oauth/fallback_controller.ex:22 #: lib/pleroma/web/oauth/fallback_controller.ex:29 #, elixir-format msgid "Invalid Username/Password" -msgstr "" +msgstr "Ongeldige Gebruikersnaam/Wachtwoord" #: lib/pleroma/web/twitter_api/twitter_api.ex:118 #, elixir-format msgid "Invalid answer data" -msgstr "" +msgstr "Ongeldig antwoord" #: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:128 #, elixir-format msgid "Nodeinfo schema version not handled" -msgstr "" +msgstr "Nodeinfo schema wordt niet ondersteund" #: lib/pleroma/web/oauth/oauth_controller.ex:169 #, elixir-format msgid "This action is outside the authorized scopes" -msgstr "" +msgstr "Deze actie bevindt zich buiten de gemachtigde scopes" #: lib/pleroma/web/oauth/fallback_controller.ex:14 #, elixir-format msgid "Unknown error, please check the details and try again." -msgstr "" +msgstr "Onbekende fout, controleer a.u.b. de details en probeer het opnieuw." #: lib/pleroma/web/oauth/oauth_controller.ex:116 #: lib/pleroma/web/oauth/oauth_controller.ex:155 #, elixir-format msgid "Unlisted redirect_uri." -msgstr "" +msgstr "Niet-vermelde redirect_uri." #: lib/pleroma/web/oauth/oauth_controller.ex:391 #, elixir-format msgid "Unsupported OAuth provider: %{provider}." -msgstr "" +msgstr "Niet ondersteunde OAuth provider: %{provider}." #: lib/pleroma/uploaders/uploader.ex:72 #, elixir-format msgid "Uploader callback timeout" -msgstr "" +msgstr "Uploader terugkoppeling timeout" #: lib/pleroma/web/uploader_controller.ex:23 #, elixir-format msgid "bad request" -msgstr "" +msgstr "ongeldig request" #: lib/pleroma/web/twitter_api/twitter_api.ex:103 #, elixir-format msgid "CAPTCHA Error" -msgstr "" +msgstr "CAPTCHA Fout" #: lib/pleroma/web/common_api/common_api.ex:200 #, elixir-format msgid "Could not add reaction emoji" -msgstr "" +msgstr "Reactie-emoji toevoegen mislukt" #: lib/pleroma/web/common_api/common_api.ex:211 #, elixir-format msgid "Could not remove reaction emoji" -msgstr "" +msgstr "Reactie-emoji verwijderen mislukt" #: lib/pleroma/web/twitter_api/twitter_api.ex:129 #, elixir-format msgid "Invalid CAPTCHA (Missing parameter: %{name})" -msgstr "" +msgstr "Ongeldige CAPTCHA (Ontbrekende parameter: %{name})" #: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:92 #, elixir-format msgid "List not found" -msgstr "" +msgstr "Lijst niet gevonden" #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:124 #, elixir-format msgid "Missing parameter: %{name}" -msgstr "" +msgstr "Ontbrekende parameter: %{name}" #: lib/pleroma/web/oauth/oauth_controller.ex:207 #: lib/pleroma/web/oauth/oauth_controller.ex:322 #, elixir-format msgid "Password reset is required" -msgstr "" +msgstr "Wachtwoordherstel is vereist" #: lib/pleroma/tests/auth_test_controller.ex:9 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/admin_api_controller.ex:6 @@ -528,53 +528,63 @@ msgstr "" #, elixir-format msgid "Security violation: OAuth scopes check was neither handled nor explicitly skipped." msgstr "" +"Schending van beveiliging: OAuth scope-controle is niet uitgevoerd en niet " +"expliciet overgeslagen." #: lib/pleroma/plugs/ensure_authenticated_plug.ex:28 #, elixir-format msgid "Two-factor authentication enabled, you must use a access token." msgstr "" +"Tweefactor authenticatie is ingeschakeld, een toegangssleutel is verplicht." #: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:210 #, elixir-format msgid "Unexpected error occurred while adding file to pack." msgstr "" +"Er is een onverwachte fout opgetreden tijdens het toevoegen van het bestand." #: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:138 #, elixir-format msgid "Unexpected error occurred while creating pack." msgstr "" +"Er is een onverwachte fout opgetreden tijdens het aanmaken van het pakket." #: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:278 #, elixir-format msgid "Unexpected error occurred while removing file from pack." msgstr "" +"Er is een onverwachte fout opgetreden tijdens het verwijderen van het " +"bestand." #: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:250 #, elixir-format msgid "Unexpected error occurred while updating file in pack." msgstr "" +"Er is een onverwachte fout opgetreden tijdens het bijwerken van het bestand." #: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:179 #, elixir-format msgid "Unexpected error occurred while updating pack metadata." msgstr "" +"Er is een onverwachte fout opgetreden tijdens het bijwerken van de pakket-" +"metadata." #: lib/pleroma/plugs/user_is_admin_plug.ex:21 #, elixir-format msgid "User is not an admin." -msgstr "" +msgstr "Gebruiker is niet een beheerder." #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61 #, elixir-format msgid "Web push subscription is disabled on this Pleroma instance" -msgstr "" +msgstr "Web push abbonement is uitgeschakeld op deze Pleroma instantie" #: lib/pleroma/web/admin_api/admin_api_controller.ex:502 #, elixir-format msgid "You can't revoke your own admin/moderator status." -msgstr "" +msgstr "Je kan je eigen beheerders- of moderatorrechten niet intrekken." #: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:105 #, elixir-format msgid "authorization required for timeline view" -msgstr "" +msgstr "machtiging is vereist voor de tijdlijn weergave" From 9af5da66660cbc0cd2c71ea08484b947de5ba38b Mon Sep 17 00:00:00 2001 From: Fristi Date: Sat, 13 Aug 2022 10:25:34 +0000 Subject: [PATCH 29/31] Translated using Weblate (Dutch) Currently translated at 100.0% (83 of 83 strings) Translation: Pleroma/Pleroma Backend (domain static_pages) Translate-URL: http://weblate.pleroma-dev.ebin.club/projects/pleroma/pleroma-backend-domain-static_pages/nl/ --- priv/gettext/nl/LC_MESSAGES/static_pages.po | 194 ++++++++++++-------- 1 file changed, 118 insertions(+), 76 deletions(-) diff --git a/priv/gettext/nl/LC_MESSAGES/static_pages.po b/priv/gettext/nl/LC_MESSAGES/static_pages.po index 91e2fa3c8e..2972384fc2 100644 --- a/priv/gettext/nl/LC_MESSAGES/static_pages.po +++ b/priv/gettext/nl/LC_MESSAGES/static_pages.po @@ -3,14 +3,16 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-08-13 13:24+0300\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-08-14 11:04+0000\n" +"Last-Translator: Fristi \n" +"Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Translate Toolkit 3.7.2\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.13.1\n" ## This file is a PO Template file. ## @@ -21,150 +23,149 @@ msgstr "" ## 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 "" +msgstr "Machtigen" #: 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 "" +msgstr "Fout bij ophalen gebruiker" #: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:4 #, elixir-autogen, elixir-format msgctxt "remote follow header" msgid "Remote follow" -msgstr "" +msgstr "Extern volgen" #: 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 "" +msgstr "Authenticatiecode" #: 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 "" +msgstr "Wachtwoord" #: 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 "" +msgstr "Gebruikersnaam" #: 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 "" +msgstr "Machtigen" #: 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 "" +msgstr "Machtigen" #: 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 "" +msgstr "Fout bij volgen van account" #: 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 "" +msgstr "Log in om te volgen" #: 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 "" +msgstr "Tweefactor authenticatie" #: lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex:4 #, elixir-autogen, elixir-format msgctxt "remote follow success" msgid "Account followed!" -msgstr "" +msgstr "Account gevolgd!" #: 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 "" +msgstr "Je account ID, b.v. gebruiker@instantie.net" #: 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 "" +msgstr "Volgen" #: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:2 #, elixir-autogen, elixir-format msgctxt "remote follow error" msgid "Error: %{error}" -msgstr "" +msgstr "Fout: %{error}" #: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:4 #, elixir-autogen, elixir-format msgctxt "remote follow header" msgid "Remotely follow %{nickname}" -msgstr "" +msgstr "%{nickname} extern volgen" #: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:12 #, elixir-autogen, elixir-format msgctxt "password reset button" msgid "Reset" -msgstr "" +msgstr "Herstellen" #: 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 "" +msgstr "Homepagina" #: 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 "" +msgstr "Wachtwoordherstel mislukt" #: 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 "" +msgstr "Bevestiging" #: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:4 #, elixir-autogen, elixir-format msgctxt "password reset form password prompt" msgid "Password" -msgstr "" +msgstr "Wachtwoord" #: 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 "" +msgstr "Ongeldige Token" #: 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 "" +msgstr "Homepagina" #: 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 "" +msgstr "Wachtwoord gewijzigd!" #: lib/pleroma/web/templates/feed/feed/tag.atom.eex:15 #: lib/pleroma/web/templates/feed/feed/tag.rss.eex:7 @@ -172,354 +173,395 @@ msgstr "" 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 "" +"Dit zijn openbare berichten die getagd zijn met #%{tag}. Je kunt op deze " +"reageren indien je een account hebt in de fediverse." #: 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 "" +msgstr "Machtiging bestaat" #: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:32 #, elixir-autogen, elixir-format msgctxt "oauth authorize approve button" msgid "Approve" -msgstr "" +msgstr "Goedkeuren" #: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:30 #, elixir-autogen, elixir-format msgctxt "oauth authorize cancel button" msgid "Cancel" -msgstr "" +msgstr "Annuleren" #: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:23 #, elixir-autogen, elixir-format msgctxt "oauth authorize message" msgid "Application %{client_name} is requesting access to your account." msgstr "" +"Applicatie %{client_name} vraagt om toegang tot je account." #: 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 "" +msgstr "Machtiging is geslaagd" #: 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 "" +msgstr "Inloggen bij externe provider" #: 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 "" +msgstr "Inloggen met %{strategy}" #: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:54 #, elixir-autogen, elixir-format msgctxt "oauth login button" msgid "Log In" -msgstr "" +msgstr "Inloggen" #: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:51 #, elixir-autogen, elixir-format msgctxt "oauth login password prompt" msgid "Password" -msgstr "" +msgstr "Wachtwoord" #: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:47 #, elixir-autogen, elixir-format msgctxt "oauth login username prompt" msgid "Username" -msgstr "" +msgstr "Gebruikersnaam" #: 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 "" +msgstr "Pleroma Gebruiker" #: 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 "" +"Let op! Je kunt je accountnaam hierna niet meer wijzigen. Je kunt echter wel " +"nog je weergavenaam wijzigen." #: 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 "" +msgstr "E-mail" #: 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 "" +"Indien je graag een nieuw account wilt registreren, vul dan a.u.b de " +"onderstaande details in." #: 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 "" +msgstr "Doorgaan als bestaande gebruiker" #: 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 "" +msgstr "Wachtwoord" #: 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 "" +msgstr "Alternatief, log in om te verbinden met een bestaand account." #: 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 "" +msgstr "Naam of e-mail" #: 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 "" +msgstr "Weergavenaam" #: 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 "" +msgstr "Doorgaan als nieuwe gebruiker" #: 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 "" +msgstr "Registratiegegevens" #: 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 "" +msgstr "Dit is je eerste bezoek! Vul a.u.b. je Pleroma gebruikersnaam in." #: 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 "" +msgstr "De volgende rechten zullen worden toegekend" #: 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
%{token}" -msgstr "" +msgstr "Token code is
%{token}" #: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:12 #, elixir-autogen, elixir-format msgctxt "mfa auth code prompt" msgid "Authentication code" -msgstr "" +msgstr "Authenticatiecode" #: 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 "" +msgstr "Tweefactor authenticatie" #: 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 "" +msgstr "Voer een tweefactor herstelcode in" #: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:20 #, elixir-autogen, elixir-format msgctxt "mfa auth verify code button" msgid "Verify" -msgstr "" +msgstr "Controleren" #: 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 "" +msgstr "Tweefactor herstel" #: 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 "" +msgstr "Herstelcode" #: 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 "" +msgstr "Voer een tweefactor code in" #: 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 "" +msgstr "Controleren" #: 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 "" +msgstr "Extern volgen" #: 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 "" +msgstr "Hoi %{nickname}, dit is wat je hebt gemist!" #: 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 %{email}. " msgstr "" +"Het e-mailadres waarmee je bent ingeschreven is %{email}. " #: 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 %{instance} Pleroma instance." msgstr "" +"Je ontvangt deze e-mail omdat je bent ingeschreven voor overzichts-mails te " +"ontvangen van %{instance} Pleroma instantie." #: 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 "" +msgstr "Je kunt je %{here} uitschrijven voor deze e-mails." #: lib/pleroma/web/templates/email/digest.html.eex:547 #, elixir-autogen, elixir-format msgctxt "digest email unsubscribe action link text" msgid "here" -msgstr "" +msgstr "hier" #: lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex:1 #, elixir-autogen, elixir-format msgctxt "mailer unsubscribe failed message" msgid "UNSUBSCRIBE FAILURE" -msgstr "" +msgstr "UITSCHRIJVEN MISLUKT" #: lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex:1 #, elixir-autogen, elixir-format msgctxt "mailer unsubscribe successful message" msgid "UNSUBSCRIBE SUCCESSFUL" -msgstr "" +msgstr "UITSCHRIJVEN GESLAAGD" #: 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] "" +msgstr[0] "%{count} Nieuwe Volger" +msgstr[1] "%{count} Nieuwe Volgers" #: lib/pleroma/emails/user_email.ex:356 #, elixir-autogen, elixir-format msgctxt "account archive email body - self-requested" msgid "

You requested a full backup of your Pleroma account. It's ready for download:

\n

%{download_url}

\n" msgstr "" +"

Je hebt een verzoek ingediend voor een volledige back-up van je Pleroma " +"account. Deze is gereed om te downloaden:

\n" +"

%{download_url}

\n" #: lib/pleroma/emails/user_email.ex:384 #, elixir-autogen, elixir-format msgctxt "account archive email subject" msgid "Your account archive is ready" -msgstr "" +msgstr "Je account archief is gereed" #: lib/pleroma/emails/user_email.ex:188 #, elixir-autogen, elixir-format msgctxt "approval pending email body" msgid "

Awaiting Approval

\n

Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.

\n" msgstr "" +"

Goedkeuring in afwachting

\n" +"

Je account bij %{instance_name} zal worden beoordeeld door de beheerders. " +"Je zult een opvolgende e-mail ontvangen wanneer je account goed gekeurd " +"is.

\n" #: lib/pleroma/emails/user_email.ex:202 #, elixir-autogen, elixir-format msgctxt "approval pending email subject" msgid "Your account is awaiting approval" -msgstr "" +msgstr "Je account is in afwachting van goedkeuring" #: lib/pleroma/emails/user_email.ex:158 #, elixir-autogen, elixir-format msgctxt "confirmation email body" msgid "

Thank you for registering on %{instance_name}

\n

Email confirmation is required to activate the account.

\n

Please click the following link to activate your account.

\n" msgstr "" +"

Bedankt voor het registreren bij %{instance_name}

\n" +"

Bevestiging via e-mail is vereist om je account te activeren.

\n" +"

Je kunt je account activeren door op deze " +"link te klikken.

\n" #: lib/pleroma/emails/user_email.ex:174 #, elixir-autogen, elixir-format msgctxt "confirmation email subject" msgid "%{instance_name} account confirmation" -msgstr "" +msgstr "%{instance_name} account bevestiging" #: lib/pleroma/emails/user_email.ex:310 #, elixir-autogen, elixir-format msgctxt "digest email subject" msgid "Your digest from %{instance_name}" -msgstr "" +msgstr "Je overzicht van %{instance_name}" #: lib/pleroma/emails/user_email.ex:81 #, elixir-autogen, elixir-format msgctxt "password reset email body" msgid "

Reset your password at %{instance_name}

\n

Someone has requested password change for your account at %{instance_name}.

\n

If it was you, visit the following link to proceed: reset password.

\n

If it was someone else, nothing to worry about: your data is secure and your password has not been changed.

\n" msgstr "" +"

Herstel je wachtwoord bij %{instance_name}

\n" +"

Iemand heeft een verzoek ingediend om het wachtwoord van je account bij " +"%{instance_name} te herstellen.

\n" +"

Als je dit zelf geweest bent, volg dan de volgende link om door te gaan: " +"wachtwoord herstellen.

\n" +"

Indien je dit niet geweest bent, hoef je geen verdere acties te " +"ondernemen: je gegevens zijn veilig en je wachtwoord is niet gewijzigd.

\n" #: lib/pleroma/emails/user_email.ex:98 #, elixir-autogen, elixir-format msgctxt "password reset email subject" msgid "Password reset" -msgstr "" +msgstr "Wachtwoord herstellen" #: lib/pleroma/emails/user_email.ex:215 #, elixir-autogen, elixir-format msgctxt "successful registration email body" msgid "

Hello @%{nickname},

\n

Your account at %{instance_name} has been registered successfully.

\n

No further action is required to activate your account.

\n" msgstr "" +"

Hoi @%{nickname},

\n" +"

Het registreren van je account bij %{instance_name} is gelukt.

\n" +"

Er zijn geen verdere stappen vereist om je account te activeren.

\n" #: lib/pleroma/emails/user_email.ex:231 #, elixir-autogen, elixir-format msgctxt "successful registration email subject" msgid "Account registered on %{instance_name}" -msgstr "" +msgstr "Account registratie bij %{instance_name}" #: lib/pleroma/emails/user_email.ex:119 #, elixir-autogen, elixir-format msgctxt "user invitation email body" msgid "

You are invited to %{instance_name}

\n

%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.

\n

Click the following link to register: accept invitation.

\n" msgstr "" +"

Je bent uitgenodigd bij %{instance_name}

\n" +"

%{inviter_name} nodigt je uit om je te registreren bij %{instance_name}, " +"een instantie van het Pleroma gefedereerde sociale netwerk.

\n" +"

Om je te registreren, klink op de volgende link: uitnodiging accepteren.

\n" #: lib/pleroma/emails/user_email.ex:136 #, elixir-autogen, elixir-format msgctxt "user invitation email subject" msgid "Invitation to %{instance_name}" -msgstr "" +msgstr "Uitnodiging van %{instance_name}" #: lib/pleroma/emails/user_email.ex:53 #, elixir-autogen, elixir-format msgctxt "welcome email html body" msgid "Welcome to %{instance_name}!" -msgstr "" +msgstr "Welkom bij %{instance_name}!" #: lib/pleroma/emails/user_email.ex:41 #, elixir-autogen, elixir-format msgctxt "welcome email subject" msgid "Welcome to %{instance_name}!" -msgstr "" +msgstr "Welkom bij %{instance_name}!" #: lib/pleroma/emails/user_email.ex:65 #, elixir-autogen, elixir-format msgctxt "welcome email text body" msgid "Welcome to %{instance_name}!" -msgstr "" +msgstr "Welkom bij %{instance_name}!" #: lib/pleroma/emails/user_email.ex:368 #, elixir-autogen, elixir-format msgctxt "account archive email body - admin requested" msgid "

Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:

\n

%{download_url}

\n" msgstr "" +"

Beheerder @%{admin_nickname} heeft een verzoek ingediend voor een " +"volledige back-up van je Pleroma account. Deze is gereed om te " +"downloaden:

\n" +"

%{download_url}

\n" From 0d8c6b0488c762bad79a6cdcfa8ed6d317b03f68 Mon Sep 17 00:00:00 2001 From: Fristi Date: Sat, 13 Aug 2022 10:33:23 +0000 Subject: [PATCH 30/31] Translated using Weblate (Dutch) Currently translated at 63.8% (30 of 47 strings) Translation: Pleroma/Pleroma Backend (domain posix_errors) Translate-URL: http://weblate.pleroma-dev.ebin.club/projects/pleroma/pleroma-backend-domain-posix_errors/nl/ --- priv/gettext/nl/LC_MESSAGES/posix_errors.po | 70 +++++++++++---------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/priv/gettext/nl/LC_MESSAGES/posix_errors.po b/priv/gettext/nl/LC_MESSAGES/posix_errors.po index d64fca5fcd..cdb1f532fc 100644 --- a/priv/gettext/nl/LC_MESSAGES/posix_errors.po +++ b/priv/gettext/nl/LC_MESSAGES/posix_errors.po @@ -3,14 +3,16 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-08-13 13:32+0300\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-08-14 11:04+0000\n" +"Last-Translator: Fristi \n" +"Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Translate Toolkit 3.7.2\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.13.1\n" ## This file is a PO Template file. ## @@ -22,94 +24,94 @@ msgstr "" ## date. Leave `msgstr`s empty as changing them here as no ## effect: edit them in PO (`.po`) files instead. msgid "eperm" -msgstr "" +msgstr "Uitvoering niet toegestaan" msgid "eacces" -msgstr "" +msgstr "Toegang geweigerd" msgid "eagain" -msgstr "" +msgstr "Resource tijdelijk niet beschikbaar" msgid "ebadf" -msgstr "" +msgstr "Ongeldige file descriptor" msgid "ebadmsg" -msgstr "" +msgstr "Ongeldig bericht" msgid "ebusy" -msgstr "" +msgstr "Apparaat of resource bezet" msgid "edeadlk" -msgstr "" +msgstr "Resource deadlock vermeden" msgid "edeadlock" -msgstr "" +msgstr "Resource deadlock vermeden" msgid "edquot" -msgstr "" +msgstr "Schijf-quota overschreden" msgid "eexist" -msgstr "" +msgstr "Bestand bestaat" msgid "efault" -msgstr "" +msgstr "Ongeldig adres" msgid "efbig" -msgstr "" +msgstr "Bestand is te groot" msgid "eftype" -msgstr "" +msgstr "Ongepast bestands-type of formaat" msgid "eintr" -msgstr "" +msgstr "Onderbroken systeem aanroep" msgid "einval" -msgstr "" +msgstr "Ongeldig argument" msgid "eio" -msgstr "" +msgstr "Input/output fout" msgid "eisdir" -msgstr "" +msgstr "Illegale bewerking op een directory" msgid "eloop" -msgstr "" +msgstr "Te veel niveau's van symbolische koppelingen" msgid "emfile" -msgstr "" +msgstr "Te veel geopende bestanden" msgid "emlink" -msgstr "" +msgstr "Te veel koppelingen" msgid "emultihop" -msgstr "" +msgstr "Multihop geprobeerd" msgid "enametoolong" -msgstr "" +msgstr "Bestandsnaam is te lang" msgid "enfile" -msgstr "" +msgstr "Te veel geopende bestanden in systeem" msgid "enobufs" -msgstr "" +msgstr "Geen buffer-ruimte beschikbaar" msgid "enodev" -msgstr "" +msgstr "Apparaat bestaat niet" msgid "enolck" -msgstr "" +msgstr "Geen sloten beschikbaar" msgid "enolink" -msgstr "" +msgstr "Koppeling is ongedaan gemaakt" msgid "enoent" -msgstr "" +msgstr "Bestand of directory bestaat niet" msgid "enomem" -msgstr "" +msgstr "Geheugen kon niet toegewezen worden" msgid "enospc" -msgstr "" +msgstr "Geen ruimte over op apparaat" msgid "enosr" msgstr "" From b439e91f57d1d7f26e94acc62703a64c747773da Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Fri, 2 Sep 2022 22:35:40 +0000 Subject: [PATCH 31/31] Revert "Merge branch 'rewrite/integration-test-websocket-client' into 'develop'" This reverts merge request !3649 --- mix.exs | 3 +- mix.lock | 1 - .../integration/mastodon_websocket_test.exs | 27 +-- test/support/websocket_client.ex | 195 +++--------------- 4 files changed, 37 insertions(+), 189 deletions(-) diff --git a/mix.exs b/mix.exs index 81c4cf9aeb..6e84fe4825 100644 --- a/mix.exs +++ b/mix.exs @@ -211,8 +211,7 @@ defp deps do {:excoveralls, "0.12.3", only: :test}, {:hackney, "~> 1.18.0", override: true}, {:mox, "~> 1.0", only: :test}, - {:mint, "~> 1.4", only: :test, override: true}, - {:mint_web_socket, "~> 0.3.0", only: :test} + {:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test} ] ++ oauth_deps() end diff --git a/mix.lock b/mix.lock index bd550041d8..14e43c7034 100644 --- a/mix.lock +++ b/mix.lock @@ -79,7 +79,6 @@ "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mint": {:hex, :mint, "1.4.0", "cd7d2451b201fc8e4a8fd86257fb3878d9e3752899eb67b0c5b25b180bde1212", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "10a99e144b815cbf8522dccbc8199d15802440fc7a64d67b6853adb6fa170217"}, - "mint_web_socket": {:hex, :mint_web_socket, "0.3.0", "c9e130dcc778d673fd713eb66434e16cf7d89cee0754e75f26f8bd9a9e592b63", [:mix], [{:mint, "~> 1.4 and >= 1.4.1", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "0605bc3fa684e1a7719b22a3f74be4de5e6a16dd43ac18ebcea72e2adc33b532"}, "mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"}, "mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"}, "mogrify": {:hex, :mogrify, "0.9.1", "a26f107c4987477769f272bd0f7e3ac4b7b75b11ba597fd001b877beffa9c068", [:mix], [], "hexpm", "134edf189337d2125c0948bf0c228fdeef975c594317452d536224069a5b7f05"}, diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs index 16525c7400..2d4c7f63b2 100644 --- a/test/pleroma/integration/mastodon_websocket_test.exs +++ b/test/pleroma/integration/mastodon_websocket_test.exs @@ -28,28 +28,21 @@ def start_socket(qs \\ nil, headers \\ []) do qs -> @path <> qs end - WebsocketClient.connect(self(), path, headers) + WebsocketClient.start_link(self(), path, headers) end test "refuses invalid requests" do capture_log(fn -> - assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 404}} = start_socket() - - assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 404}} = - start_socket("?stream=ncjdk") - + assert {:error, {404, _}} = start_socket() + assert {:error, {404, _}} = start_socket("?stream=ncjdk") Process.sleep(30) end) end test "requires authentication and a valid token for protected streams" do capture_log(fn -> - assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = - start_socket("?stream=user&access_token=aaaaaaaaaaaa") - - assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = - start_socket("?stream=user") - + assert {:error, {401, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") + assert {:error, {401, _}} = start_socket("?stream=user") Process.sleep(30) end) end @@ -109,9 +102,7 @@ test "accepts the 'user' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}") capture_log(fn -> - assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = - start_socket("?stream=user") - + assert {:error, {401, _}} = start_socket("?stream=user") Process.sleep(30) end) end @@ -120,9 +111,7 @@ test "accepts the 'user:notification' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}") capture_log(fn -> - assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = - start_socket("?stream=user:notification") - + assert {:error, {401, _}} = start_socket("?stream=user:notification") Process.sleep(30) end) end @@ -131,7 +120,7 @@ test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}]) capture_log(fn -> - assert {:error, %Mint.WebSocket.UpgradeFailureError{status_code: 401}} = + assert {:error, {401, _}} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) Process.sleep(30) diff --git a/test/support/websocket_client.ex b/test/support/websocket_client.ex index 43f2854de4..d149b324ec 100644 --- a/test/support/websocket_client.ex +++ b/test/support/websocket_client.ex @@ -3,199 +3,60 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Integration.WebsocketClient do - @moduledoc """ - A WebSocket client used to test Mastodon API streaming - - Based on Phoenix Framework's WebsocketClient - https://github.com/phoenixframework/phoenix/blob/master/test/support/websocket_client.exs - """ - - use GenServer - import Kernel, except: [send: 2] - - defstruct [ - :conn, - :request_ref, - :websocket, - :caller, - :status, - :resp_headers, - :sender, - closing?: false - ] + # https://github.com/phoenixframework/phoenix/blob/master/test/support/websocket_client.exs @doc """ - Starts the WebSocket client for given ws URL. `Phoenix.Socket.Message`s - received from the server are forwarded to the sender pid. + Starts the WebSocket server for given ws URL. Received Socket.Message's + are forwarded to the sender pid """ - def connect(sender, url, headers \\ []) do - with {:ok, socket} <- GenServer.start_link(__MODULE__, {sender}), - {:ok, :connected} <- GenServer.call(socket, {:connect, url, headers}) do - {:ok, socket} - end + def start_link(sender, url, headers \\ []) do + :crypto.start() + :ssl.start() + + :websocket_client.start_link( + String.to_charlist(url), + __MODULE__, + [sender], + extra_headers: headers + ) end @doc """ Closes the socket """ def close(socket) do - GenServer.cast(socket, :close) + send(socket, :close) end @doc """ Sends a low-level text message to the client. """ def send_text(server_pid, msg) do - GenServer.call(server_pid, {:text, msg}) + send(server_pid, {:text, msg}) end @doc false - def init({sender}) do - state = %__MODULE__{sender: sender} + def init([sender], _conn_state) do + {:ok, %{sender: sender}} + end + @doc false + def websocket_handle(frame, _conn_state, state) do + send(state.sender, frame) {:ok, state} end @doc false - def handle_call({:connect, url, headers}, from, state) do - uri = URI.parse(url) + def websocket_info({:text, msg}, _conn_state, state) do + {:reply, {:text, msg}, state} + end - http_scheme = - case uri.scheme do - "ws" -> :http - "wss" -> :https - end - - ws_scheme = - case uri.scheme do - "ws" -> :ws - "wss" -> :wss - end - - path = - case uri.query do - nil -> uri.path - query -> uri.path <> "?" <> query - end - - with {:ok, conn} <- Mint.HTTP.connect(http_scheme, uri.host, uri.port), - {:ok, conn, ref} <- Mint.WebSocket.upgrade(ws_scheme, conn, path, headers) do - state = %{state | conn: conn, request_ref: ref, caller: from} - {:noreply, state} - else - {:error, reason} -> - {:reply, {:error, reason}, state} - - {:error, conn, reason} -> - {:reply, {:error, reason}, put_in(state.conn, conn)} - end + def websocket_info(:close, _conn_state, _state) do + {:close, <<>>, "done"} end @doc false - def handle_info(message, state) do - case Mint.WebSocket.stream(state.conn, message) do - {:ok, conn, responses} -> - state = put_in(state.conn, conn) |> handle_responses(responses) - if state.closing?, do: do_close(state), else: {:noreply, state} - - {:error, conn, reason, _responses} -> - state = put_in(state.conn, conn) |> reply({:error, reason}) - {:noreply, state} - - :unknown -> - {:noreply, state} - end - end - - defp do_close(state) do - # Streaming a close frame may fail if the server has already closed - # for writing. - _ = stream_frame(state, :close) - Mint.HTTP.close(state.conn) - {:stop, :normal, state} - end - - defp handle_responses(state, responses) - - defp handle_responses(%{request_ref: ref} = state, [{:status, ref, status} | rest]) do - put_in(state.status, status) - |> handle_responses(rest) - end - - defp handle_responses(%{request_ref: ref} = state, [{:headers, ref, resp_headers} | rest]) do - put_in(state.resp_headers, resp_headers) - |> handle_responses(rest) - end - - defp handle_responses(%{request_ref: ref} = state, [{:done, ref} | rest]) do - case Mint.WebSocket.new(state.conn, ref, state.status, state.resp_headers) do - {:ok, conn, websocket} -> - %{state | conn: conn, websocket: websocket, status: nil, resp_headers: nil} - |> reply({:ok, :connected}) - |> handle_responses(rest) - - {:error, conn, reason} -> - put_in(state.conn, conn) - |> reply({:error, reason}) - end - end - - defp handle_responses(%{request_ref: ref, websocket: websocket} = state, [ - {:data, ref, data} | rest - ]) - when websocket != nil do - case Mint.WebSocket.decode(websocket, data) do - {:ok, websocket, frames} -> - put_in(state.websocket, websocket) - |> handle_frames(frames) - |> handle_responses(rest) - - {:error, websocket, reason} -> - put_in(state.websocket, websocket) - |> reply({:error, reason}) - end - end - - defp handle_responses(state, [_response | rest]) do - handle_responses(state, rest) - end - - defp handle_responses(state, []), do: state - - defp handle_frames(state, frames) do - {frames, state} = - Enum.flat_map_reduce(frames, state, fn - # prepare to close the connection when a close frame is received - {:close, _code, _data}, state -> - {[], put_in(state.closing?, true)} - - frame, state -> - {[frame], state} - end) - - Enum.each(frames, &Kernel.send(state.sender, &1)) - - state - end - - defp reply(state, response) do - if state.caller, do: GenServer.reply(state.caller, response) - put_in(state.caller, nil) - end - - # Encodes a frame as a binary and sends it along the wire, keeping `conn` - # and `websocket` up to date in `state`. - defp stream_frame(state, frame) do - with {:ok, websocket, data} <- Mint.WebSocket.encode(state.websocket, frame), - state = put_in(state.websocket, websocket), - {:ok, conn} <- Mint.WebSocket.stream_request_body(state.conn, state.request_ref, data) do - {:ok, put_in(state.conn, conn)} - else - {:error, %Mint.WebSocket{} = websocket, reason} -> - {:error, put_in(state.websocket, websocket), reason} - - {:error, conn, reason} -> - {:error, put_in(state.conn, conn), reason} - end + def websocket_terminate(_reason, _conn_state, _state) do + :ok end end