From daff85a985c165c73fda3fcd20a3f46c76d36e6d Mon Sep 17 00:00:00 2001
From: Ivan Tashkinov
Date: Mon, 8 Jul 2019 19:53:02 +0300
Subject: [PATCH 01/31] [#878] Refactored assumptions on embedded object
presence in tests. Adjusted note factory to not embed object into activity.
---
lib/pleroma/object.ex | 26 +++++----
test/activity_test.exs | 8 ++-
test/bbs/handler_test.exs | 8 +--
test/support/factory.ex | 6 ++-
.../activity_pub_controller_test.exs | 24 +++++----
test/web/activity_pub/activity_pub_test.exs | 53 ++++++++++---------
test/web/activity_pub/transmogrifier_test.exs | 33 ++++++------
.../activity_pub/views/object_view_test.exs | 10 ++--
test/web/common_api/common_api_test.exs | 6 +--
test/web/mastodon_api/status_view_test.exs | 21 ++++----
.../web/ostatus/activity_representer_test.exs | 19 +++----
.../delete_handling_test.exs | 9 ++--
test/web/ostatus/ostatus_controller_test.exs | 13 +++--
test/web/ostatus/ostatus_test.exs | 46 ++++++++--------
.../twitter_api_controller_test.exs | 2 +-
test/web/twitter_api/twitter_api_test.exs | 12 ++---
.../twitter_api/views/activity_view_test.exs | 6 +--
17 files changed, 164 insertions(+), 138 deletions(-)
diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex
index 4b181ec59..a4dbc3947 100644
--- a/lib/pleroma/object.ex
+++ b/lib/pleroma/object.ex
@@ -44,7 +44,15 @@ def get_by_ap_id(ap_id) do
Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id)))
end
+ defp warn_on_no_object_preloaded(ap_id) do
+ "Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object"
+ |> Logger.debug()
+
+ Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
+ end
+
def normalize(_, fetch_remote \\ true)
+
# If we pass an Activity to Object.normalize(), we can try to use the preloaded object.
# Use this whenever possible, especially when walking graphs in an O(N) loop!
def normalize(%Object{} = object, _), do: object
@@ -55,25 +63,15 @@ def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}, _) do
%Object{id: "pleroma:fake_object_id", data: data}
end
- # Catch and log Object.normalize() calls where the Activity's child object is not
- # preloaded.
+ # No preloaded object
def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}, fetch_remote) do
- Logger.debug(
- "Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!"
- )
-
- Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
-
+ warn_on_no_object_preloaded(ap_id)
normalize(ap_id, fetch_remote)
end
+ # No preloaded object
def normalize(%Activity{data: %{"object" => ap_id}}, fetch_remote) do
- Logger.debug(
- "Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!"
- )
-
- Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
-
+ warn_on_no_object_preloaded(ap_id)
normalize(ap_id, fetch_remote)
end
diff --git a/test/activity_test.exs b/test/activity_test.exs
index 7ba4363c8..b27f6fd36 100644
--- a/test/activity_test.exs
+++ b/test/activity_test.exs
@@ -6,6 +6,7 @@ defmodule Pleroma.ActivityTest do
use Pleroma.DataCase
alias Pleroma.Activity
alias Pleroma.Bookmark
+ alias Pleroma.Object
alias Pleroma.ThreadMute
import Pleroma.Factory
@@ -18,15 +19,18 @@ test "returns an activity by it's AP id" do
test "returns activities by it's objects AP ids" do
activity = insert(:note_activity)
- [found_activity] = Activity.get_all_create_by_object_ap_id(activity.data["object"]["id"])
+ object_data = Object.normalize(activity).data
+
+ [found_activity] = Activity.get_all_create_by_object_ap_id(object_data["id"])
assert activity == found_activity
end
test "returns the activity that created an object" do
activity = insert(:note_activity)
+ object_data = Object.normalize(activity).data
- found_activity = Activity.get_create_by_object_ap_id(activity.data["object"]["id"])
+ found_activity = Activity.get_create_by_object_ap_id(object_data["id"])
assert activity == found_activity
end
diff --git a/test/bbs/handler_test.exs b/test/bbs/handler_test.exs
index 7d5d68d11..6f6533e3d 100644
--- a/test/bbs/handler_test.exs
+++ b/test/bbs/handler_test.exs
@@ -59,6 +59,7 @@ test "replying" do
another_user = insert(:user)
{:ok, activity} = CommonAPI.post(another_user, %{"status" => "this is a test post"})
+ activity_object = Object.normalize(activity)
output =
capture_io(fn ->
@@ -76,8 +77,9 @@ test "replying" do
)
assert reply.actor == user.ap_id
- object = Object.normalize(reply)
- assert object.data["content"] == "this is a reply"
- assert object.data["inReplyTo"] == activity.data["object"]
+
+ reply_object_data = Object.normalize(reply).data
+ assert reply_object_data["content"] == "this is a reply"
+ assert reply_object_data["inReplyTo"] == activity_object.data["id"]
end
end
diff --git a/test/support/factory.ex b/test/support/factory.ex
index c2812e8f7..b1023da38 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Factory do
use ExMachina.Ecto, repo: Pleroma.Repo
alias Pleroma.User
+ alias Pleroma.Object
def participation_factory do
conversation = insert(:conversation)
@@ -122,7 +123,7 @@ def note_activity_factory(attrs \\ %{}) do
"type" => "Create",
"actor" => note.data["actor"],
"to" => note.data["to"],
- "object" => note.data,
+ "object" => note.data["id"],
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
"context" => note.data["context"]
}
@@ -176,13 +177,14 @@ def announce_activity_factory(attrs \\ %{}) do
def like_activity_factory do
note_activity = insert(:note_activity)
+ object = Object.normalize(note_activity)
user = insert(:user)
data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"actor" => user.ap_id,
"type" => "Like",
- "object" => note_activity.data["object"]["id"],
+ "object" => object.data["id"],
"published_at" => DateTime.utc_now() |> DateTime.to_iso8601()
}
diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs
index 8b3233729..c99726180 100644
--- a/test/web/activity_pub/activity_pub_controller_test.exs
+++ b/test/web/activity_pub/activity_pub_controller_test.exs
@@ -163,7 +163,8 @@ test "it returns 404 for tombstone objects", %{conn: conn} do
describe "/object/:uuid/likes" do
test "it returns the like activities in a collection", %{conn: conn} do
like = insert(:like_activity)
- uuid = String.split(like.data["object"], "/") |> List.last()
+ like_object_ap_id = Object.normalize(like).data["id"]
+ uuid = String.split(like_object_ap_id, "/") |> List.last()
result =
conn
@@ -302,6 +303,7 @@ test "it rejects reads from other users", %{conn: conn} do
test "it returns a note activity in a collection", %{conn: conn} do
note_activity = insert(:direct_note_activity)
+ note_object = Object.normalize(note_activity)
user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
conn =
@@ -310,7 +312,7 @@ test "it returns a note activity in a collection", %{conn: conn} do
|> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}/inbox")
- assert response(conn, 200) =~ note_activity.data["object"]["content"]
+ assert response(conn, 200) =~ note_object.data["content"]
end
test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do
@@ -388,6 +390,7 @@ test "it will not bomb when there is no activity", %{conn: conn} do
test "it returns a note activity in a collection", %{conn: conn} do
note_activity = insert(:note_activity)
+ note_object = Object.normalize(note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
conn =
@@ -395,7 +398,7 @@ test "it returns a note activity in a collection", %{conn: conn} do
|> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}/outbox")
- assert response(conn, 200) =~ note_activity.data["object"]["content"]
+ assert response(conn, 200) =~ note_object.data["content"]
end
test "it returns an announce activity in a collection", %{conn: conn} do
@@ -457,12 +460,13 @@ test "it rejects an incoming activity with bogus type", %{conn: conn} do
test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
note_activity = insert(:note_activity)
+ note_object = Object.normalize(note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
data = %{
type: "Delete",
object: %{
- id: note_activity.data["object"]["id"]
+ id: note_object.data["id"]
}
}
@@ -475,19 +479,19 @@ test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
result = json_response(conn, 201)
assert Activity.get_by_ap_id(result["id"])
- object = Object.get_by_ap_id(note_activity.data["object"]["id"])
- assert object
+ assert object = Object.get_by_ap_id(note_object.data["id"])
assert object.data["type"] == "Tombstone"
end
test "it rejects delete activity of object from other actor", %{conn: conn} do
note_activity = insert(:note_activity)
+ note_object = Object.normalize(note_activity)
user = insert(:user)
data = %{
type: "Delete",
object: %{
- id: note_activity.data["object"]["id"]
+ id: note_object.data["id"]
}
}
@@ -502,12 +506,13 @@ test "it rejects delete activity of object from other actor", %{conn: conn} do
test "it increases like count when receiving a like action", %{conn: conn} do
note_activity = insert(:note_activity)
+ note_object = Object.normalize(note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
data = %{
type: "Like",
object: %{
- id: note_activity.data["object"]["id"]
+ id: note_object.data["id"]
}
}
@@ -520,8 +525,7 @@ test "it increases like count when receiving a like action", %{conn: conn} do
result = json_response(conn, 201)
assert Activity.get_by_ap_id(result["id"])
- object = Object.get_by_ap_id(note_activity.data["object"]["id"])
- assert object
+ assert object = Object.get_by_ap_id(note_object.data["id"])
assert object.data["like_count"] == 1
end
end
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index 76586ee4a..2728fef25 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -254,10 +254,8 @@ test "adds an id to a given object if it lacks one and is a note and inserts it
}
{:ok, %Activity{} = activity} = ActivityPub.insert(data)
- object = Object.normalize(activity.data["object"])
-
+ assert object = Object.normalize(activity)
assert is_binary(object.data["id"])
- assert %Object{} = Object.get_by_ap_id(activity.data["object"])
end
end
@@ -659,7 +657,8 @@ test "returns reblogs for users for whom reblogs have not been muted" do
describe "like an object" do
test "adds a like activity to the db" do
note_activity = insert(:note_activity)
- object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+ assert object = Object.normalize(note_activity)
+
user = insert(:user)
user_two = insert(:user)
@@ -667,7 +666,7 @@ test "adds a like activity to the db" do
assert like_activity.data["actor"] == user.ap_id
assert like_activity.data["type"] == "Like"
- assert like_activity.data["object"] == object.data["id"]
+ # assert like_activity.data["object"] == object.data["id"]
assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]]
assert like_activity.data["context"] == object.data["context"]
assert object.data["like_count"] == 1
@@ -678,9 +677,7 @@ test "adds a like activity to the db" do
assert like_activity == same_like_activity
assert object.data["likes"] == [user.ap_id]
-
- [note_activity] = Activity.get_all_create_by_object_ap_id(object.data["id"])
- assert note_activity.data["object"]["like_count"] == 1
+ assert object.data["like_count"] == 1
{:ok, _like_activity, object} = ActivityPub.like(user_two, object)
assert object.data["like_count"] == 2
@@ -690,7 +687,7 @@ test "adds a like activity to the db" do
describe "unliking" do
test "unliking a previously liked object" do
note_activity = insert(:note_activity)
- object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+ object = Object.normalize(note_activity)
user = insert(:user)
# Unliking something that hasn't been liked does nothing
@@ -710,7 +707,7 @@ test "unliking a previously liked object" do
describe "announcing an object" do
test "adds an announce activity to the db" do
note_activity = insert(:note_activity)
- object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+ object = Object.normalize(note_activity)
user = insert(:user)
{:ok, announce_activity, object} = ActivityPub.announce(user, object)
@@ -731,7 +728,7 @@ test "adds an announce activity to the db" do
describe "unannouncing an object" do
test "unannouncing a previously announced object" do
note_activity = insert(:note_activity)
- object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+ object = Object.normalize(note_activity)
user = insert(:user)
# Unannouncing an object that is not announced does nothing
@@ -810,10 +807,11 @@ test "creates an undo activity for the last follow" do
assert activity.data["type"] == "Undo"
assert activity.data["actor"] == follower.ap_id
- assert is_map(activity.data["object"])
- assert activity.data["object"]["type"] == "Follow"
- assert activity.data["object"]["object"] == followed.ap_id
- assert activity.data["object"]["id"] == follow_activity.data["id"]
+ embedded_object = activity.data["object"]
+ assert is_map(embedded_object)
+ assert embedded_object["type"] == "Follow"
+ assert embedded_object["object"] == followed.ap_id
+ assert embedded_object["id"] == follow_activity.data["id"]
end
end
@@ -839,22 +837,23 @@ test "creates an undo activity for the last block" do
assert activity.data["type"] == "Undo"
assert activity.data["actor"] == blocker.ap_id
- assert is_map(activity.data["object"])
- assert activity.data["object"]["type"] == "Block"
- assert activity.data["object"]["object"] == blocked.ap_id
- assert activity.data["object"]["id"] == block_activity.data["id"]
+ embedded_object = activity.data["object"]
+ assert is_map(embedded_object)
+ assert embedded_object["type"] == "Block"
+ assert embedded_object["object"] == blocked.ap_id
+ assert embedded_object["id"] == block_activity.data["id"]
end
end
describe "deletion" do
test "it creates a delete activity and deletes the original object" do
note = insert(:note_activity)
- object = Object.get_by_ap_id(note.data["object"]["id"])
+ object = Object.normalize(note)
{:ok, delete} = ActivityPub.delete(object)
assert delete.data["type"] == "Delete"
assert delete.data["actor"] == note.data["actor"]
- assert delete.data["object"] == note.data["object"]["id"]
+ assert delete.data["object"] == object.data["id"]
assert Activity.get_by_id(delete.id) != nil
@@ -900,13 +899,14 @@ test "decrements user note count only for public activities" do
test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
user = insert(:user)
note = insert(:note_activity)
+ object = Object.normalize(note)
{:ok, object} =
- Object.get_by_ap_id(note.data["object"]["id"])
+ object
|> Object.change(%{
data: %{
- "actor" => note.data["object"]["actor"],
- "id" => note.data["object"]["id"],
+ "actor" => object.data["actor"],
+ "id" => object.data["id"],
"to" => [user.ap_id],
"type" => "Note"
}
@@ -1018,8 +1018,9 @@ test "it creates an update activity with the new user data" do
assert update.data["actor"] == user.ap_id
assert update.data["to"] == [user.follower_address]
- assert update.data["object"]["id"] == user_data["id"]
- assert update.data["object"]["type"] == user_data["type"]
+ assert embedded_object = update.data["object"]
+ assert embedded_object["id"] == user_data["id"]
+ assert embedded_object["type"] == user_data["type"]
end
end
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
index 68ec03c33..e0ab7b4c6 100644
--- a/test/web/activity_pub/transmogrifier_test.exs
+++ b/test/web/activity_pub/transmogrifier_test.exs
@@ -30,7 +30,7 @@ test "it ignores an incoming notice if we already have it" do
data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()
- |> Map.put("object", activity.data["object"])
+ |> Map.put("object", Object.normalize(activity).data)
{:ok, returned_activity} = Transmogrifier.handle_incoming(data)
@@ -51,7 +51,7 @@ test "it fetches replied-to activities if we don't have them" do
|> Map.put("object", object)
{:ok, returned_activity} = Transmogrifier.handle_incoming(data)
- returned_object = Object.normalize(returned_activity.data["object"])
+ returned_object = Object.normalize(returned_activity)
assert activity =
Activity.get_create_by_object_ap_id(
@@ -99,25 +99,27 @@ test "it works for incoming notices" do
assert data["actor"] == "http://mastodon.example.org/users/admin"
- object = Object.normalize(data["object"]).data
- assert object["id"] == "http://mastodon.example.org/users/admin/statuses/99512778738411822"
+ object_data = Object.normalize(data["object"]).data
- assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
+ assert object_data["id"] ==
+ "http://mastodon.example.org/users/admin/statuses/99512778738411822"
- assert object["cc"] == [
+ assert object_data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
+
+ assert object_data["cc"] == [
"http://mastodon.example.org/users/admin/followers",
"http://localtesting.pleroma.lol/users/lain"
]
- assert object["actor"] == "http://mastodon.example.org/users/admin"
- assert object["attributedTo"] == "http://mastodon.example.org/users/admin"
+ assert object_data["actor"] == "http://mastodon.example.org/users/admin"
+ assert object_data["attributedTo"] == "http://mastodon.example.org/users/admin"
- assert object["context"] ==
+ assert object_data["context"] ==
"tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
- assert object["sensitive"] == true
+ assert object_data["sensitive"] == true
- user = User.get_cached_by_ap_id(object["actor"])
+ user = User.get_cached_by_ap_id(object_data["actor"])
assert user.info.note_count == 1
end
@@ -548,10 +550,11 @@ test "it works for incoming unannounces with an existing notice" do
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
assert data["type"] == "Undo"
- assert data["object"]["type"] == "Announce"
- assert data["object"]["object"] == activity.data["object"]
+ assert object_data = data["object"]
+ assert object_data["type"] == "Announce"
+ assert object_data["object"] == activity.data["object"]
- assert data["object"]["id"] ==
+ assert object_data["id"] ==
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
end
@@ -861,7 +864,7 @@ test "it accepts Flag activities" do
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
message = %{
"@context" => "https://www.w3.org/ns/activitystreams",
diff --git a/test/web/activity_pub/views/object_view_test.exs b/test/web/activity_pub/views/object_view_test.exs
index d939fc5a7..281f96e1e 100644
--- a/test/web/activity_pub/views/object_view_test.exs
+++ b/test/web/activity_pub/views/object_view_test.exs
@@ -2,6 +2,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
use Pleroma.DataCase
import Pleroma.Factory
+ alias Pleroma.Object
alias Pleroma.Web.ActivityPub.ObjectView
alias Pleroma.Web.CommonAPI
@@ -19,19 +20,21 @@ test "renders a note object" do
test "renders a note activity" do
note = insert(:note_activity)
+ object = Pleroma.Object.normalize(note)
result = ObjectView.render("object.json", %{object: note})
assert result["id"] == note.data["id"]
assert result["to"] == note.data["to"]
assert result["object"]["type"] == "Note"
- assert result["object"]["content"] == note.data["object"]["content"]
+ assert result["object"]["content"] == object.data["content"]
assert result["type"] == "Create"
assert result["@context"]
end
test "renders a like activity" do
note = insert(:note_activity)
+ object = Object.normalize(note)
user = insert(:user)
{:ok, like_activity, _} = CommonAPI.favorite(note.id, user)
@@ -39,12 +42,13 @@ test "renders a like activity" do
result = ObjectView.render("object.json", %{object: like_activity})
assert result["id"] == like_activity.data["id"]
- assert result["object"] == note.data["object"]["id"]
+ assert result["object"] == object.data["id"]
assert result["type"] == "Like"
end
test "renders an announce activity" do
note = insert(:note_activity)
+ object = Object.normalize(note)
user = insert(:user)
{:ok, announce_activity, _} = CommonAPI.repeat(note.id, user)
@@ -52,7 +56,7 @@ test "renders an announce activity" do
result = ObjectView.render("object.json", %{object: announce_activity})
assert result["id"] == announce_activity.data["id"]
- assert result["object"] == note.data["object"]["id"]
+ assert result["object"] == object.data["id"]
assert result["type"] == "Announce"
end
end
diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs
index 6f57bbe1f..958c931c4 100644
--- a/test/web/common_api/common_api_test.exs
+++ b/test/web/common_api/common_api_test.exs
@@ -34,7 +34,7 @@ test "it de-duplicates tags" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu #2HU"})
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
assert object.data["tag"] == ["2hu"]
end
@@ -87,7 +87,7 @@ test "it filters out obviously bad tags when accepting a post as HTML" do
"content_type" => "text/html"
})
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
assert object.data["content"] == "2hu
alert('xss')"
end
@@ -103,7 +103,7 @@ test "it filters out obviously bad tags when accepting a post as Markdown" do
"content_type" => "text/markdown"
})
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
assert object.data["content"] == "2hu
alert('xss')"
end
diff --git a/test/web/mastodon_api/status_view_test.exs b/test/web/mastodon_api/status_view_test.exs
index ec75150ab..f637097b8 100644
--- a/test/web/mastodon_api/status_view_test.exs
+++ b/test/web/mastodon_api/status_view_test.exs
@@ -55,7 +55,7 @@ test "tries to get a user by nickname if fetching by ap_id doesn't work" do
test "a note with null content" do
note = insert(:note_activity)
- note_object = Object.normalize(note.data["object"])
+ note_object = Object.normalize(note)
data =
note_object.data
@@ -73,26 +73,27 @@ test "a note with null content" do
test "a note activity" do
note = insert(:note_activity)
+ object_data = Object.normalize(note).data
user = User.get_cached_by_ap_id(note.data["actor"])
- convo_id = Utils.context_to_conversation_id(note.data["object"]["context"])
+ convo_id = Utils.context_to_conversation_id(object_data["context"])
status = StatusView.render("status.json", %{activity: note})
created_at =
- (note.data["object"]["published"] || "")
+ (object_data["published"] || "")
|> String.replace(~r/\.\d+Z/, ".000Z")
expected = %{
id: to_string(note.id),
- uri: note.data["object"]["id"],
+ uri: object_data["id"],
url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note),
account: AccountView.render("account.json", %{user: user}),
in_reply_to_id: nil,
in_reply_to_account_id: nil,
card: nil,
reblog: nil,
- content: HtmlSanitizeEx.basic_html(note.data["object"]["content"]),
+ content: HtmlSanitizeEx.basic_html(object_data["content"]),
created_at: created_at,
reblogs_count: 0,
replies_count: 0,
@@ -104,14 +105,14 @@ test "a note activity" do
pinned: false,
sensitive: false,
poll: nil,
- spoiler_text: HtmlSanitizeEx.basic_html(note.data["object"]["summary"]),
+ spoiler_text: HtmlSanitizeEx.basic_html(object_data["summary"]),
visibility: "public",
media_attachments: [],
mentions: [],
tags: [
%{
- name: "#{note.data["object"]["tag"]}",
- url: "/tag/#{note.data["object"]["tag"]}"
+ name: "#{object_data["tag"]}",
+ url: "/tag/#{object_data["tag"]}"
}
],
application: %{
@@ -131,8 +132,8 @@ test "a note activity" do
local: true,
conversation_id: convo_id,
in_reply_to_account_acct: nil,
- content: %{"text/plain" => HtmlSanitizeEx.strip_tags(note.data["object"]["content"])},
- spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(note.data["object"]["summary"])}
+ content: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["content"])},
+ spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["summary"])}
}
}
diff --git a/test/web/ostatus/activity_representer_test.exs b/test/web/ostatus/activity_representer_test.exs
index 16ee02abb..a3a92ce5b 100644
--- a/test/web/ostatus/activity_representer_test.exs
+++ b/test/web/ostatus/activity_representer_test.exs
@@ -38,22 +38,23 @@ test "an external note activity" do
test "a note activity" do
note_activity = insert(:note_activity)
+ object_data = Object.normalize(note_activity).data
user = User.get_cached_by_ap_id(note_activity.data["actor"])
expected = """
http://activitystrea.ms/schema/1.0/note
http://activitystrea.ms/schema/1.0/post
- #{note_activity.data["object"]["id"]}
+ #{object_data["id"]}
New note by #{user.nickname}
- #{note_activity.data["object"]["content"]}
- #{note_activity.data["object"]["published"]}
- #{note_activity.data["object"]["published"]}
+ #{object_data["content"]}
+ #{object_data["published"]}
+ #{object_data["published"]}
#{note_activity.data["context"]}
- #{note_activity.data["object"]["summary"]}
-
-
+ #{object_data["summary"]}
+
+
@@ -106,7 +107,7 @@ test "a reply note" do
test "an announce activity" do
note = insert(:note_activity)
user = insert(:user)
- object = Object.get_cached_by_ap_id(note.data["object"]["id"])
+ object = Object.normalize(note)
{:ok, announce, _object} = ActivityPub.announce(user, object)
@@ -125,7 +126,7 @@ test "an announce activity" do
http://activitystrea.ms/schema/1.0/share
#{announce.data["id"]}
#{user.nickname} repeated a notice
- RT #{note.data["object"]["content"]}
+ RT #{object.data["content"]}
#{announce.data["published"]}
#{announce.data["published"]}
#{announce.data["context"]}
diff --git a/test/web/ostatus/incoming_documents/delete_handling_test.exs b/test/web/ostatus/incoming_documents/delete_handling_test.exs
index ca6e61339..1fe714d00 100644
--- a/test/web/ostatus/incoming_documents/delete_handling_test.exs
+++ b/test/web/ostatus/incoming_documents/delete_handling_test.exs
@@ -17,8 +17,9 @@ defmodule Pleroma.Web.OStatus.DeleteHandlingTest do
test "it removes the mentioned activity" do
note = insert(:note_activity)
second_note = insert(:note_activity)
+ object = Object.normalize(note)
+ second_object = Object.normalize(second_note)
user = insert(:user)
- object = Object.get_by_ap_id(note.data["object"]["id"])
{:ok, like, _object} = Pleroma.Web.ActivityPub.ActivityPub.like(user, object)
@@ -26,16 +27,16 @@ test "it removes the mentioned activity" do
File.read!("test/fixtures/delete.xml")
|> String.replace(
"tag:mastodon.sdf.org,2017-06-10:objectId=310513:objectType=Status",
- note.data["object"]["id"]
+ object.data["id"]
)
{:ok, [delete]} = OStatus.handle_incoming(incoming)
refute Activity.get_by_id(note.id)
refute Activity.get_by_id(like.id)
- assert Object.get_by_ap_id(note.data["object"]["id"]).data["type"] == "Tombstone"
+ assert Object.get_by_ap_id(object.data["id"]).data["type"] == "Tombstone"
assert Activity.get_by_id(second_note.id)
- assert Object.get_by_ap_id(second_note.data["object"]["id"])
+ assert Object.get_by_ap_id(second_object.data["id"])
assert delete.data["type"] == "Delete"
end
diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs
index 7441e5fce..9e958f6ca 100644
--- a/test/web/ostatus/ostatus_controller_test.exs
+++ b/test/web/ostatus/ostatus_controller_test.exs
@@ -65,6 +65,7 @@ test "decodes a salmon with a changed magic key", %{conn: conn} do
test "gets a feed", %{conn: conn} do
note_activity = insert(:note_activity)
+ object = Object.normalize(note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
conn =
@@ -72,7 +73,7 @@ test "gets a feed", %{conn: conn} do
|> put_req_header("content-type", "application/atom+xml")
|> get("/users/#{user.nickname}/feed.atom")
- assert response(conn, 200) =~ note_activity.data["object"]["content"]
+ assert response(conn, 200) =~ object.data["content"]
end
test "returns 404 for a missing feed", %{conn: conn} do
@@ -86,8 +87,9 @@ test "returns 404 for a missing feed", %{conn: conn} do
test "gets an object", %{conn: conn} do
note_activity = insert(:note_activity)
+ object = Object.normalize(note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
- [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["object"]["id"]))
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
url = "/objects/#{uuid}"
conn =
@@ -106,7 +108,8 @@ test "gets an object", %{conn: conn} do
test "404s on private objects", %{conn: conn} do
note_activity = insert(:direct_note_activity)
- [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["object"]["id"]))
+ object = Object.normalize(note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
conn
|> get("/objects/#{uuid}")
@@ -131,8 +134,8 @@ test "gets an activity in xml format", %{conn: conn} do
test "404s on deleted objects", %{conn: conn} do
note_activity = insert(:note_activity)
- [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["object"]["id"]))
- object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+ object = Object.normalize(note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
conn
|> put_req_header("accept", "application/xml")
diff --git a/test/web/ostatus/ostatus_test.exs b/test/web/ostatus/ostatus_test.exs
index f6be16862..e9ca31bc4 100644
--- a/test/web/ostatus/ostatus_test.exs
+++ b/test/web/ostatus/ostatus_test.exs
@@ -28,7 +28,7 @@ test "don't insert create notes twice" do
test "handle incoming note - GS, Salmon" do
incoming = File.read!("test/fixtures/incoming_note_activity.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
user = User.get_cached_by_ap_id(activity.data["actor"])
assert user.info.note_count == 1
@@ -51,7 +51,7 @@ test "handle incoming note - GS, Salmon" do
test "handle incoming notes - GS, subscription" do
incoming = File.read!("test/fixtures/ostatus_incoming_post.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
assert activity.data["type"] == "Create"
assert object.data["type"] == "Note"
@@ -65,7 +65,7 @@ test "handle incoming notes - GS, subscription" do
test "handle incoming notes with attachments - GS, subscription" do
incoming = File.read!("test/fixtures/incoming_websub_gnusocial_attachments.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
assert activity.data["type"] == "Create"
assert object.data["type"] == "Note"
@@ -78,7 +78,7 @@ test "handle incoming notes with attachments - GS, subscription" do
test "handle incoming notes with tags" do
incoming = File.read!("test/fixtures/ostatus_incoming_post_tag.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
assert object.data["tag"] == ["nsfw"]
assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
@@ -95,7 +95,7 @@ test "handle incoming notes - Mastodon, salmon, reply" do
incoming = File.read!("test/fixtures/incoming_reply_mastodon.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
assert activity.data["type"] == "Create"
assert object.data["type"] == "Note"
@@ -107,7 +107,7 @@ test "handle incoming notes - Mastodon, salmon, reply" do
test "handle incoming notes - Mastodon, with CW" do
incoming = File.read!("test/fixtures/mastodon-note-cw.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
assert activity.data["type"] == "Create"
assert object.data["type"] == "Note"
@@ -119,7 +119,7 @@ test "handle incoming notes - Mastodon, with CW" do
test "handle incoming unlisted messages, put public into cc" do
incoming = File.read!("test/fixtures/mastodon-note-unlisted.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
refute "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["cc"]
@@ -130,7 +130,7 @@ test "handle incoming unlisted messages, put public into cc" do
test "handle incoming retweets - Mastodon, with CW" do
incoming = File.read!("test/fixtures/cw_retweet.xml")
{:ok, [[_activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
- retweeted_object = Object.normalize(retweeted_activity.data["object"])
+ retweeted_object = Object.normalize(retweeted_activity)
assert retweeted_object.data["summary"] == "Hey."
end
@@ -138,7 +138,7 @@ test "handle incoming retweets - Mastodon, with CW" do
test "handle incoming notes - GS, subscription, reply" do
incoming = File.read!("test/fixtures/ostatus_incoming_reply.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
assert activity.data["type"] == "Create"
assert object.data["type"] == "Note"
@@ -164,7 +164,7 @@ test "handle incoming retweets - GS, subscription" do
refute activity.local
retweeted_activity = Activity.get_by_id(retweeted_activity.id)
- retweeted_object = Object.normalize(retweeted_activity.data["object"])
+ retweeted_object = Object.normalize(retweeted_activity)
assert retweeted_activity.data["type"] == "Create"
assert retweeted_activity.data["actor"] == "https://pleroma.soykaf.com/users/lain"
refute retweeted_activity.local
@@ -176,18 +176,19 @@ test "handle incoming retweets - GS, subscription" do
test "handle incoming retweets - GS, subscription - local message" do
incoming = File.read!("test/fixtures/share-gs-local.xml")
note_activity = insert(:note_activity)
+ object = Object.normalize(note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
incoming =
incoming
- |> String.replace("LOCAL_ID", note_activity.data["object"]["id"])
+ |> String.replace("LOCAL_ID", object.data["id"])
|> String.replace("LOCAL_USER", user.ap_id)
{:ok, [[activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
assert activity.data["type"] == "Announce"
assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
- assert activity.data["object"] == retweeted_activity.data["object"]["id"]
+ assert activity.data["object"] == object.data["id"]
assert user.ap_id in activity.data["to"]
refute activity.local
@@ -202,7 +203,7 @@ test "handle incoming retweets - GS, subscription - local message" do
test "handle incoming retweets - Mastodon, salmon" do
incoming = File.read!("test/fixtures/share.xml")
{:ok, [[activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
- retweeted_object = Object.normalize(retweeted_activity.data["object"])
+ retweeted_object = Object.normalize(retweeted_activity)
assert activity.data["type"] == "Announce"
assert activity.data["actor"] == "https://mastodon.social/users/lambadalambda"
@@ -251,16 +252,17 @@ test "handle conversation references" do
test "handle incoming favorites with locally available object - GS, websub" do
note_activity = insert(:note_activity)
+ object = Object.normalize(note_activity)
incoming =
File.read!("test/fixtures/favorite_with_local_note.xml")
- |> String.replace("localid", note_activity.data["object"]["id"])
+ |> String.replace("localid", object.data["id"])
{:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming)
assert activity.data["type"] == "Like"
assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
- assert activity.data["object"] == favorited_activity.data["object"]["id"]
+ assert activity.data["object"] == object.data["id"]
refute activity.local
assert note_activity.id == favorited_activity.id
assert favorited_activity.local
@@ -269,7 +271,7 @@ test "handle incoming favorites with locally available object - GS, websub" do
test "handle incoming replies" do
incoming = File.read!("test/fixtures/incoming_note_activity_answer.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
assert activity.data["type"] == "Create"
assert object.data["type"] == "Note"
@@ -315,13 +317,14 @@ test "handle incoming unfollows with existing follow" do
"undo:tag:social.heldscal.la,2017-05-07:subscription:23211:person:44803:2017-05-07T09:54:48+00:00"
assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
- assert is_map(activity.data["object"])
- assert activity.data["object"]["type"] == "Follow"
- assert activity.data["object"]["object"] == "https://pawoo.net/users/pekorino"
+ embedded_object = activity.data["object"]
+ assert is_map(embedded_object)
+ assert embedded_object["type"] == "Follow"
+ assert embedded_object["object"] == "https://pawoo.net/users/pekorino"
refute activity.local
follower = User.get_cached_by_ap_id(activity.data["actor"])
- followed = User.get_cached_by_ap_id(activity.data["object"]["object"])
+ followed = User.get_cached_by_ap_id(embedded_object["object"])
refute User.following?(follower, followed)
end
@@ -538,8 +541,7 @@ test "Note objects are representable" do
test "Article objects are not representable" do
note_activity = insert(:note_activity)
-
- note_object = Object.normalize(note_activity.data["object"])
+ note_object = Object.normalize(note_activity)
note_data =
note_object.data
diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs
index 8187ffd0e..8be289789 100644
--- a/test/web/twitter_api/twitter_api_controller_test.exs
+++ b/test/web/twitter_api/twitter_api_controller_test.exs
@@ -892,7 +892,7 @@ test "without valid credentials", %{conn: conn} do
test "with credentials", %{conn: conn, user: current_user} do
note_activity = insert(:note_activity)
- object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+ object = Object.normalize(note_activity)
ActivityPub.like(current_user, object)
conn =
diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs
index 475531a09..cbe83852e 100644
--- a/test/web/twitter_api/twitter_api_test.exs
+++ b/test/web/twitter_api/twitter_api_test.exs
@@ -46,7 +46,7 @@ test "create a status" do
}
{:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input)
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
expected_text =
"Hello again, @shp .<script></script> This is on another :firefox: line. #2hu #epic #phantasmagoric image.jpg "
@@ -91,7 +91,7 @@ test "create a status that is a reply" do
}
{:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input)
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
input = %{
"status" => "Here's your (you).",
@@ -99,7 +99,7 @@ test "create a status that is a reply" do
}
{:ok, reply = %Activity{}} = TwitterAPI.create_status(user, input)
- reply_object = Object.normalize(reply.data["object"])
+ reply_object = Object.normalize(reply)
assert get_in(reply.data, ["context"]) == get_in(activity.data, ["context"])
@@ -216,7 +216,7 @@ test "it favorites a status, returns the updated activity" do
updated_activity = Activity.get_by_ap_id(note_activity.data["id"])
assert ActivityView.render("activity.json", %{activity: updated_activity})["fave_num"] == 1
- object = Object.normalize(note_activity.data["object"])
+ object = Object.normalize(note_activity)
assert object.data["like_count"] == 1
@@ -224,7 +224,7 @@ test "it favorites a status, returns the updated activity" do
{:ok, _status} = TwitterAPI.fav(other_user, note_activity.id)
- object = Object.normalize(note_activity.data["object"])
+ object = Object.normalize(note_activity)
assert object.data["like_count"] == 2
@@ -235,7 +235,7 @@ test "it favorites a status, returns the updated activity" do
test "it unfavorites a status, returns the updated activity" do
user = insert(:user)
note_activity = insert(:note_activity)
- object = Object.get_by_ap_id(note_activity.data["object"]["id"])
+ object = Object.normalize(note_activity)
{:ok, _like_activity, _object} = ActivityPub.like(user, object)
updated_activity = Activity.get_by_ap_id(note_activity.data["id"])
diff --git a/test/web/twitter_api/views/activity_view_test.exs b/test/web/twitter_api/views/activity_view_test.exs
index 43bd77f78..56d861efb 100644
--- a/test/web/twitter_api/views/activity_view_test.exs
+++ b/test/web/twitter_api/views/activity_view_test.exs
@@ -126,7 +126,7 @@ test "a create activity with a note" do
other_user = insert(:user, %{nickname: "shp"})
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
result = ActivityView.render("activity.json", activity: activity)
@@ -177,7 +177,7 @@ test "a list of activities" do
user = insert(:user)
other_user = insert(:user, %{nickname: "shp"})
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity)
convo_id = Utils.context_to_conversation_id(object.data["context"])
@@ -351,7 +351,7 @@ test "a delete activity" do
"is_post_verb" => false,
"statusnet_html" => "deleted notice {{tag",
"text" => "deleted notice {{tag",
- "uri" => delete.data["object"],
+ "uri" => Object.normalize(delete).data["id"],
"user" => UserView.render("show.json", user: user)
}
From 46cf81a544edd91f4c3893897fbe2db053f5f6d5 Mon Sep 17 00:00:00 2001
From: Ivan Tashkinov
Date: Mon, 8 Jul 2019 20:05:02 +0300
Subject: [PATCH 02/31] [#878] Formatting fix.
---
test/support/factory.ex | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/support/factory.ex b/test/support/factory.ex
index b1023da38..0e3c900c9 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -4,8 +4,8 @@
defmodule Pleroma.Factory do
use ExMachina.Ecto, repo: Pleroma.Repo
- alias Pleroma.User
alias Pleroma.Object
+ alias Pleroma.User
def participation_factory do
conversation = insert(:conversation)
From c91b5c87ffee82fcfe8e088b76025857caed61b8 Mon Sep 17 00:00:00 2001
From: feld
Date: Tue, 9 Jul 2019 18:20:36 +0000
Subject: [PATCH 03/31] Docs/more mastodon api
---
CHANGELOG.md | 2 +-
docs/api/differences_in_mastoapi_responses.md | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b92129849..227f721e3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
Configuration: `federation_incoming_replies_max_depth` option
- Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses)
- Mastodon API, streaming: Add support for passing the token in the `Sec-WebSocket-Protocol` header
+- Mastodon API, extension: Ability to reset avatar, profile banner, and background
- Admin API: Return users' tags when querying reports
- Admin API: Return avatar and display name when querying users
- Admin API: Allow querying user by ID
@@ -86,7 +87,6 @@ Configuration: `federation_incoming_replies_max_depth` option
- OAuth: added job to clean expired access tokens
- MRF: Support for rejecting reports from specific instances (`mrf_simple`)
- MRF: Support for stripping avatars and banner images from specific instances (`mrf_simple`)
-- Ability to reset avatar, profile banner and backgroud
- MRF: Support for running subchains.
- Configuration: `skip_thread_containment` option
- Configuration: `rate_limit` option. See `Pleroma.Plugs.RateLimiter` documentation for details.
diff --git a/docs/api/differences_in_mastoapi_responses.md b/docs/api/differences_in_mastoapi_responses.md
index 3ee7115cf..2cbe1458d 100644
--- a/docs/api/differences_in_mastoapi_responses.md
+++ b/docs/api/differences_in_mastoapi_responses.md
@@ -46,6 +46,14 @@ Has these additional fields under the `pleroma` object:
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
- `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
+### Extensions for PleromaFE
+
+These endpoints added for controlling PleromaFE features over the Mastodon API
+
+- PATCH `/api/v1/accounts/update_avatar`: Set/clear user avatar image
+- PATCH `/api/v1/accounts/update_banner`: Set/clear user banner image
+- PATCH `/api/v1/accounts/update_background`: Set/clear user background image
+
### Source
Has these additional fields under the `pleroma` object:
From 6fc0c27be3118ac35c075a1ad9bbcd7ff6901704 Mon Sep 17 00:00:00 2001
From: Ivan Tashkinov
Date: Tue, 9 Jul 2019 22:28:04 +0300
Subject: [PATCH 04/31] [#878] Uncommented test statement.
---
test/web/activity_pub/activity_pub_test.exs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index 2728fef25..12e78e729 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -666,7 +666,7 @@ test "adds a like activity to the db" do
assert like_activity.data["actor"] == user.ap_id
assert like_activity.data["type"] == "Like"
- # assert like_activity.data["object"] == object.data["id"]
+ assert like_activity.data["object"] == object.data["id"]
assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]]
assert like_activity.data["context"] == object.data["context"]
assert object.data["like_count"] == 1
From 8a41d34673532c03cf99a2334399b9436e245f1b Mon Sep 17 00:00:00 2001
From: Ivan Tashkinov
Date: Tue, 9 Jul 2019 22:37:59 +0300
Subject: [PATCH 05/31] [#878] Tests improvements per code review.
---
test/web/activity_pub/activity_pub_test.exs | 6 ++++++
test/web/activity_pub/views/object_view_test.exs | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index 12e78e729..59d56f3a7 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -679,8 +679,14 @@ test "adds a like activity to the db" do
assert object.data["likes"] == [user.ap_id]
assert object.data["like_count"] == 1
+ [note_activity] = Activity.get_all_create_by_object_ap_id(object.data["id"])
+ assert note_activity.data["object"]["like_count"] == 1
+
{:ok, _like_activity, object} = ActivityPub.like(user_two, object)
assert object.data["like_count"] == 2
+
+ [note_activity] = Activity.get_all_create_by_object_ap_id(object.data["id"])
+ assert note_activity.data["object"]["like_count"] == 2
end
end
diff --git a/test/web/activity_pub/views/object_view_test.exs b/test/web/activity_pub/views/object_view_test.exs
index 281f96e1e..ac78c9cf1 100644
--- a/test/web/activity_pub/views/object_view_test.exs
+++ b/test/web/activity_pub/views/object_view_test.exs
@@ -20,7 +20,7 @@ test "renders a note object" do
test "renders a note activity" do
note = insert(:note_activity)
- object = Pleroma.Object.normalize(note)
+ object = Object.normalize(note)
result = ObjectView.render("object.json", %{object: note})
From 6d0ae264fc90508a6df39f77a11d1d8069bfa466 Mon Sep 17 00:00:00 2001
From: Sachin Joshi
Date: Wed, 10 Jul 2019 01:42:41 +0545
Subject: [PATCH 06/31] add listener port and ip option for 'pleroma.instance
gen' and enable its test
---
lib/mix/tasks/pleroma/instance.ex | 26 +++++++++++++++++--
priv/templates/sample_config.eex | 1 +
.../tasks/{instance.exs => instance_test.exs} | 15 +++++++++--
3 files changed, 38 insertions(+), 4 deletions(-)
rename test/tasks/{instance.exs => instance_test.exs} (83%)
diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex
index 2ae16adc0..9080adb52 100644
--- a/lib/mix/tasks/pleroma/instance.ex
+++ b/lib/mix/tasks/pleroma/instance.ex
@@ -34,6 +34,8 @@ defmodule Mix.Tasks.Pleroma.Instance do
- `--db-configurable Y/N` - Allow/disallow configuring instance from admin part
- `--uploads-dir` - the directory uploads go in when using a local uploader
- `--static-dir` - the directory custom public files should be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)
+ - `--listen-ip` - the ip the app should listen to, defaults to 127.0.0.1
+ - `--listen-port` - the port the app should listen to, defaults to 4000
"""
def run(["gen" | rest]) do
@@ -56,7 +58,9 @@ def run(["gen" | rest]) do
indexable: :string,
db_configurable: :string,
uploads_dir: :string,
- static_dir: :string
+ static_dir: :string,
+ listen_ip: :string,
+ listen_port: :string
],
aliases: [
o: :output,
@@ -146,6 +150,22 @@ def run(["gen" | rest]) do
"n"
) === "y"
+ listen_port =
+ get_option(
+ options,
+ :listen_port,
+ "What port will the app listen to (leave it if you are using the default setup with nginx)?",
+ 4000
+ )
+
+ listen_ip =
+ get_option(
+ options,
+ :listen_ip,
+ "What ip will the app listen to (leave it if you are using the default setup with nginx)?",
+ "127.0.0.1"
+ )
+
uploads_dir =
get_option(
options,
@@ -186,7 +206,9 @@ def run(["gen" | rest]) do
db_configurable?: db_configurable?,
static_dir: static_dir,
uploads_dir: uploads_dir,
- rum_enabled: rum_enabled
+ rum_enabled: rum_enabled,
+ listen_ip: listen_ip,
+ listen_port: listen_port
)
result_psql =
diff --git a/priv/templates/sample_config.eex b/priv/templates/sample_config.eex
index 5cc31c604..ca9c7a2c2 100644
--- a/priv/templates/sample_config.eex
+++ b/priv/templates/sample_config.eex
@@ -11,6 +11,7 @@ end %>
config :pleroma, Pleroma.Web.Endpoint,
url: [host: "<%= domain %>", scheme: "https", port: <%= port %>],
+ http: [ip: {<%= String.replace(listen_ip, ".", ", ") %>}, port: <%= listen_port %>],
secret_key_base: "<%= secret %>",
signing_salt: "<%= signing_salt %>"
diff --git a/test/tasks/instance.exs b/test/tasks/instance_test.exs
similarity index 83%
rename from test/tasks/instance.exs
rename to test/tasks/instance_test.exs
index 1875f52a3..229ecc9c1 100644
--- a/test/tasks/instance.exs
+++ b/test/tasks/instance_test.exs
@@ -38,7 +38,17 @@ test "running gen" do
"--indexable",
"y",
"--db-configurable",
- "y"
+ "y",
+ "--rum",
+ "y",
+ "--listen-port",
+ "4000",
+ "--listen-ip",
+ "127.0.0.1",
+ "--uploads-dir",
+ "test/uploads",
+ "--static-dir",
+ "instance/static/"
])
end
@@ -56,10 +66,11 @@ test "running gen" do
assert generated_config =~ "username: \"dbuser\""
assert generated_config =~ "password: \"dbpass\""
assert generated_config =~ "dynamic_configuration: true"
+ assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]"
assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql()
end
defp generated_setup_psql do
- ~s(CREATE USER dbuser WITH ENCRYPTED PASSWORD 'dbpass';\nCREATE DATABASE dbname OWNER dbuser;\n\\c dbname;\n--Extensions made by ecto.migrate that need superuser access\nCREATE EXTENSION IF NOT EXISTS citext;\nCREATE EXTENSION IF NOT EXISTS pg_trgm;\nCREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";\n)
+ ~s(CREATE USER dbuser WITH ENCRYPTED PASSWORD 'dbpass';\nCREATE DATABASE dbname OWNER dbuser;\n\\c dbname;\n--Extensions made by ecto.migrate that need superuser access\nCREATE EXTENSION IF NOT EXISTS citext;\nCREATE EXTENSION IF NOT EXISTS pg_trgm;\nCREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";\nCREATE EXTENSION IF NOT EXISTS rum;\n)
end
end
From bb8065a1fd41459961e9c03735de281fcee0eefe Mon Sep 17 00:00:00 2001
From: Maksim
Date: Wed, 10 Jul 2019 05:12:21 +0000
Subject: [PATCH 07/31] tests MRF filters
---
.../activity_pub/mrf/ensure_re_prepended.ex | 17 +--
.../mrf/no_placeholder_text_policy.ex | 12 +-
.../web/activity_pub/mrf/normalize_markup.ex | 10 +-
.../web/activity_pub/mrf/reject_non_public.ex | 36 +++--
.../web/activity_pub/mrf/tag_policy.ex | 53 +++++---
..._allowlist.ex => user_allowlist_policy.ex} | 7 +-
mix.lock | 1 +
.../mrf/ensure_re_prepended_test.exs | 82 ++++++++++++
.../mrf/no_placeholder_text_policy_test.exs | 37 ++++++
.../mrf/normalize_markup_test.exs | 42 ++++++
.../mrf/reject_non_public_test.exs | 105 +++++++++++++++
test/web/activity_pub/mrf/tag_policy_test.exs | 123 ++++++++++++++++++
.../mrf/user_allowlist_policy_test.exs | 36 +++++
13 files changed, 494 insertions(+), 67 deletions(-)
rename lib/pleroma/web/activity_pub/mrf/{user_allowlist.ex => user_allowlist_policy.ex} (86%)
create mode 100644 test/web/activity_pub/mrf/ensure_re_prepended_test.exs
create mode 100644 test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs
create mode 100644 test/web/activity_pub/mrf/normalize_markup_test.exs
create mode 100644 test/web/activity_pub/mrf/reject_non_public_test.exs
create mode 100644 test/web/activity_pub/mrf/tag_policy_test.exs
create mode 100644 test/web/activity_pub/mrf/user_allowlist_policy_test.exs
diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
index 15d8514be..2d03df68a 100644
--- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
+++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
@@ -9,8 +9,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
@behaviour Pleroma.Web.ActivityPub.MRF
@reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
+
def filter_by_summary(
- %{"summary" => parent_summary} = _parent,
+ %{data: %{"summary" => parent_summary}} = _in_reply_to,
%{"summary" => child_summary} = child
)
when not is_nil(child_summary) and byte_size(child_summary) > 0 and
@@ -24,17 +25,13 @@ def filter_by_summary(
end
end
- def filter_by_summary(_parent, child), do: child
-
- def filter(%{"type" => activity_type} = object) when activity_type == "Create" do
- child = object["object"]
- in_reply_to = Object.normalize(child["inReplyTo"])
+ def filter_by_summary(_in_reply_to, child), do: child
+ def filter(%{"type" => "Create", "object" => child_object} = object) do
child =
- if(in_reply_to,
- do: filter_by_summary(in_reply_to.data, child),
- else: child
- )
+ child_object["inReplyTo"]
+ |> Object.normalize(child_object["inReplyTo"])
+ |> filter_by_summary(child_object)
object = Map.put(object, "object", child)
diff --git a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
index f30fee0d5..86a48bda5 100644
--- a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
@@ -10,19 +10,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do
def filter(
%{
"type" => "Create",
- "object" => %{"content" => content, "attachment" => _attachment} = child_object
+ "object" => %{"content" => content, "attachment" => _} = _child_object
} = object
)
when content in [".", ".
"] do
- child_object =
- child_object
- |> Map.put("content", "")
-
- object =
- object
- |> Map.put("object", child_object)
-
- {:ok, object}
+ {:ok, put_in(object, ["object", "content"], "")}
end
@impl true
diff --git a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
index 9c87c6963..c269d0f89 100644
--- a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
+++ b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
@@ -8,18 +8,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
@behaviour Pleroma.Web.ActivityPub.MRF
- def filter(%{"type" => activity_type} = object) when activity_type == "Create" do
+ def filter(%{"type" => "Create", "object" => child_object} = object) do
scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy])
- child = object["object"]
-
content =
- child["content"]
+ child_object["content"]
|> HTML.filter_tags(scrub_policy)
- child = Map.put(child, "content", content)
-
- object = Map.put(object, "object", child)
+ object = put_in(object, ["object", "content"], content)
{:ok, object}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
index ea3df1b4d..da13fd7c7 100644
--- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
+++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
@@ -3,46 +3,42 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
- alias Pleroma.User
@moduledoc "Rejects non-public (followers-only, direct) activities"
+
+ alias Pleroma.Config
+ alias Pleroma.User
+
@behaviour Pleroma.Web.ActivityPub.MRF
+ @public "https://www.w3.org/ns/activitystreams#Public"
+
@impl true
def filter(%{"type" => "Create"} = object) do
user = User.get_cached_by_ap_id(object["actor"])
- public = "https://www.w3.org/ns/activitystreams#Public"
# Determine visibility
visibility =
cond do
- public in object["to"] -> "public"
- public in object["cc"] -> "unlisted"
+ @public in object["to"] -> "public"
+ @public in object["cc"] -> "unlisted"
user.follower_address in object["to"] -> "followers"
true -> "direct"
end
- policy = Pleroma.Config.get(:mrf_rejectnonpublic)
+ policy = Config.get(:mrf_rejectnonpublic)
- case visibility do
- "public" ->
+ cond do
+ visibility in ["public", "unlisted"] ->
{:ok, object}
- "unlisted" ->
+ visibility == "followers" and Keyword.get(policy, :allow_followersonly) ->
{:ok, object}
- "followers" ->
- with true <- Keyword.get(policy, :allow_followersonly) do
- {:ok, object}
- else
- _e -> {:reject, nil}
- end
+ visibility == "direct" and Keyword.get(policy, :allow_direct) ->
+ {:ok, object}
- "direct" ->
- with true <- Keyword.get(policy, :allow_direct) do
- {:ok, object}
- else
- _e -> {:reject, nil}
- end
+ true ->
+ {:reject, nil}
end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
index 6683b8d8e..b42c4ed76 100644
--- a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
@@ -19,12 +19,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
- `mrf_tag:disable-any-subscription`: Reject any follow requests
"""
+ @public "https://www.w3.org/ns/activitystreams#Public"
+
defp get_tags(%User{tags: tags}) when is_list(tags), do: tags
defp get_tags(_), do: []
defp process_tag(
"mrf_tag:media-force-nsfw",
- %{"type" => "Create", "object" => %{"attachment" => child_attachment} = object} = message
+ %{
+ "type" => "Create",
+ "object" => %{"attachment" => child_attachment} = object
+ } = message
)
when length(child_attachment) > 0 do
tags = (object["tag"] || []) ++ ["nsfw"]
@@ -41,7 +46,10 @@ defp process_tag(
defp process_tag(
"mrf_tag:media-strip",
- %{"type" => "Create", "object" => %{"attachment" => child_attachment} = object} = message
+ %{
+ "type" => "Create",
+ "object" => %{"attachment" => child_attachment} = object
+ } = message
)
when length(child_attachment) > 0 do
object = Map.delete(object, "attachment")
@@ -52,19 +60,22 @@ defp process_tag(
defp process_tag(
"mrf_tag:force-unlisted",
- %{"type" => "Create", "to" => to, "cc" => cc, "actor" => actor} = message
+ %{
+ "type" => "Create",
+ "to" => to,
+ "cc" => cc,
+ "actor" => actor,
+ "object" => object
+ } = message
) do
user = User.get_cached_by_ap_id(actor)
- if Enum.member?(to, "https://www.w3.org/ns/activitystreams#Public") do
- to =
- List.delete(to, "https://www.w3.org/ns/activitystreams#Public") ++ [user.follower_address]
-
- cc =
- List.delete(cc, user.follower_address) ++ ["https://www.w3.org/ns/activitystreams#Public"]
+ if Enum.member?(to, @public) do
+ to = List.delete(to, @public) ++ [user.follower_address]
+ cc = List.delete(cc, user.follower_address) ++ [@public]
object =
- message["object"]
+ object
|> Map.put("to", to)
|> Map.put("cc", cc)
@@ -82,19 +93,22 @@ defp process_tag(
defp process_tag(
"mrf_tag:sandbox",
- %{"type" => "Create", "to" => to, "cc" => cc, "actor" => actor} = message
+ %{
+ "type" => "Create",
+ "to" => to,
+ "cc" => cc,
+ "actor" => actor,
+ "object" => object
+ } = message
) do
user = User.get_cached_by_ap_id(actor)
- if Enum.member?(to, "https://www.w3.org/ns/activitystreams#Public") or
- Enum.member?(cc, "https://www.w3.org/ns/activitystreams#Public") do
- to =
- List.delete(to, "https://www.w3.org/ns/activitystreams#Public") ++ [user.follower_address]
-
- cc = List.delete(cc, "https://www.w3.org/ns/activitystreams#Public")
+ if Enum.member?(to, @public) or Enum.member?(cc, @public) do
+ to = List.delete(to, @public) ++ [user.follower_address]
+ cc = List.delete(cc, @public)
object =
- message["object"]
+ object
|> Map.put("to", to)
|> Map.put("cc", cc)
@@ -123,7 +137,8 @@ defp process_tag(
end
end
- defp process_tag("mrf_tag:disable-any-subscription", %{"type" => "Follow"}), do: {:reject, nil}
+ defp process_tag("mrf_tag:disable-any-subscription", %{"type" => "Follow"}),
+ do: {:reject, nil}
defp process_tag(_, message), do: {:ok, message}
diff --git a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex b/lib/pleroma/web/activity_pub/mrf/user_allowlist_policy.ex
similarity index 86%
rename from lib/pleroma/web/activity_pub/mrf/user_allowlist.ex
rename to lib/pleroma/web/activity_pub/mrf/user_allowlist_policy.ex
index 47663414a..e35d2c422 100644
--- a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex
+++ b/lib/pleroma/web/activity_pub/mrf/user_allowlist_policy.ex
@@ -21,7 +21,12 @@ defp filter_by_list(%{"actor" => actor} = object, allow_list) do
@impl true
def filter(%{"actor" => actor} = object) do
actor_info = URI.parse(actor)
- allow_list = Config.get([:mrf_user_allowlist, String.to_atom(actor_info.host)], [])
+
+ allow_list =
+ Config.get(
+ [:mrf_user_allowlist, String.to_atom(actor_info.host)],
+ []
+ )
filter_by_list(object, allow_list)
end
diff --git a/mix.lock b/mix.lock
index e711be635..bd6ab9100 100644
--- a/mix.lock
+++ b/mix.lock
@@ -76,6 +76,7 @@
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
"recon": {:git, "https://github.com/ferd/recon.git", "75d70c7c08926d2f24f1ee6de14ee50fe8a52763", [tag: "2.4.0"]},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
+ "stream_data": {:hex, :stream_data, "0.4.3", "62aafd870caff0849a5057a7ec270fad0eb86889f4d433b937d996de99e3db25", [:mix], [], "hexpm"},
"swoosh": {:hex, :swoosh, "0.20.0", "9a6c13822c9815993c03b6f8fccc370fcffb3c158d9754f67b1fdee6b3a5d928", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.12", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]},
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
diff --git a/test/web/activity_pub/mrf/ensure_re_prepended_test.exs b/test/web/activity_pub/mrf/ensure_re_prepended_test.exs
new file mode 100644
index 000000000..dbc8b9e80
--- /dev/null
+++ b/test/web/activity_pub/mrf/ensure_re_prepended_test.exs
@@ -0,0 +1,82 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrependedTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Activity
+ alias Pleroma.Object
+ alias Pleroma.Web.ActivityPub.MRF.EnsureRePrepended
+
+ describe "rewrites summary" do
+ test "it adds `re:` to summary object when child summary and parent summary equal" do
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "summary" => "object-summary",
+ "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}}
+ }
+ }
+
+ assert {:ok, res} = EnsureRePrepended.filter(message)
+ assert res["object"]["summary"] == "re: object-summary"
+ end
+
+ test "it adds `re:` to summary object when child summary containts re-subject of parent summary " do
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "summary" => "object-summary",
+ "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "re: object-summary"}}}
+ }
+ }
+
+ assert {:ok, res} = EnsureRePrepended.filter(message)
+ assert res["object"]["summary"] == "re: object-summary"
+ end
+ end
+
+ describe "skip filter" do
+ test "it skip if type isn't 'Create'" do
+ message = %{
+ "type" => "Annotation",
+ "object" => %{"summary" => "object-summary"}
+ }
+
+ assert {:ok, res} = EnsureRePrepended.filter(message)
+ assert res == message
+ end
+
+ test "it skip if summary is empty" do
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "summary"}}}
+ }
+ }
+
+ assert {:ok, res} = EnsureRePrepended.filter(message)
+ assert res == message
+ end
+
+ test "it skip if inReplyTo is empty" do
+ message = %{"type" => "Create", "object" => %{"summary" => "summary"}}
+ assert {:ok, res} = EnsureRePrepended.filter(message)
+ assert res == message
+ end
+
+ test "it skip if parent and child summary isn't equal" do
+ message = %{
+ "type" => "Create",
+ "object" => %{
+ "summary" => "object-summary",
+ "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "summary"}}}
+ }
+ }
+
+ assert {:ok, res} = EnsureRePrepended.filter(message)
+ assert res == message
+ end
+ end
+end
diff --git a/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs b/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs
new file mode 100644
index 000000000..63ed71129
--- /dev/null
+++ b/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs
@@ -0,0 +1,37 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicyTest do
+ use Pleroma.DataCase
+ alias Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy
+
+ test "it clears content object" do
+ message = %{
+ "type" => "Create",
+ "object" => %{"content" => ".", "attachment" => "image"}
+ }
+
+ assert {:ok, res} = NoPlaceholderTextPolicy.filter(message)
+ assert res["object"]["content"] == ""
+
+ message = put_in(message, ["object", "content"], ".
")
+ assert {:ok, res} = NoPlaceholderTextPolicy.filter(message)
+ assert res["object"]["content"] == ""
+ end
+
+ @messages [
+ %{
+ "type" => "Create",
+ "object" => %{"content" => "test", "attachment" => "image"}
+ },
+ %{"type" => "Create", "object" => %{"content" => "."}},
+ %{"type" => "Create", "object" => %{"content" => ".
"}}
+ ]
+ test "it skips filter" do
+ Enum.each(@messages, fn message ->
+ assert {:ok, res} = NoPlaceholderTextPolicy.filter(message)
+ assert res == message
+ end)
+ end
+end
diff --git a/test/web/activity_pub/mrf/normalize_markup_test.exs b/test/web/activity_pub/mrf/normalize_markup_test.exs
new file mode 100644
index 000000000..3916a1f35
--- /dev/null
+++ b/test/web/activity_pub/mrf/normalize_markup_test.exs
@@ -0,0 +1,42 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkupTest do
+ use Pleroma.DataCase
+ alias Pleroma.Web.ActivityPub.MRF.NormalizeMarkup
+
+ @html_sample """
+ this is in bold
+ this is a paragraph
+ this is a linebreak
+ this is a link with allowed "rel" attribute: example.com
+ this is a link with not allowed "rel" attribute: example.com
+ this is an image:
+
+ """
+
+ test "it filter html tags" do
+ expected = """
+ this is in bold
+ this is a paragraph
+ this is a linebreak
+ this is a link with allowed "rel" attribute: example.com
+ this is a link with not allowed "rel" attribute: example.com
+ this is an image:
+ alert('hacked')
+ """
+
+ message = %{"type" => "Create", "object" => %{"content" => @html_sample}}
+
+ assert {:ok, res} = NormalizeMarkup.filter(message)
+ assert res["object"]["content"] == expected
+ end
+
+ test "it skips filter if type isn't `Create`" do
+ message = %{"type" => "Note", "object" => %{}}
+
+ assert {:ok, res} = NormalizeMarkup.filter(message)
+ assert res == message
+ end
+end
diff --git a/test/web/activity_pub/mrf/reject_non_public_test.exs b/test/web/activity_pub/mrf/reject_non_public_test.exs
new file mode 100644
index 000000000..fdf6b245e
--- /dev/null
+++ b/test/web/activity_pub/mrf/reject_non_public_test.exs
@@ -0,0 +1,105 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+
+ alias Pleroma.Web.ActivityPub.MRF.RejectNonPublic
+
+ setup do
+ policy = Pleroma.Config.get([:mrf_rejectnonpublic])
+ on_exit(fn -> Pleroma.Config.put([:mrf_rejectnonpublic], policy) end)
+
+ :ok
+ end
+
+ describe "public message" do
+ test "it's allowed when address is public" do
+ actor = insert(:user, follower_address: "test-address")
+
+ message = %{
+ "actor" => actor.ap_id,
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "type" => "Create"
+ }
+
+ assert {:ok, message} = RejectNonPublic.filter(message)
+ end
+
+ test "it's allowed when cc address contain public address" do
+ actor = insert(:user, follower_address: "test-address")
+
+ message = %{
+ "actor" => actor.ap_id,
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "type" => "Create"
+ }
+
+ assert {:ok, message} = RejectNonPublic.filter(message)
+ end
+ end
+
+ describe "followers message" do
+ test "it's allowed when addrer of message in the follower addresses of user and it enabled in config" do
+ actor = insert(:user, follower_address: "test-address")
+
+ message = %{
+ "actor" => actor.ap_id,
+ "to" => ["test-address"],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "type" => "Create"
+ }
+
+ Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], true)
+ assert {:ok, message} = RejectNonPublic.filter(message)
+ end
+
+ test "it's rejected when addrer of message in the follower addresses of user and it disabled in config" do
+ actor = insert(:user, follower_address: "test-address")
+
+ message = %{
+ "actor" => actor.ap_id,
+ "to" => ["test-address"],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "type" => "Create"
+ }
+
+ Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], false)
+ assert {:reject, nil} = RejectNonPublic.filter(message)
+ end
+ end
+
+ describe "direct message" do
+ test "it's allows when direct messages are allow" do
+ actor = insert(:user)
+
+ message = %{
+ "actor" => actor.ap_id,
+ "to" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "type" => "Create"
+ }
+
+ Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], true)
+ assert {:ok, message} = RejectNonPublic.filter(message)
+ end
+
+ test "it's reject when direct messages aren't allow" do
+ actor = insert(:user)
+
+ message = %{
+ "actor" => actor.ap_id,
+ "to" => ["https://www.w3.org/ns/activitystreams#Publid~~~"],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Publid"],
+ "type" => "Create"
+ }
+
+ Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], false)
+ assert {:reject, nil} = RejectNonPublic.filter(message)
+ end
+ end
+end
diff --git a/test/web/activity_pub/mrf/tag_policy_test.exs b/test/web/activity_pub/mrf/tag_policy_test.exs
new file mode 100644
index 000000000..4aa35311e
--- /dev/null
+++ b/test/web/activity_pub/mrf/tag_policy_test.exs
@@ -0,0 +1,123 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+
+ alias Pleroma.Web.ActivityPub.MRF.TagPolicy
+ @public "https://www.w3.org/ns/activitystreams#Public"
+
+ describe "mrf_tag:disable-any-subscription" do
+ test "rejects message" do
+ actor = insert(:user, tags: ["mrf_tag:disable-any-subscription"])
+ message = %{"object" => actor.ap_id, "type" => "Follow"}
+ assert {:reject, nil} = TagPolicy.filter(message)
+ end
+ end
+
+ describe "mrf_tag:disable-remote-subscription" do
+ test "rejects non-local follow requests" do
+ actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"])
+ follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: false)
+ message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id}
+ assert {:reject, nil} = TagPolicy.filter(message)
+ end
+
+ test "allows non-local follow requests" do
+ actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"])
+ follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: true)
+ message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id}
+ assert {:ok, message} = TagPolicy.filter(message)
+ end
+ end
+
+ describe "mrf_tag:sandbox" do
+ test "removes from public timelines" do
+ actor = insert(:user, tags: ["mrf_tag:sandbox"])
+
+ message = %{
+ "actor" => actor.ap_id,
+ "type" => "Create",
+ "object" => %{},
+ "to" => [@public, "f"],
+ "cc" => [@public, "d"]
+ }
+
+ except_message = %{
+ "actor" => actor.ap_id,
+ "type" => "Create",
+ "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d"]},
+ "to" => ["f", actor.follower_address],
+ "cc" => ["d"]
+ }
+
+ assert TagPolicy.filter(message) == {:ok, except_message}
+ end
+ end
+
+ describe "mrf_tag:force-unlisted" do
+ test "removes from the federated timeline" do
+ actor = insert(:user, tags: ["mrf_tag:force-unlisted"])
+
+ message = %{
+ "actor" => actor.ap_id,
+ "type" => "Create",
+ "object" => %{},
+ "to" => [@public, "f"],
+ "cc" => [actor.follower_address, "d"]
+ }
+
+ except_message = %{
+ "actor" => actor.ap_id,
+ "type" => "Create",
+ "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]},
+ "to" => ["f", actor.follower_address],
+ "cc" => ["d", @public]
+ }
+
+ assert TagPolicy.filter(message) == {:ok, except_message}
+ end
+ end
+
+ describe "mrf_tag:media-strip" do
+ test "removes attachments" do
+ actor = insert(:user, tags: ["mrf_tag:media-strip"])
+
+ message = %{
+ "actor" => actor.ap_id,
+ "type" => "Create",
+ "object" => %{"attachment" => ["file1"]}
+ }
+
+ except_message = %{
+ "actor" => actor.ap_id,
+ "type" => "Create",
+ "object" => %{}
+ }
+
+ assert TagPolicy.filter(message) == {:ok, except_message}
+ end
+ end
+
+ describe "mrf_tag:media-force-nsfw" do
+ test "Mark as sensitive on presence of attachments" do
+ actor = insert(:user, tags: ["mrf_tag:media-force-nsfw"])
+
+ message = %{
+ "actor" => actor.ap_id,
+ "type" => "Create",
+ "object" => %{"tag" => ["test"], "attachment" => ["file1"]}
+ }
+
+ except_message = %{
+ "actor" => actor.ap_id,
+ "type" => "Create",
+ "object" => %{"tag" => ["test", "nsfw"], "attachment" => ["file1"], "sensitive" => true}
+ }
+
+ assert TagPolicy.filter(message) == {:ok, except_message}
+ end
+ end
+end
diff --git a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs b/test/web/activity_pub/mrf/user_allowlist_policy_test.exs
new file mode 100644
index 000000000..6519e2398
--- /dev/null
+++ b/test/web/activity_pub/mrf/user_allowlist_policy_test.exs
@@ -0,0 +1,36 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicyTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+
+ alias Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy
+
+ setup do
+ policy = Pleroma.Config.get([:mrf_user_allowlist]) || []
+ on_exit(fn -> Pleroma.Config.put([:mrf_user_allowlist], policy) end)
+
+ :ok
+ end
+
+ test "pass filter if allow list is empty" do
+ actor = insert(:user)
+ message = %{"actor" => actor.ap_id}
+ assert UserAllowListPolicy.filter(message) == {:ok, message}
+ end
+
+ test "pass filter if allow list isn't empty and user in allow list" do
+ actor = insert(:user)
+ Pleroma.Config.put([:mrf_user_allowlist, :localhost], [actor.ap_id, "test-ap-id"])
+ message = %{"actor" => actor.ap_id}
+ assert UserAllowListPolicy.filter(message) == {:ok, message}
+ end
+
+ test "rejected if allow list isn't empty and user not in allow list" do
+ actor = insert(:user)
+ Pleroma.Config.put([:mrf_user_allowlist, :localhost], ["test-ap-id"])
+ message = %{"actor" => actor.ap_id}
+ assert UserAllowListPolicy.filter(message) == {:reject, nil}
+ end
+end
From 93a0eeab16dc98b9278ee8649b233c3acd7807ec Mon Sep 17 00:00:00 2001
From: feld
Date: Wed, 10 Jul 2019 05:13:23 +0000
Subject: [PATCH 08/31] Add license/copyright to all project files
---
lib/healthcheck.ex | 4 ++++
lib/jason_types.ex | 4 ++++
lib/mix/tasks/pleroma/benchmark.ex | 4 ++++
lib/mix/tasks/pleroma/config.ex | 4 ++++
lib/pleroma/bbs/authenticator.ex | 4 ++++
lib/pleroma/bbs/handler.ex | 4 ++++
lib/pleroma/bookmark.ex | 4 ++++
lib/pleroma/config/transfer_task.ex | 4 ++++
lib/pleroma/instances.ex | 4 ++++
lib/pleroma/instances/instance.ex | 4 ++++
lib/pleroma/object/fetcher.ex | 4 ++++
lib/pleroma/object_tombstone.ex | 4 ++++
lib/pleroma/pagination.ex | 4 ++++
lib/pleroma/reverse_proxy/client.ex | 4 ++++
lib/pleroma/user/welcome_message.ex | 4 ++++
lib/pleroma/web/activity_pub/visibility.ex | 4 ++++
lib/pleroma/web/admin_api/views/config_view.ex | 4 ++++
lib/pleroma/web/mastodon_api/mastodon_api.ex | 4 ++++
lib/pleroma/web/mastodon_api/views/conversation_view.ex | 4 ++++
lib/pleroma/web/metadata/player_view.ex | 4 ++++
lib/pleroma/web/metadata/rel_me.ex | 4 ++++
lib/pleroma/web/oauth/token/response.ex | 4 ++++
lib/pleroma/web/oauth/token/strategy/refresh_token.ex | 4 ++++
lib/pleroma/web/oauth/token/strategy/revoke.ex | 4 ++++
lib/pleroma/web/oauth/token/utils.ex | 4 ++++
lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex | 4 ++++
lib/pleroma/web/rich_media/parsers/oembed_parser.ex | 4 ++++
lib/pleroma/web/rich_media/parsers/ogp.ex | 4 ++++
lib/pleroma/web/rich_media/parsers/twitter_card.ex | 4 ++++
lib/pleroma/web/uploader_controller.ex | 4 ++++
lib/transports.ex | 4 ++++
lib/xml_builder.ex | 4 ++++
test/bbs/handler_test.exs | 4 ++++
test/bookmark_test.exs | 4 ++++
test/config/transfer_task_test.exs | 4 ++++
test/emoji_test.exs | 4 ++++
test/healthcheck_test.exs | 4 ++++
test/http/request_builder_test.exs | 4 ++++
test/keys_test.exs | 4 ++++
test/object/containment_test.exs | 4 ++++
test/object/fetcher_test.exs | 4 ++++
test/plugs/rate_limiter_test.exs | 4 ++++
test/repo_test.exs | 4 ++++
test/reverse_proxy_test.exs | 4 ++++
test/tasks/config_test.exs | 4 ++++
test/tasks/ecto/ecto_test.exs | 4 ++++
test/tasks/ecto/rollback_test.exs | 4 ++++
test/tasks/instance.exs | 4 ++++
test/tasks/pleroma_test.exs | 4 ++++
test/tasks/robots_txt_test.exs | 4 ++++
test/upload/filter/anonymize_filename_test.exs | 4 ++++
test/user_invite_token_test.exs | 4 ++++
test/web/activity_pub/utils_test.exs | 4 ++++
test/web/activity_pub/views/object_view_test.exs | 4 ++++
test/web/activity_pub/views/user_view_test.exs | 4 ++++
test/web/activity_pub/visibilty_test.exs | 4 ++++
test/web/admin_api/config_test.exs | 4 ++++
test/web/metadata/rel_me_test.exs | 4 ++++
test/web/ostatus/incoming_documents/delete_handling_test.exs | 4 ++++
test/web/rel_me_test.exs | 4 ++++
test/web/rich_media/helpers_test.exs | 4 ++++
test/web/rich_media/parser_test.exs | 4 ++++
test/web/twitter_api/password_controller_test.exs | 4 ++++
test/web/twitter_api/util_controller_test.exs | 4 ++++
64 files changed, 256 insertions(+)
diff --git a/lib/healthcheck.ex b/lib/healthcheck.ex
index 32aafc210..f97d14432 100644
--- a/lib/healthcheck.ex
+++ b/lib/healthcheck.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Healthcheck do
@moduledoc """
Module collects metrics about app and assign healthy status.
diff --git a/lib/jason_types.ex b/lib/jason_types.ex
index d1a7bc7ac..c558aef57 100644
--- a/lib/jason_types.ex
+++ b/lib/jason_types.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
Postgrex.Types.define(
Pleroma.PostgresTypes,
[] ++ Ecto.Adapters.Postgres.extensions(),
diff --git a/lib/mix/tasks/pleroma/benchmark.ex b/lib/mix/tasks/pleroma/benchmark.ex
index d43db7b35..5222cce80 100644
--- a/lib/mix/tasks/pleroma/benchmark.ex
+++ b/lib/mix/tasks/pleroma/benchmark.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Mix.Tasks.Pleroma.Benchmark do
import Mix.Pleroma
use Mix.Task
diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex
index faa605d9b..a71bcd447 100644
--- a/lib/mix/tasks/pleroma/config.ex
+++ b/lib/mix/tasks/pleroma/config.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Mix.Tasks.Pleroma.Config do
use Mix.Task
import Mix.Pleroma
diff --git a/lib/pleroma/bbs/authenticator.ex b/lib/pleroma/bbs/authenticator.ex
index a2c153720..79f133ea6 100644
--- a/lib/pleroma/bbs/authenticator.ex
+++ b/lib/pleroma/bbs/authenticator.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.BBS.Authenticator do
use Sshd.PasswordAuthenticator
alias Comeonin.Pbkdf2
diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex
index f34be961f..0a381f592 100644
--- a/lib/pleroma/bbs/handler.ex
+++ b/lib/pleroma/bbs/handler.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.BBS.Handler do
use Sshd.ShellHandler
alias Pleroma.Activity
diff --git a/lib/pleroma/bookmark.ex b/lib/pleroma/bookmark.ex
index 7f8fd43b6..d976f949c 100644
--- a/lib/pleroma/bookmark.ex
+++ b/lib/pleroma/bookmark.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Bookmark do
use Ecto.Schema
diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex
index cf880aa22..3c13a0558 100644
--- a/lib/pleroma/config/transfer_task.ex
+++ b/lib/pleroma/config/transfer_task.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Config.TransferTask do
use Task
alias Pleroma.Web.AdminAPI.Config
diff --git a/lib/pleroma/instances.ex b/lib/pleroma/instances.ex
index fa5043bc5..1b05d573c 100644
--- a/lib/pleroma/instances.ex
+++ b/lib/pleroma/instances.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Instances do
@moduledoc "Instances context."
diff --git a/lib/pleroma/instances/instance.ex b/lib/pleroma/instances/instance.ex
index 420803a8f..4d7ed4ca1 100644
--- a/lib/pleroma/instances/instance.ex
+++ b/lib/pleroma/instances/instance.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Instances.Instance do
@moduledoc "Instance."
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
index fffbf2bbb..101c21f96 100644
--- a/lib/pleroma/object/fetcher.ex
+++ b/lib/pleroma/object/fetcher.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Object.Fetcher do
alias Pleroma.HTTP
alias Pleroma.Object
diff --git a/lib/pleroma/object_tombstone.ex b/lib/pleroma/object_tombstone.ex
index 64d836d3e..fe947ffd3 100644
--- a/lib/pleroma/object_tombstone.ex
+++ b/lib/pleroma/object_tombstone.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.ObjectTombstone do
@enforce_keys [:id, :formerType, :deleted]
defstruct [:id, :formerType, :deleted, type: "Tombstone"]
diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex
index f435e5c9c..3d7dd9e6a 100644
--- a/lib/pleroma/pagination.ex
+++ b/lib/pleroma/pagination.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Pagination do
@moduledoc """
Implements Mastodon-compatible pagination.
diff --git a/lib/pleroma/reverse_proxy/client.ex b/lib/pleroma/reverse_proxy/client.ex
index 57c2d2cfd..776c4794c 100644
--- a/lib/pleroma/reverse_proxy/client.ex
+++ b/lib/pleroma/reverse_proxy/client.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.ReverseProxy.Client do
@callback request(atom(), String.t(), [tuple()], String.t(), list()) ::
{:ok, pos_integer(), [tuple()], reference() | map()}
diff --git a/lib/pleroma/user/welcome_message.ex b/lib/pleroma/user/welcome_message.ex
index 2ba65b75a..99fba729e 100644
--- a/lib/pleroma/user/welcome_message.ex
+++ b/lib/pleroma/user/welcome_message.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.User.WelcomeMessage do
alias Pleroma.User
alias Pleroma.Web.CommonAPI
diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex
index 8965e3253..9908a2e75 100644
--- a/lib/pleroma/web/activity_pub/visibility.ex
+++ b/lib/pleroma/web/activity_pub/visibility.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.ActivityPub.Visibility do
alias Pleroma.Activity
alias Pleroma.Object
diff --git a/lib/pleroma/web/admin_api/views/config_view.ex b/lib/pleroma/web/admin_api/views/config_view.ex
index 3ccc9ca46..a31f1041f 100644
--- a/lib/pleroma/web/admin_api/views/config_view.ex
+++ b/lib/pleroma/web/admin_api/views/config_view.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.AdminAPI.ConfigView do
use Pleroma.Web, :view
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex
index 3a3ec7c2a..c82b20123 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
import Ecto.Query
import Ecto.Changeset
diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
index af1dcf66d..38bdec737 100644
--- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.MastodonAPI.ConversationView do
use Pleroma.Web, :view
diff --git a/lib/pleroma/web/metadata/player_view.ex b/lib/pleroma/web/metadata/player_view.ex
index e9a8cfc8d..4289ebdbd 100644
--- a/lib/pleroma/web/metadata/player_view.ex
+++ b/lib/pleroma/web/metadata/player_view.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.Metadata.PlayerView do
use Pleroma.Web, :view
import Phoenix.HTML.Tag, only: [content_tag: 3, tag: 2]
diff --git a/lib/pleroma/web/metadata/rel_me.ex b/lib/pleroma/web/metadata/rel_me.ex
index 03af899c4..f87fc1973 100644
--- a/lib/pleroma/web/metadata/rel_me.ex
+++ b/lib/pleroma/web/metadata/rel_me.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.Metadata.Providers.RelMe do
alias Pleroma.Web.Metadata.Providers.Provider
@behaviour Provider
diff --git a/lib/pleroma/web/oauth/token/response.ex b/lib/pleroma/web/oauth/token/response.ex
index 2648571ad..266110814 100644
--- a/lib/pleroma/web/oauth/token/response.ex
+++ b/lib/pleroma/web/oauth/token/response.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.OAuth.Token.Response do
@moduledoc false
diff --git a/lib/pleroma/web/oauth/token/strategy/refresh_token.ex b/lib/pleroma/web/oauth/token/strategy/refresh_token.ex
index 7df0be14e..c620050c8 100644
--- a/lib/pleroma/web/oauth/token/strategy/refresh_token.ex
+++ b/lib/pleroma/web/oauth/token/strategy/refresh_token.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.OAuth.Token.Strategy.RefreshToken do
@moduledoc """
Functions for dealing with refresh token strategy.
diff --git a/lib/pleroma/web/oauth/token/strategy/revoke.ex b/lib/pleroma/web/oauth/token/strategy/revoke.ex
index dea63ca54..983f095b4 100644
--- a/lib/pleroma/web/oauth/token/strategy/revoke.ex
+++ b/lib/pleroma/web/oauth/token/strategy/revoke.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.OAuth.Token.Strategy.Revoke do
@moduledoc """
Functions for dealing with revocation.
diff --git a/lib/pleroma/web/oauth/token/utils.ex b/lib/pleroma/web/oauth/token/utils.ex
index 7a4fddafd..1e8765e93 100644
--- a/lib/pleroma/web/oauth/token/utils.ex
+++ b/lib/pleroma/web/oauth/token/utils.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.OAuth.Token.Utils do
@moduledoc """
Auxiliary functions for dealing with tokens.
diff --git a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex
index fb79630e4..913975616 100644
--- a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex
+++ b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do
def parse(html, data, prefix, error_message, key_name, value_name \\ "content") do
meta_data =
diff --git a/lib/pleroma/web/rich_media/parsers/oembed_parser.ex b/lib/pleroma/web/rich_media/parsers/oembed_parser.ex
index 2530b8c9d..875637c4d 100644
--- a/lib/pleroma/web/rich_media/parsers/oembed_parser.ex
+++ b/lib/pleroma/web/rich_media/parsers/oembed_parser.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do
def parse(html, _data) do
with elements = [_ | _] <- get_discovery_data(html),
diff --git a/lib/pleroma/web/rich_media/parsers/ogp.ex b/lib/pleroma/web/rich_media/parsers/ogp.ex
index 0e1a0e719..d40fa009f 100644
--- a/lib/pleroma/web/rich_media/parsers/ogp.ex
+++ b/lib/pleroma/web/rich_media/parsers/ogp.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.RichMedia.Parsers.OGP do
def parse(html, data) do
Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse(
diff --git a/lib/pleroma/web/rich_media/parsers/twitter_card.ex b/lib/pleroma/web/rich_media/parsers/twitter_card.ex
index a317c3e78..e4efe2dd0 100644
--- a/lib/pleroma/web/rich_media/parsers/twitter_card.ex
+++ b/lib/pleroma/web/rich_media/parsers/twitter_card.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do
def parse(html, data) do
Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse(
diff --git a/lib/pleroma/web/uploader_controller.ex b/lib/pleroma/web/uploader_controller.ex
index 5d8a77346..d11e8e63e 100644
--- a/lib/pleroma/web/uploader_controller.ex
+++ b/lib/pleroma/web/uploader_controller.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.UploaderController do
use Pleroma.Web, :controller
diff --git a/lib/transports.ex b/lib/transports.ex
index 42f645b21..9f3fc535d 100644
--- a/lib/transports.ex
+++ b/lib/transports.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Phoenix.Transports.WebSocket.Raw do
import Plug.Conn,
only: [
diff --git a/lib/xml_builder.ex b/lib/xml_builder.ex
index b58602c7b..ceeef2755 100644
--- a/lib/xml_builder.ex
+++ b/lib/xml_builder.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.XmlBuilder do
def to_xml({tag, attributes, content}) do
open_tag = make_open_tag(tag, attributes)
diff --git a/test/bbs/handler_test.exs b/test/bbs/handler_test.exs
index 6f6533e3d..4f0c13417 100644
--- a/test/bbs/handler_test.exs
+++ b/test/bbs/handler_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.BBS.HandlerTest do
use Pleroma.DataCase
alias Pleroma.Activity
diff --git a/test/bookmark_test.exs b/test/bookmark_test.exs
index b81c102ef..e54bd359c 100644
--- a/test/bookmark_test.exs
+++ b/test/bookmark_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.BookmarkTest do
use Pleroma.DataCase
import Pleroma.Factory
diff --git a/test/config/transfer_task_test.exs b/test/config/transfer_task_test.exs
index c0e433263..dbeadbe87 100644
--- a/test/config/transfer_task_test.exs
+++ b/test/config/transfer_task_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Config.TransferTaskTest do
use Pleroma.DataCase
diff --git a/test/emoji_test.exs b/test/emoji_test.exs
index 2eaa26be6..07ac6ff1d 100644
--- a/test/emoji_test.exs
+++ b/test/emoji_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.EmojiTest do
use ExUnit.Case, async: true
alias Pleroma.Emoji
diff --git a/test/healthcheck_test.exs b/test/healthcheck_test.exs
index e05061220..6bb8d5b7f 100644
--- a/test/healthcheck_test.exs
+++ b/test/healthcheck_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.HealthcheckTest do
use Pleroma.DataCase
alias Pleroma.Healthcheck
diff --git a/test/http/request_builder_test.exs b/test/http/request_builder_test.exs
index a368999ff..7febe84c5 100644
--- a/test/http/request_builder_test.exs
+++ b/test/http/request_builder_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.HTTP.RequestBuilderTest do
use ExUnit.Case, async: true
alias Pleroma.HTTP.RequestBuilder
diff --git a/test/keys_test.exs b/test/keys_test.exs
index 776fdea6f..059f70b74 100644
--- a/test/keys_test.exs
+++ b/test/keys_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.KeysTest do
use Pleroma.DataCase
diff --git a/test/object/containment_test.exs b/test/object/containment_test.exs
index a860355b8..1beed6236 100644
--- a/test/object/containment_test.exs
+++ b/test/object/containment_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Object.ContainmentTest do
use Pleroma.DataCase
diff --git a/test/object/fetcher_test.exs b/test/object/fetcher_test.exs
index 26dc9496d..3b666e0d1 100644
--- a/test/object/fetcher_test.exs
+++ b/test/object/fetcher_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Object.FetcherTest do
use Pleroma.DataCase
diff --git a/test/plugs/rate_limiter_test.exs b/test/plugs/rate_limiter_test.exs
index b8d6aff89..f8251b5c7 100644
--- a/test/plugs/rate_limiter_test.exs
+++ b/test/plugs/rate_limiter_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Plugs.RateLimiterTest do
use ExUnit.Case, async: true
use Plug.Test
diff --git a/test/repo_test.exs b/test/repo_test.exs
index 85085a1fa..85b64d4d1 100644
--- a/test/repo_test.exs
+++ b/test/repo_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.RepoTest do
use Pleroma.DataCase
import Pleroma.Factory
diff --git a/test/reverse_proxy_test.exs b/test/reverse_proxy_test.exs
index 75a61445a..f542de97c 100644
--- a/test/reverse_proxy_test.exs
+++ b/test/reverse_proxy_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.ReverseProxyTest do
use Pleroma.Web.ConnCase, async: true
import ExUnit.CaptureLog
diff --git a/test/tasks/config_test.exs b/test/tasks/config_test.exs
index 83a363356..bbcc57217 100644
--- a/test/tasks/config_test.exs
+++ b/test/tasks/config_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Mix.Tasks.Pleroma.ConfigTest do
use Pleroma.DataCase
alias Pleroma.Repo
diff --git a/test/tasks/ecto/ecto_test.exs b/test/tasks/ecto/ecto_test.exs
index b48662c88..a1b9ca174 100644
--- a/test/tasks/ecto/ecto_test.exs
+++ b/test/tasks/ecto/ecto_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Mix.Tasks.Pleroma.EctoTest do
use ExUnit.Case, async: true
diff --git a/test/tasks/ecto/rollback_test.exs b/test/tasks/ecto/rollback_test.exs
index 33d093fca..c33c4e940 100644
--- a/test/tasks/ecto/rollback_test.exs
+++ b/test/tasks/ecto/rollback_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Mix.Tasks.Pleroma.Ecto.RollbackTest do
use Pleroma.DataCase
import ExUnit.CaptureLog
diff --git a/test/tasks/instance.exs b/test/tasks/instance.exs
index 1875f52a3..bf3359b6f 100644
--- a/test/tasks/instance.exs
+++ b/test/tasks/instance.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.InstanceTest do
use ExUnit.Case, async: true
diff --git a/test/tasks/pleroma_test.exs b/test/tasks/pleroma_test.exs
index e236ccbbb..a20bd9cf2 100644
--- a/test/tasks/pleroma_test.exs
+++ b/test/tasks/pleroma_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Mix.PleromaTest do
use ExUnit.Case, async: true
import Mix.Pleroma
diff --git a/test/tasks/robots_txt_test.exs b/test/tasks/robots_txt_test.exs
index 539193f73..97147a919 100644
--- a/test/tasks/robots_txt_test.exs
+++ b/test/tasks/robots_txt_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Mix.Tasks.Pleroma.RobotsTxtTest do
use ExUnit.Case, async: true
alias Mix.Tasks.Pleroma.RobotsTxt
diff --git a/test/upload/filter/anonymize_filename_test.exs b/test/upload/filter/anonymize_filename_test.exs
index 02241cfa4..a31b38ab1 100644
--- a/test/upload/filter/anonymize_filename_test.exs
+++ b/test/upload/filter/anonymize_filename_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do
use Pleroma.DataCase
diff --git a/test/user_invite_token_test.exs b/test/user_invite_token_test.exs
index 276788254..111e40361 100644
--- a/test/user_invite_token_test.exs
+++ b/test/user_invite_token_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.UserInviteTokenTest do
use ExUnit.Case, async: true
use Pleroma.DataCase
diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs
index 932d5f5e7..ca5f057a7 100644
--- a/test/web/activity_pub/utils_test.exs
+++ b/test/web/activity_pub/utils_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.ActivityPub.UtilsTest do
use Pleroma.DataCase
alias Pleroma.Activity
diff --git a/test/web/activity_pub/views/object_view_test.exs b/test/web/activity_pub/views/object_view_test.exs
index ac78c9cf1..13447dc29 100644
--- a/test/web/activity_pub/views/object_view_test.exs
+++ b/test/web/activity_pub/views/object_view_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
use Pleroma.DataCase
import Pleroma.Factory
diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs
index e6483db8b..969860c4c 100644
--- a/test/web/activity_pub/views/user_view_test.exs
+++ b/test/web/activity_pub/views/user_view_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.ActivityPub.UserViewTest do
use Pleroma.DataCase
import Pleroma.Factory
diff --git a/test/web/activity_pub/visibilty_test.exs b/test/web/activity_pub/visibilty_test.exs
index e24df3cab..4d5c07da4 100644
--- a/test/web/activity_pub/visibilty_test.exs
+++ b/test/web/activity_pub/visibilty_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.ActivityPub.VisibilityTest do
use Pleroma.DataCase
diff --git a/test/web/admin_api/config_test.exs b/test/web/admin_api/config_test.exs
index 10cb3b68a..b281831e3 100644
--- a/test/web/admin_api/config_test.exs
+++ b/test/web/admin_api/config_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.AdminAPI.ConfigTest do
use Pleroma.DataCase, async: true
import Pleroma.Factory
diff --git a/test/web/metadata/rel_me_test.exs b/test/web/metadata/rel_me_test.exs
index f66bf7834..3874e077b 100644
--- a/test/web/metadata/rel_me_test.exs
+++ b/test/web/metadata/rel_me_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.Metadata.Providers.RelMeTest do
use Pleroma.DataCase
import Pleroma.Factory
diff --git a/test/web/ostatus/incoming_documents/delete_handling_test.exs b/test/web/ostatus/incoming_documents/delete_handling_test.exs
index 1fe714d00..cd0447af7 100644
--- a/test/web/ostatus/incoming_documents/delete_handling_test.exs
+++ b/test/web/ostatus/incoming_documents/delete_handling_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.OStatus.DeleteHandlingTest do
use Pleroma.DataCase
diff --git a/test/web/rel_me_test.exs b/test/web/rel_me_test.exs
index 5188f4de1..85515c432 100644
--- a/test/web/rel_me_test.exs
+++ b/test/web/rel_me_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.RelMeTest do
use ExUnit.Case, async: true
diff --git a/test/web/rich_media/helpers_test.exs b/test/web/rich_media/helpers_test.exs
index c8f442b05..92198f3d9 100644
--- a/test/web/rich_media/helpers_test.exs
+++ b/test/web/rich_media/helpers_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.RichMedia.HelpersTest do
use Pleroma.DataCase
diff --git a/test/web/rich_media/parser_test.exs b/test/web/rich_media/parser_test.exs
index bc48341ca..19c19e895 100644
--- a/test/web/rich_media/parser_test.exs
+++ b/test/web/rich_media/parser_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.RichMedia.ParserTest do
use ExUnit.Case, async: true
diff --git a/test/web/twitter_api/password_controller_test.exs b/test/web/twitter_api/password_controller_test.exs
index 6b9da8204..3a7246ea8 100644
--- a/test/web/twitter_api/password_controller_test.exs
+++ b/test/web/twitter_api/password_controller_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
use Pleroma.Web.ConnCase
diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs
index cab9e5d90..21324399f 100644
--- a/test/web/twitter_api/util_controller_test.exs
+++ b/test/web/twitter_api/util_controller_test.exs
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
use Pleroma.Web.ConnCase
From 2d2b50cccaa99b551b88be36a4b33b271300d3c8 Mon Sep 17 00:00:00 2001
From: Sergey Suprunenko
Date: Wed, 10 Jul 2019 05:16:08 +0000
Subject: [PATCH 09/31] Send and handle "Delete" activity for deleted users
---
lib/pleroma/user.ex | 6 +-
lib/pleroma/web/activity_pub/activity_pub.ex | 13 +++
.../web/activity_pub/transmogrifier.ex | 27 +++++-
test/fixtures/mastodon-delete-user.json | 24 +++++
test/user_test.exs | 92 +++++++++++++------
test/web/activity_pub/transmogrifier_test.exs | 24 +++++
6 files changed, 152 insertions(+), 34 deletions(-)
create mode 100644 test/fixtures/mastodon-delete-user.json
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index d03810d1a..034c414bf 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -937,6 +937,8 @@ def delete(%User{} = user),
@spec perform(atom(), User.t()) :: {:ok, User.t()}
def perform(:delete, %User{} = user) do
+ {:ok, _user} = ActivityPub.delete(user)
+
# Remove all relationships
{:ok, followers} = User.get_followers(user)
@@ -953,8 +955,8 @@ def perform(:delete, %User{} = user) do
end)
delete_user_activities(user)
-
- {:ok, _user} = Repo.delete(user)
+ invalidate_cache(user)
+ Repo.delete(user)
end
@spec perform(atom(), User.t()) :: {:ok, User.t()}
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 55315d66e..41b55bbab 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -405,6 +405,19 @@ def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
end
end
+ def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
+ with data <- %{
+ "to" => [follower_address],
+ "type" => "Delete",
+ "actor" => ap_id,
+ "object" => %{"type" => "Person", "id" => ap_id}
+ },
+ {:ok, activity} <- insert(data, true, true),
+ :ok <- maybe_federate(activity) do
+ {:ok, user}
+ end
+ end
+
def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
user = User.get_cached_by_ap_id(actor)
to = (object.data["to"] || []) ++ (object.data["cc"] || [])
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 543d4bb7d..e34fe6611 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -641,7 +641,7 @@ def handle_incoming(
# an error or a tombstone. This would allow us to verify that a deletion actually took
# place.
def handle_incoming(
- %{"type" => "Delete", "object" => object_id, "actor" => _actor, "id" => _id} = data,
+ %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = data,
_options
) do
object_id = Utils.get_ap_id(object_id)
@@ -653,7 +653,30 @@ def handle_incoming(
{:ok, activity} <- ActivityPub.delete(object, false) do
{:ok, activity}
else
- _e -> :error
+ nil ->
+ case User.get_cached_by_ap_id(object_id) do
+ %User{ap_id: ^actor} = user ->
+ {:ok, followers} = User.get_followers(user)
+
+ Enum.each(followers, fn follower ->
+ User.unfollow(follower, user)
+ end)
+
+ {:ok, friends} = User.get_friends(user)
+
+ Enum.each(friends, fn followed ->
+ User.unfollow(user, followed)
+ end)
+
+ User.invalidate_cache(user)
+ Repo.delete(user)
+
+ nil ->
+ :error
+ end
+
+ _e ->
+ :error
end
end
diff --git a/test/fixtures/mastodon-delete-user.json b/test/fixtures/mastodon-delete-user.json
new file mode 100644
index 000000000..f19088fec
--- /dev/null
+++ b/test/fixtures/mastodon-delete-user.json
@@ -0,0 +1,24 @@
+{
+ "type": "Delete",
+ "object": {
+ "type": "Person",
+ "id": "http://mastodon.example.org/users/admin",
+ "atomUri": "http://mastodon.example.org/users/admin"
+ },
+ "id": "http://mastodon.example.org/users/admin#delete",
+ "actor": "http://mastodon.example.org/users/admin",
+ "@context": [
+ {
+ "toot": "http://joinmastodon.org/ns#",
+ "sensitive": "as:sensitive",
+ "ostatus": "http://ostatus.org#",
+ "movedTo": "as:movedTo",
+ "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+ "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
+ "conversation": "ostatus:conversation",
+ "atomUri": "ostatus:atomUri",
+ "Hashtag": "as:Hashtag",
+ "Emoji": "toot:Emoji"
+ }
+ ]
+}
diff --git a/test/user_test.exs b/test/user_test.exs
index 0f27d73f7..62be79b4f 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -14,6 +14,7 @@ defmodule Pleroma.UserTest do
use Pleroma.DataCase
import Pleroma.Factory
+ import Mock
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
@@ -915,49 +916,80 @@ test "hide a user's statuses from timelines and notifications" do
end
end
- test ".delete_user_activities deletes all create activities" do
- user = insert(:user)
+ describe "delete" do
+ setup do
+ {:ok, user} = insert(:user) |> User.set_cache()
- {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
+ [user: user]
+ end
- {:ok, _} = User.delete_user_activities(user)
+ test ".delete_user_activities deletes all create activities", %{user: user} do
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
- # TODO: Remove favorites, repeats, delete activities.
- refute Activity.get_by_id(activity.id)
- end
+ {:ok, _} = User.delete_user_activities(user)
- test ".delete deactivates a user, all follow relationships and all activities" do
- user = insert(:user)
- follower = insert(:user)
+ # TODO: Remove favorites, repeats, delete activities.
+ refute Activity.get_by_id(activity.id)
+ end
- {:ok, follower} = User.follow(follower, user)
+ test "it deletes a user, all follow relationships and all activities", %{user: user} do
+ follower = insert(:user)
+ {:ok, follower} = User.follow(follower, user)
- {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
- {:ok, activity_two} = CommonAPI.post(follower, %{"status" => "3hu"})
+ object = insert(:note, user: user)
+ activity = insert(:note_activity, user: user, note: object)
- {:ok, like, _} = CommonAPI.favorite(activity_two.id, user)
- {:ok, like_two, _} = CommonAPI.favorite(activity.id, follower)
- {:ok, repeat, _} = CommonAPI.repeat(activity_two.id, user)
+ object_two = insert(:note, user: follower)
+ activity_two = insert(:note_activity, user: follower, note: object_two)
- {:ok, _} = User.delete(user)
+ {:ok, like, _} = CommonAPI.favorite(activity_two.id, user)
+ {:ok, like_two, _} = CommonAPI.favorite(activity.id, follower)
+ {:ok, repeat, _} = CommonAPI.repeat(activity_two.id, user)
- follower = User.get_cached_by_id(follower.id)
+ {:ok, _} = User.delete(user)
- refute User.following?(follower, user)
- refute User.get_by_id(user.id)
+ follower = User.get_cached_by_id(follower.id)
- user_activities =
- user.ap_id
- |> Activity.query_by_actor()
- |> Repo.all()
- |> Enum.map(fn act -> act.data["type"] end)
+ refute User.following?(follower, user)
+ refute User.get_by_id(user.id)
+ assert {:ok, nil} == Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
- assert Enum.all?(user_activities, fn act -> act in ~w(Delete Undo) end)
+ user_activities =
+ user.ap_id
+ |> Activity.query_by_actor()
+ |> Repo.all()
+ |> Enum.map(fn act -> act.data["type"] end)
- refute Activity.get_by_id(activity.id)
- refute Activity.get_by_id(like.id)
- refute Activity.get_by_id(like_two.id)
- refute Activity.get_by_id(repeat.id)
+ assert Enum.all?(user_activities, fn act -> act in ~w(Delete Undo) end)
+
+ refute Activity.get_by_id(activity.id)
+ refute Activity.get_by_id(like.id)
+ refute Activity.get_by_id(like_two.id)
+ refute Activity.get_by_id(repeat.id)
+ end
+
+ test_with_mock "it sends out User Delete activity",
+ %{user: user},
+ Pleroma.Web.ActivityPub.Publisher,
+ [:passthrough],
+ [] do
+ config_path = [:instance, :federating]
+ initial_setting = Pleroma.Config.get(config_path)
+ Pleroma.Config.put(config_path, true)
+
+ {:ok, follower} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
+ {:ok, _} = User.follow(follower, user)
+
+ {:ok, _user} = User.delete(user)
+
+ assert called(
+ Pleroma.Web.ActivityPub.Publisher.publish_one(%{
+ inbox: "http://mastodon.example.org/inbox"
+ })
+ )
+
+ Pleroma.Config.put(config_path, initial_setting)
+ end
end
test "get_public_key_for_ap_id fetches a user that's not in the db" do
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
index d152169b8..825e99879 100644
--- a/test/web/activity_pub/transmogrifier_test.exs
+++ b/test/web/activity_pub/transmogrifier_test.exs
@@ -553,6 +553,30 @@ test "it fails for incoming deletes with spoofed origin" do
assert Activity.get_by_id(activity.id)
end
+ test "it works for incoming user deletes" do
+ %{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
+
+ data =
+ File.read!("test/fixtures/mastodon-delete-user.json")
+ |> Poison.decode!()
+
+ {:ok, _} = Transmogrifier.handle_incoming(data)
+
+ refute User.get_cached_by_ap_id(ap_id)
+ end
+
+ test "it fails for incoming user deletes with spoofed origin" do
+ %{ap_id: ap_id} = insert(:user)
+
+ data =
+ File.read!("test/fixtures/mastodon-delete-user.json")
+ |> Poison.decode!()
+ |> Map.put("actor", ap_id)
+
+ assert :error == Transmogrifier.handle_incoming(data)
+ assert User.get_cached_by_ap_id(ap_id)
+ end
+
test "it works for incoming unannounces with an existing notice" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
From 12b1454245fc2efba22d5633f65539dac727ee3d Mon Sep 17 00:00:00 2001
From: Maksim
Date: Wed, 10 Jul 2019 05:34:21 +0000
Subject: [PATCH 10/31] [#1062] added option to disable send email
---
CHANGELOG.md | 1 +
config/config.exs | 2 +-
config/test.exs | 2 +-
docs/config.md | 1 +
lib/pleroma/emails/mailer.ex | 49 ++++++++++++++++++++++++++-
mix.exs | 2 +-
mix.lock | 9 ++---
test/emails/admin_email_test.exs | 37 +++++++++++++++++++++
test/emails/mailer_test.exs | 57 ++++++++++++++++++++++++++++++++
test/emails/user_email_test.exs | 48 +++++++++++++++++++++++++++
10 files changed, 200 insertions(+), 8 deletions(-)
create mode 100644 test/emails/admin_email_test.exs
create mode 100644 test/emails/mailer_test.exs
create mode 100644 test/emails/user_email_test.exs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 227f721e3..763cd8d92 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ Configuration: `federation_incoming_replies_max_depth` option
- Admin API: Return avatar and display name when querying users
- Admin API: Allow querying user by ID
- Added synchronization of following/followers counters for external users
+- Configuration: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`.
### Fixed
- Not being able to pin unlisted posts
diff --git a/config/config.exs b/config/config.exs
index 09681f122..0d3419102 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -501,7 +501,7 @@
config :pleroma, :auth, oauth_consumer_strategies: oauth_consumer_strategies
-config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail
+config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail, enabled: false
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
diff --git a/config/test.exs b/config/test.exs
index 63443dde0..19d7cca5f 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -23,7 +23,7 @@
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
-config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test
+config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test, enabled: true
config :pleroma, :instance,
email: "admin@example.com",
diff --git a/docs/config.md b/docs/config.md
index 931155fe9..01730ec16 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -41,6 +41,7 @@ This filter replaces the filename (not the path) of an upload. For complete obfu
## Pleroma.Emails.Mailer
* `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox.
* `api_key` / `password` and / or other adapter-specific settings, per the above documentation.
+* `enabled`: Allows enable/disable send emails. Default: `false`.
An example for Sendgrid adapter:
diff --git a/lib/pleroma/emails/mailer.ex b/lib/pleroma/emails/mailer.ex
index 53f5a661c..2e4657b7c 100644
--- a/lib/pleroma/emails/mailer.ex
+++ b/lib/pleroma/emails/mailer.ex
@@ -3,11 +3,58 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.Mailer do
- use Swoosh.Mailer, otp_app: :pleroma
+ @moduledoc """
+ Defines the Pleroma mailer.
+ The module contains functions to delivery email using Swoosh.Mailer.
+ """
+
+ alias Swoosh.DeliveryError
+
+ @otp_app :pleroma
+ @mailer_config [otp: :pleroma]
+
+ @spec enabled?() :: boolean()
+ def enabled?, do: Pleroma.Config.get([__MODULE__, :enabled])
+
+ @doc "add email to queue"
def deliver_async(email, config \\ []) do
PleromaJobQueue.enqueue(:mailer, __MODULE__, [:deliver_async, email, config])
end
+ @doc "callback to perform send email from queue"
def perform(:deliver_async, email, config), do: deliver(email, config)
+
+ @spec deliver(Swoosh.Email.t(), Keyword.t()) :: {:ok, term} | {:error, term}
+ def deliver(email, config \\ [])
+
+ def deliver(email, config) do
+ case enabled?() do
+ true -> Swoosh.Mailer.deliver(email, parse_config(config))
+ false -> {:error, :deliveries_disabled}
+ end
+ end
+
+ @spec deliver!(Swoosh.Email.t(), Keyword.t()) :: term | no_return
+ def deliver!(email, config \\ [])
+
+ def deliver!(email, config) do
+ case deliver(email, config) do
+ {:ok, result} -> result
+ {:error, reason} -> raise DeliveryError, reason: reason
+ end
+ end
+
+ @on_load :validate_dependency
+
+ @doc false
+ def validate_dependency do
+ parse_config([])
+ |> Keyword.get(:adapter)
+ |> Swoosh.Mailer.validate_dependency()
+ end
+
+ defp parse_config(config) do
+ Swoosh.Mailer.parse_config(@otp_app, __MODULE__, @mailer_config, config)
+ end
end
diff --git a/mix.exs b/mix.exs
index 8f64562ef..f96789d21 100644
--- a/mix.exs
+++ b/mix.exs
@@ -125,7 +125,7 @@ defp deps do
{:cors_plug, "~> 1.5"},
{:ex_doc, "~> 0.20.2", only: :dev, runtime: false},
{:web_push_encryption, "~> 0.2.1"},
- {:swoosh, "~> 0.20"},
+ {:swoosh, "~> 0.23.2"},
{:gen_smtp, "~> 0.13"},
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test},
{:floki, "~> 0.20.0"},
diff --git a/mix.lock b/mix.lock
index bd6ab9100..2594ee632 100644
--- a/mix.lock
+++ b/mix.lock
@@ -17,7 +17,7 @@
"credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},
"db_connection": {:hex, :db_connection, "2.0.6", "bde2f85d047969c5b5800cb8f4b3ed6316c8cb11487afedac4aa5f93fd39abfa", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
- "decimal": {:hex, :decimal, "1.7.0", "30d6b52c88541f9a66637359ddf85016df9eb266170d53105f02e4a67e00c5aa", [:mix], [], "hexpm"},
+ "decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"},
"earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"},
"ecto": {:hex, :ecto, "3.1.4", "69d852da7a9f04ede725855a35ede48d158ca11a404fe94f8b2fb3b2162cd3c9", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
@@ -33,7 +33,7 @@
"ex_syslogger": {:git, "https://github.com/slashmili/ex_syslogger.git", "f3963399047af17e038897c69e20d552e6899e1d", [tag: "1.4.0"]},
"excoveralls": {:hex, :excoveralls, "0.11.1", "dd677fbdd49114fdbdbf445540ec735808250d56b011077798316505064edb2c", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"floki": {:hex, :floki, "0.20.4", "be42ac911fece24b4c72f3b5846774b6e61b83fe685c2fc9d62093277fb3bc86", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}, {:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
- "gen_smtp": {:hex, :gen_smtp, "0.13.0", "11f08504c4bdd831dc520b8f84a1dce5ce624474a797394e7aafd3c29f5dcd25", [:rebar3], [], "hexpm"},
+ "gen_smtp": {:hex, :gen_smtp, "0.14.0", "39846a03522456077c6429b4badfd1d55e5e7d0fdfb65e935b7c5e38549d9202", [:rebar3], [], "hexpm"},
"gettext": {:hex, :gettext, "0.15.0", "40a2b8ce33a80ced7727e36768499fc9286881c43ebafccae6bab731e2b2b8ce", [:mix], [], "hexpm"},
"hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm"},
@@ -66,18 +66,19 @@
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
+ "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},
"postgrex": {:hex, :postgrex, "0.14.3", "5754dee2fdf6e9e508cbf49ab138df964278700b764177e8f3871e658b345a1e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"prometheus": {:hex, :prometheus, "4.2.2", "a830e77b79dc6d28183f4db050a7cac926a6c58f1872f9ef94a35cd989aceef8", [:mix, :rebar3], [], "hexpm"},
"prometheus_ecto": {:hex, :prometheus_ecto, "1.4.1", "6c768ea9654de871e5b32fab2eac348467b3021604ebebbcbd8bcbe806a65ed5", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_ex": {:hex, :prometheus_ex, "3.0.5", "fa58cfd983487fc5ead331e9a3e0aa622c67232b3ec71710ced122c4c453a02f", [:mix], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_phoenix": {:hex, :prometheus_phoenix, "1.2.1", "964a74dfbc055f781d3a75631e06ce3816a2913976d1df7830283aa3118a797a", [:mix], [{:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.3 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_plugs": {:hex, :prometheus_plugs, "1.1.5", "25933d48f8af3a5941dd7b621c889749894d8a1082a6ff7c67cc99dec26377c5", [:mix], [{:accept, "~> 0.1", [hex: :accept, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}, {:prometheus_process_collector, "~> 1.1", [hex: :prometheus_process_collector, repo: "hexpm", optional: true]}], "hexpm"},
+ "prometheus_process_collector": {:hex, :prometheus_process_collector, "1.4.0", "6dbd39e3165b9ef1c94a7a820e9ffe08479f949dcdd431ed4aaea7b250eebfde", [:rebar3], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"},
"quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm"},
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
"recon": {:git, "https://github.com/ferd/recon.git", "75d70c7c08926d2f24f1ee6de14ee50fe8a52763", [tag: "2.4.0"]},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
- "stream_data": {:hex, :stream_data, "0.4.3", "62aafd870caff0849a5057a7ec270fad0eb86889f4d433b937d996de99e3db25", [:mix], [], "hexpm"},
- "swoosh": {:hex, :swoosh, "0.20.0", "9a6c13822c9815993c03b6f8fccc370fcffb3c158d9754f67b1fdee6b3a5d928", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.12", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
+ "swoosh": {:hex, :swoosh, "0.23.2", "7dda95ff0bf54a2298328d6899c74dae1223777b43563ccebebb4b5d2b61df38", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]},
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
"tesla": {:hex, :tesla, "1.2.1", "864783cc27f71dd8c8969163704752476cec0f3a51eb3b06393b3971dc9733ff", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
diff --git a/test/emails/admin_email_test.exs b/test/emails/admin_email_test.exs
new file mode 100644
index 000000000..4bf54b0c2
--- /dev/null
+++ b/test/emails/admin_email_test.exs
@@ -0,0 +1,37 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Emails.AdminEmailTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+
+ alias Pleroma.Emails.AdminEmail
+ alias Pleroma.Web.Router.Helpers
+
+ test "build report email" do
+ config = Pleroma.Config.get(:instance)
+ to_user = insert(:user)
+ reporter = insert(:user)
+ account = insert(:user)
+
+ res =
+ AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment")
+
+ status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, "12")
+ reporter_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.nickname)
+ account_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, account.nickname)
+
+ assert res.to == [{to_user.name, to_user.email}]
+ assert res.from == {config[:name], config[:notify_email]}
+ assert res.reply_to == {reporter.name, reporter.email}
+ assert res.subject == "#{config[:name]} Report"
+
+ assert res.html_body ==
+ "Reported by: #{reporter.nickname}
\nReported Account: #{account.nickname}
\nComment: Test comment\n
Statuses:\n
\n
\n\n"
+ end
+end
diff --git a/test/emails/mailer_test.exs b/test/emails/mailer_test.exs
new file mode 100644
index 000000000..450bb09c7
--- /dev/null
+++ b/test/emails/mailer_test.exs
@@ -0,0 +1,57 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Emails.MailerTest do
+ use Pleroma.DataCase
+ alias Pleroma.Emails.Mailer
+
+ import Swoosh.TestAssertions
+
+ @email %Swoosh.Email{
+ from: {"Pleroma", "noreply@example.com"},
+ html_body: "Test email",
+ subject: "Pleroma test email",
+ to: [{"Test User", "user1@example.com"}]
+ }
+
+ setup do
+ value = Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled])
+ on_exit(fn -> Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], value) end)
+ :ok
+ end
+
+ test "not send email when mailer is disabled" do
+ Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false)
+ Mailer.deliver(@email)
+
+ refute_email_sent(
+ from: {"Pleroma", "noreply@example.com"},
+ to: [{"Test User", "user1@example.com"}],
+ html_body: "Test email",
+ subject: "Pleroma test email"
+ )
+ end
+
+ test "send email" do
+ Mailer.deliver(@email)
+
+ assert_email_sent(
+ from: {"Pleroma", "noreply@example.com"},
+ to: [{"Test User", "user1@example.com"}],
+ html_body: "Test email",
+ subject: "Pleroma test email"
+ )
+ end
+
+ test "perform" do
+ Mailer.perform(:deliver_async, @email, [])
+
+ assert_email_sent(
+ from: {"Pleroma", "noreply@example.com"},
+ to: [{"Test User", "user1@example.com"}],
+ html_body: "Test email",
+ subject: "Pleroma test email"
+ )
+ end
+end
diff --git a/test/emails/user_email_test.exs b/test/emails/user_email_test.exs
new file mode 100644
index 000000000..7d8df6abc
--- /dev/null
+++ b/test/emails/user_email_test.exs
@@ -0,0 +1,48 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Emails.UserEmailTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Emails.UserEmail
+ alias Pleroma.Web.Endpoint
+ alias Pleroma.Web.Router
+
+ import Pleroma.Factory
+
+ test "build password reset email" do
+ config = Pleroma.Config.get(:instance)
+ user = insert(:user)
+ email = UserEmail.password_reset_email(user, "test_token")
+ assert email.from == {config[:name], config[:notify_email]}
+ assert email.to == [{user.name, user.email}]
+ assert email.subject == "Password reset"
+ assert email.html_body =~ Router.Helpers.reset_password_url(Endpoint, :reset, "test_token")
+ end
+
+ test "build user invitation email" do
+ config = Pleroma.Config.get(:instance)
+ user = insert(:user)
+ token = %Pleroma.UserInviteToken{token: "test-token"}
+ email = UserEmail.user_invitation_email(user, token, "test@test.com", "Jonh")
+ assert email.from == {config[:name], config[:notify_email]}
+ assert email.subject == "Invitation to Pleroma"
+ assert email.to == [{"Jonh", "test@test.com"}]
+
+ assert email.html_body =~
+ Router.Helpers.redirect_url(Endpoint, :registration_page, token.token)
+ end
+
+ test "build account confirmation email" do
+ config = Pleroma.Config.get(:instance)
+ user = insert(:user, info: %Pleroma.User.Info{confirmation_token: "conf-token"})
+ email = UserEmail.account_confirmation_email(user)
+ assert email.from == {config[:name], config[:notify_email]}
+ assert email.to == [{user.name, user.email}]
+ assert email.subject == "#{config[:name]} account confirmation"
+
+ assert email.html_body =~
+ Router.Helpers.confirm_email_url(Endpoint, :confirm_email, user.id, "conf-token")
+ end
+end
From e2782c342f063f1028575d997d8f369b1aa76964 Mon Sep 17 00:00:00 2001
From: rinpatch
Date: Wed, 10 Jul 2019 11:09:55 +0300
Subject: [PATCH 11/31] Add a breaking changelog entry for explicitly disabling
the mailer and reorder changelog sections in order of importance
---
CHANGELOG.md | 25 ++++++++++++-------------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 763cd8d92..9ec8a5551 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
+### Changed
+- **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
+- Configuration: OpenGraph and TwitterCard providers enabled by default
+- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
+- NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
+
+### Fixed
+- Not being able to pin unlisted posts
+- Metadata rendering errors resulting in the entire page being inaccessible
+- Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`)
+- Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity
+
### Added
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
Configuration: `federation_incoming_replies_max_depth` option
@@ -16,19 +28,6 @@ Configuration: `federation_incoming_replies_max_depth` option
- Added synchronization of following/followers counters for external users
- Configuration: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`.
-### Fixed
-- Not being able to pin unlisted posts
-- Metadata rendering errors resulting in the entire page being inaccessible
-- Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`)
-- Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity
-
-### Changed
-- Configuration: OpenGraph and TwitterCard providers enabled by default
-- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
-
-### Changed
-- NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
-
## [1.0.0] - 2019-06-29
### Security
- Mastodon API: Fix display names not being sanitized
From 008c55e4e995f33f9fd2188568a92f135d235222 Mon Sep 17 00:00:00 2001
From: Maksim
Date: Wed, 10 Jul 2019 08:28:03 +0000
Subject: [PATCH 12/31] add test for search_controller/ 100% coverage
---
.../web/mastodon_api/search_controller.ex | 35 +--
.../web/mastodon_api/views/status_view.ex | 2 +
.../mastodon_api/search_controller_test.exs | 241 ++++++++++++------
3 files changed, 176 insertions(+), 102 deletions(-)
diff --git a/lib/pleroma/web/mastodon_api/search_controller.ex b/lib/pleroma/web/mastodon_api/search_controller.ex
index efa9cc788..939f7f6cb 100644
--- a/lib/pleroma/web/mastodon_api/search_controller.ex
+++ b/lib/pleroma/web/mastodon_api/search_controller.ex
@@ -4,29 +4,27 @@
defmodule Pleroma.Web.MastodonAPI.SearchController do
use Pleroma.Web, :controller
+
alias Pleroma.Activity
+ alias Pleroma.Plugs.RateLimiter
alias Pleroma.User
alias Pleroma.Web
+ alias Pleroma.Web.ControllerHelper
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.StatusView
- alias Pleroma.Web.ControllerHelper
-
require Logger
-
- plug(Pleroma.Plugs.RateLimiter, :search when action in [:search, :search2, :account_search])
+ plug(RateLimiter, :search when action in [:search, :search2, :account_search])
def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, [])
statuses = with_fallback(fn -> Activity.search(user, query) end, [])
+
tags_path = Web.base_url() <> "/tag/"
tags =
query
- |> String.split()
- |> Enum.uniq()
- |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
- |> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
+ |> prepare_tags
|> Enum.map(fn tag -> %{name: tag, url: tags_path <> tag} end)
res = %{
@@ -40,15 +38,10 @@ def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
end
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
- accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, [])
- statuses = with_fallback(fn -> Activity.search(user, query) end, [])
+ accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end)
+ statuses = with_fallback(fn -> Activity.search(user, query) end)
- tags =
- query
- |> String.split()
- |> Enum.uniq()
- |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
- |> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
+ tags = prepare_tags(query)
res = %{
"accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
@@ -67,6 +60,14 @@ def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) d
json(conn, res)
end
+ defp prepare_tags(query) do
+ query
+ |> String.split()
+ |> Enum.uniq()
+ |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
+ |> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
+ end
+
defp search_options(params, user) do
[
resolve: params["resolve"] == "true",
@@ -77,7 +78,7 @@ defp search_options(params, user) do
]
end
- defp with_fallback(f, fallback) do
+ defp with_fallback(f, fallback \\ []) do
try do
f.()
rescue
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index ec582b919..a070bc942 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -19,6 +19,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1]
# TODO: Add cached version.
+ defp get_replied_to_activities([]), do: %{}
+
defp get_replied_to_activities(activities) do
activities
|> Enum.map(fn
diff --git a/test/web/mastodon_api/search_controller_test.exs b/test/web/mastodon_api/search_controller_test.exs
index c3f531590..ea534b393 100644
--- a/test/web/mastodon_api/search_controller_test.exs
+++ b/test/web/mastodon_api/search_controller_test.exs
@@ -6,123 +6,194 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
use Pleroma.Web.ConnCase
alias Pleroma.Object
+ alias Pleroma.Web
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
import ExUnit.CaptureLog
import Tesla.Mock
+ import Mock
setup do
- mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
end
- test "account search", %{conn: conn} do
- user = insert(:user)
- user_two = insert(:user, %{nickname: "shp@shitposter.club"})
- user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
+ describe ".search2" do
+ test "it returns empty result if user or status search return undefined error", %{conn: conn} do
+ with_mocks [
+ {Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]},
+ {Pleroma.Activity, [], [search: fn _u, _q -> raise "Oops" end]}
+ ] do
+ conn = get(conn, "/api/v2/search", %{"q" => "2hu"})
- results =
- conn
- |> assign(:user, user)
- |> get("/api/v1/accounts/search", %{"q" => "shp"})
- |> json_response(200)
+ assert results = json_response(conn, 200)
- result_ids = for result <- results, do: result["acct"]
+ assert results["accounts"] == []
+ assert results["statuses"] == []
+ end
+ end
- assert user_two.nickname in result_ids
- assert user_three.nickname in result_ids
+ test "search", %{conn: conn} do
+ user = insert(:user)
+ user_two = insert(:user, %{nickname: "shp@shitposter.club"})
+ user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
- results =
- conn
- |> assign(:user, user)
- |> get("/api/v1/accounts/search", %{"q" => "2hu"})
- |> json_response(200)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu private"})
- result_ids = for result <- results, do: result["acct"]
+ {:ok, _activity} =
+ CommonAPI.post(user, %{
+ "status" => "This is about 2hu, but private",
+ "visibility" => "private"
+ })
- assert user_three.nickname in result_ids
- end
+ {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
- test "search", %{conn: conn} do
- user = insert(:user)
- user_two = insert(:user, %{nickname: "shp@shitposter.club"})
- user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
-
- {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
-
- {:ok, _activity} =
- CommonAPI.post(user, %{
- "status" => "This is about 2hu, but private",
- "visibility" => "private"
- })
-
- {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
-
- conn =
- conn
- |> get("/api/v1/search", %{"q" => "2hu"})
-
- assert results = json_response(conn, 200)
-
- [account | _] = results["accounts"]
- assert account["id"] == to_string(user_three.id)
-
- assert results["hashtags"] == []
-
- [status] = results["statuses"]
- assert status["id"] == to_string(activity.id)
- end
-
- test "search fetches remote statuses", %{conn: conn} do
- capture_log(fn ->
- conn =
- conn
- |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
+ conn = get(conn, "/api/v2/search", %{"q" => "2hu #private"})
assert results = json_response(conn, 200)
+ # IO.inspect results
+
+ [account | _] = results["accounts"]
+ assert account["id"] == to_string(user_three.id)
+
+ assert results["hashtags"] == [
+ %{"name" => "private", "url" => "#{Web.base_url()}/tag/private"}
+ ]
[status] = results["statuses"]
- assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
- end)
+ assert status["id"] == to_string(activity.id)
+ end
end
- test "search doesn't show statuses that it shouldn't", %{conn: conn} do
- {:ok, activity} =
- CommonAPI.post(insert(:user), %{
- "status" => "This is about 2hu, but private",
- "visibility" => "private"
- })
+ describe ".account_search" do
+ test "account search", %{conn: conn} do
+ user = insert(:user)
+ user_two = insert(:user, %{nickname: "shp@shitposter.club"})
+ user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
+
+ results =
+ conn
+ |> assign(:user, user)
+ |> get("/api/v1/accounts/search", %{"q" => "shp"})
+ |> json_response(200)
+
+ result_ids = for result <- results, do: result["acct"]
+
+ assert user_two.nickname in result_ids
+ assert user_three.nickname in result_ids
+
+ results =
+ conn
+ |> assign(:user, user)
+ |> get("/api/v1/accounts/search", %{"q" => "2hu"})
+ |> json_response(200)
+
+ result_ids = for result <- results, do: result["acct"]
+
+ assert user_three.nickname in result_ids
+ end
+ end
+
+ describe ".search" do
+ test "it returns empty result if user or status search return undefined error", %{conn: conn} do
+ with_mocks [
+ {Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]},
+ {Pleroma.Activity, [], [search: fn _u, _q -> raise "Oops" end]}
+ ] do
+ conn =
+ conn
+ |> get("/api/v1/search", %{"q" => "2hu"})
+
+ assert results = json_response(conn, 200)
+
+ assert results["accounts"] == []
+ assert results["statuses"] == []
+ end
+ end
+
+ test "search", %{conn: conn} do
+ user = insert(:user)
+ user_two = insert(:user, %{nickname: "shp@shitposter.club"})
+ user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
+
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
+
+ {:ok, _activity} =
+ CommonAPI.post(user, %{
+ "status" => "This is about 2hu, but private",
+ "visibility" => "private"
+ })
+
+ {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
- capture_log(fn ->
conn =
conn
- |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
+ |> get("/api/v1/search", %{"q" => "2hu"})
assert results = json_response(conn, 200)
- [] = results["statuses"]
- end)
- end
+ [account | _] = results["accounts"]
+ assert account["id"] == to_string(user_three.id)
- test "search fetches remote accounts", %{conn: conn} do
- user = insert(:user)
+ assert results["hashtags"] == []
- conn =
- conn
- |> assign(:user, user)
- |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
+ [status] = results["statuses"]
+ assert status["id"] == to_string(activity.id)
+ end
- assert results = json_response(conn, 200)
- [account] = results["accounts"]
- assert account["acct"] == "shp@social.heldscal.la"
- end
+ test "search fetches remote statuses", %{conn: conn} do
+ capture_log(fn ->
+ conn =
+ conn
+ |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
- test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do
- conn =
- conn
- |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "false"})
+ assert results = json_response(conn, 200)
- assert results = json_response(conn, 200)
- assert [] == results["accounts"]
+ [status] = results["statuses"]
+
+ assert status["uri"] ==
+ "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
+ end)
+ end
+
+ test "search doesn't show statuses that it shouldn't", %{conn: conn} do
+ {:ok, activity} =
+ CommonAPI.post(insert(:user), %{
+ "status" => "This is about 2hu, but private",
+ "visibility" => "private"
+ })
+
+ capture_log(fn ->
+ conn =
+ conn
+ |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
+
+ assert results = json_response(conn, 200)
+
+ [] = results["statuses"]
+ end)
+ end
+
+ test "search fetches remote accounts", %{conn: conn} do
+ user = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
+
+ assert results = json_response(conn, 200)
+ [account] = results["accounts"]
+ assert account["acct"] == "shp@social.heldscal.la"
+ end
+
+ test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do
+ conn =
+ conn
+ |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "false"})
+
+ assert results = json_response(conn, 200)
+ assert [] == results["accounts"]
+ end
end
end
From 0d54a571ca1c15e97faeeaa8ec18dc829052a94a Mon Sep 17 00:00:00 2001
From: Egor Kislitsyn
Date: Tue, 9 Jul 2019 18:30:15 +0700
Subject: [PATCH 13/31] Add SetLocalePlug
---
lib/pleroma/plugs/set_locale_plug.ex | 63 ++++++++++++++++++++++++++++
lib/pleroma/web/endpoint.ex | 10 ++---
test/plugs/set_locale_plug_test.exs | 46 ++++++++++++++++++++
3 files changed, 114 insertions(+), 5 deletions(-)
create mode 100644 lib/pleroma/plugs/set_locale_plug.ex
create mode 100644 test/plugs/set_locale_plug_test.exs
diff --git a/lib/pleroma/plugs/set_locale_plug.ex b/lib/pleroma/plugs/set_locale_plug.ex
new file mode 100644
index 000000000..8646cb30d
--- /dev/null
+++ b/lib/pleroma/plugs/set_locale_plug.ex
@@ -0,0 +1,63 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+# NOTE: this module is based on https://github.com/smeevil/set_locale
+defmodule Pleroma.Plugs.SetLocalePlug do
+ import Plug.Conn, only: [get_req_header: 2, assign: 3]
+
+ def init(_), do: nil
+
+ def call(conn, _) do
+ locale = get_locale_from_header(conn) || Gettext.get_locale()
+ Gettext.put_locale(locale)
+ assign(conn, :locale, locale)
+ end
+
+ defp get_locale_from_header(conn) do
+ conn
+ |> extract_accept_language()
+ |> Enum.find(&supported_locale?/1)
+ end
+
+ defp extract_accept_language(conn) do
+ case get_req_header(conn, "accept-language") do
+ [value | _] ->
+ value
+ |> String.split(",")
+ |> Enum.map(&parse_language_option/1)
+ |> Enum.sort(&(&1.quality > &2.quality))
+ |> Enum.map(& &1.tag)
+ |> Enum.reject(&is_nil/1)
+ |> ensure_language_fallbacks()
+
+ _ ->
+ []
+ end
+ end
+
+ defp supported_locale?(locale) do
+ Pleroma.Web.Gettext
+ |> Gettext.known_locales()
+ |> Enum.member?(locale)
+ end
+
+ defp parse_language_option(string) do
+ captures = Regex.named_captures(~r/^\s?(?[\w\-]+)(?:;q=(?[\d\.]+))?$/i, string)
+
+ quality =
+ case Float.parse(captures["quality"] || "1.0") do
+ {val, _} -> val
+ :error -> 1.0
+ end
+
+ %{tag: captures["tag"], quality: quality}
+ end
+
+ defp ensure_language_fallbacks(tags) do
+ Enum.flat_map(tags, fn tag ->
+ [language | _] = String.split(tag, "-")
+ if Enum.member?(tags, language), do: [tag], else: [tag, language]
+ end)
+ end
+end
diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex
index ddaf88f1d..c123530dc 100644
--- a/lib/pleroma/web/endpoint.ex
+++ b/lib/pleroma/web/endpoint.ex
@@ -7,13 +7,9 @@ defmodule Pleroma.Web.Endpoint do
socket("/socket", Pleroma.Web.UserSocket)
- # Serve at "/" the static files from "priv/static" directory.
- #
- # You should set gzip to true if you are running phoenix.digest
- # when deploying your static files in production.
+ plug(Pleroma.Plugs.SetLocalePlug)
plug(CORSPlug)
plug(Pleroma.Plugs.HTTPSecurityPlug)
-
plug(Pleroma.Plugs.UploadedMedia)
@static_cache_control "public, no-cache"
@@ -30,6 +26,10 @@ defmodule Pleroma.Web.Endpoint do
}
)
+ # Serve at "/" the static files from "priv/static" directory.
+ #
+ # You should set gzip to true if you are running phoenix.digest
+ # when deploying your static files in production.
plug(
Plug.Static,
at: "/",
diff --git a/test/plugs/set_locale_plug_test.exs b/test/plugs/set_locale_plug_test.exs
new file mode 100644
index 000000000..3e31b0ae7
--- /dev/null
+++ b/test/plugs/set_locale_plug_test.exs
@@ -0,0 +1,46 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Plugs.SetLocalePlugTest do
+ use ExUnit.Case, async: true
+ use Plug.Test
+
+ alias Plug.Conn
+ alias Pleroma.Plugs.SetLocalePlug
+
+ test "default locale is `en`" do
+ conn =
+ :get
+ |> conn("/cofe")
+ |> SetLocalePlug.call([])
+
+ assert "en" == Gettext.get_locale()
+ assert %{locale: "en"} == conn.assigns
+ end
+
+ test "use supported locale from `accept-language`" do
+ conn =
+ :get
+ |> conn("/cofe")
+ |> Conn.put_req_header(
+ "accept-language",
+ "ru, fr-CH, fr;q=0.9, en;q=0.8, *;q=0.5"
+ )
+ |> SetLocalePlug.call([])
+
+ assert "ru" == Gettext.get_locale()
+ assert %{locale: "ru"} == conn.assigns
+ end
+
+ test "use default locale if locale from `accept-language` is not supported" do
+ conn =
+ :get
+ |> conn("/cofe")
+ |> Conn.put_req_header("accept-language", "tlh")
+ |> SetLocalePlug.call([])
+
+ assert "en" == Gettext.get_locale()
+ assert %{locale: "en"} == conn.assigns
+ end
+end
From 663aebdd59295a6d92bacd973df343a0ffc43a7d Mon Sep 17 00:00:00 2001
From: Egor Kislitsyn
Date: Wed, 10 Jul 2019 14:01:57 +0700
Subject: [PATCH 14/31] Update `gettext` dependency
---
mix.lock | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mix.lock b/mix.lock
index 2594ee632..9c0fd0e98 100644
--- a/mix.lock
+++ b/mix.lock
@@ -34,7 +34,7 @@
"excoveralls": {:hex, :excoveralls, "0.11.1", "dd677fbdd49114fdbdbf445540ec735808250d56b011077798316505064edb2c", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"floki": {:hex, :floki, "0.20.4", "be42ac911fece24b4c72f3b5846774b6e61b83fe685c2fc9d62093277fb3bc86", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}, {:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
"gen_smtp": {:hex, :gen_smtp, "0.14.0", "39846a03522456077c6429b4badfd1d55e5e7d0fdfb65e935b7c5e38549d9202", [:rebar3], [], "hexpm"},
- "gettext": {:hex, :gettext, "0.15.0", "40a2b8ce33a80ced7727e36768499fc9286881c43ebafccae6bab731e2b2b8ce", [:mix], [], "hexpm"},
+ "gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"},
"hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm"},
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
From 26a6871609036c3f3dae53f38effa262af0b10dd Mon Sep 17 00:00:00 2001
From: Egor Kislitsyn
Date: Wed, 10 Jul 2019 14:52:41 +0700
Subject: [PATCH 15/31] Add translation helpers
---
lib/pleroma/web/translation_helpers.ex | 17 +++++++++++++++++
lib/pleroma/web/web.ex | 2 ++
2 files changed, 19 insertions(+)
create mode 100644 lib/pleroma/web/translation_helpers.ex
diff --git a/lib/pleroma/web/translation_helpers.ex b/lib/pleroma/web/translation_helpers.ex
new file mode 100644
index 000000000..8f5a43bf6
--- /dev/null
+++ b/lib/pleroma/web/translation_helpers.ex
@@ -0,0 +1,17 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.TranslationHelpers do
+ defmacro render_error(conn, status, msgid, bindings \\ Macro.escape(%{})) do
+ quote do
+ require Pleroma.Web.Gettext
+
+ unquote(conn)
+ |> Plug.Conn.put_status(unquote(status))
+ |> Phoenix.Controller.json(%{
+ error: Pleroma.Web.Gettext.dgettext("errors", unquote(msgid), unquote(bindings))
+ })
+ end
+ end
+end
diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex
index 66813e4dd..b42f6887e 100644
--- a/lib/pleroma/web/web.ex
+++ b/lib/pleroma/web/web.ex
@@ -23,9 +23,11 @@ defmodule Pleroma.Web do
def controller do
quote do
use Phoenix.Controller, namespace: Pleroma.Web
+
import Plug.Conn
import Pleroma.Web.Gettext
import Pleroma.Web.Router.Helpers
+ import Pleroma.Web.TranslationHelpers
plug(:set_put_layout)
From 5104f65b69cb00155c3e0f3ea2c6dca5bb8c10b7 Mon Sep 17 00:00:00 2001
From: Egor Kislitsyn
Date: Wed, 10 Jul 2019 16:25:58 +0700
Subject: [PATCH 16/31] Wrap error messages into gettext helpers
---
lib/pleroma/captcha/captcha.ex | 9 +-
lib/pleroma/captcha/kocaptcha.ex | 5 +-
.../plugs/ensure_authenticated_plug.ex | 4 +-
.../ensure_public_or_authenticated_plug.ex | 4 +-
lib/pleroma/plugs/oauth_scopes_plug.ex | 8 +-
lib/pleroma/plugs/rate_limiter.ex | 10 +-
lib/pleroma/plugs/uploaded_media.ex | 8 +-
lib/pleroma/plugs/user_is_admin_plug.ex | 4 +-
lib/pleroma/uploaders/uploader.ex | 4 +-
.../activity_pub/activity_pub_controller.ex | 37 +-
.../web/admin_api/admin_api_controller.ex | 26 +-
lib/pleroma/web/admin_api/config.ex | 7 +-
lib/pleroma/web/common_api/common_api.ex | 58 ++-
lib/pleroma/web/common_api/utils.ex | 13 +-
.../mastodon_api/mastodon_api_controller.ex | 156 +++-----
.../mastodon_api/subscription_controller.ex | 8 +-
.../web/mongooseim/mongoose_im_controller.ex | 2 +-
.../web/nodeinfo/nodeinfo_controller.ex | 4 +-
lib/pleroma/web/oauth/fallback_controller.ex | 9 +-
lib/pleroma/web/oauth/oauth_controller.ex | 52 ++-
lib/pleroma/web/ostatus/ostatus_controller.ex | 8 +-
lib/pleroma/web/uploader_controller.ex | 4 +-
priv/gettext/en/LC_MESSAGES/errors.po | 372 +++++++++++++++++
priv/gettext/errors.pot | 373 +++++++++++++++++-
24 files changed, 948 insertions(+), 237 deletions(-)
diff --git a/lib/pleroma/captcha/captcha.ex b/lib/pleroma/captcha/captcha.ex
index f105cbb25..a73b87251 100644
--- a/lib/pleroma/captcha/captcha.ex
+++ b/lib/pleroma/captcha/captcha.ex
@@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha do
+ import Pleroma.Web.Gettext
+
alias Calendar.DateTime
alias Plug.Crypto.KeyGenerator
alias Plug.Crypto.MessageEncryptor
@@ -83,10 +85,11 @@ def handle_call({:validate, token, captcha, answer_data}, _from, state) do
with {:ok, data} <- MessageEncryptor.decrypt(answer_data, secret, sign_secret),
%{at: at, answer_data: answer_md5} <- :erlang.binary_to_term(data) do
try do
- if DateTime.before?(at, valid_if_after), do: throw({:error, "CAPTCHA expired"})
+ if DateTime.before?(at, valid_if_after),
+ do: throw({:error, dgettext("errors", "CAPTCHA expired")})
if not is_nil(Cachex.get!(:used_captcha_cache, token)),
- do: throw({:error, "CAPTCHA already used"})
+ do: throw({:error, dgettext("errors", "CAPTCHA already used")})
res = method().validate(token, captcha, answer_md5)
# Throw if an error occurs
@@ -101,7 +104,7 @@ def handle_call({:validate, token, captcha, answer_data}, _from, state) do
:throw, e -> e
end
else
- _ -> {:error, "Invalid answer data"}
+ _ -> {:error, dgettext("errors", "Invalid answer data")}
end
{:reply, result, state}
diff --git a/lib/pleroma/captcha/kocaptcha.ex b/lib/pleroma/captcha/kocaptcha.ex
index 18931d5a0..4e1a07c59 100644
--- a/lib/pleroma/captcha/kocaptcha.ex
+++ b/lib/pleroma/captcha/kocaptcha.ex
@@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha.Kocaptcha do
+ import Pleroma.Web.Gettext
alias Pleroma.Captcha.Service
@behaviour Service
@@ -12,7 +13,7 @@ def new do
case Tesla.get(endpoint <> "/new") do
{:error, _} ->
- %{error: "Kocaptcha service unavailable"}
+ %{error: dgettext("errors", "Kocaptcha service unavailable")}
{:ok, res} ->
json_resp = Jason.decode!(res.body)
@@ -32,6 +33,6 @@ def validate(_token, captcha, answer_data) do
if not is_nil(captcha) and
:crypto.hash(:md5, captcha) |> Base.encode16() == String.upcase(answer_data),
do: :ok,
- else: {:error, "Invalid CAPTCHA"}
+ else: {:error, dgettext("errors", "Invalid CAPTCHA")}
end
end
diff --git a/lib/pleroma/plugs/ensure_authenticated_plug.ex b/lib/pleroma/plugs/ensure_authenticated_plug.ex
index 11c4342c4..27cd41aec 100644
--- a/lib/pleroma/plugs/ensure_authenticated_plug.ex
+++ b/lib/pleroma/plugs/ensure_authenticated_plug.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Plugs.EnsureAuthenticatedPlug do
import Plug.Conn
+ import Pleroma.Web.TranslationHelpers
alias Pleroma.User
def init(options) do
@@ -16,8 +17,7 @@ def call(%{assigns: %{user: %User{}}} = conn, _) do
def call(conn, _) do
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{error: "Invalid credentials."}))
+ |> render_error(:forbidden, "Invalid credentials.")
|> halt
end
end
diff --git a/lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex b/lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex
index 317fd5445..a16f61435 100644
--- a/lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex
+++ b/lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex
@@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug do
+ import Pleroma.Web.TranslationHelpers
import Plug.Conn
alias Pleroma.Config
alias Pleroma.User
@@ -23,8 +24,7 @@ def call(conn, _) do
{false, _} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{error: "This resource requires authentication."}))
+ |> render_error(:forbidden, "This resource requires authentication.")
|> halt
end
end
diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/plugs/oauth_scopes_plug.ex
index f2bfa2b1a..b508628a9 100644
--- a/lib/pleroma/plugs/oauth_scopes_plug.ex
+++ b/lib/pleroma/plugs/oauth_scopes_plug.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Plugs.OAuthScopesPlug do
import Plug.Conn
+ import Pleroma.Web.Gettext
@behaviour Plug
@@ -30,11 +31,14 @@ def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
true ->
missing_scopes = scopes -- token.scopes
- error_message = "Insufficient permissions: #{Enum.join(missing_scopes, " #{op} ")}."
+ permissions = Enum.join(missing_scopes, " #{op} ")
+
+ error_message =
+ dgettext("errors", "Insufficient permissions: %{permissions}.", permissions: permissions)
conn
|> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{error: error_message}))
+ |> send_resp(:forbidden, Jason.encode!(%{error: error_message}))
|> halt()
end
end
diff --git a/lib/pleroma/plugs/rate_limiter.ex b/lib/pleroma/plugs/rate_limiter.ex
index 9ba5875fa..c5e0957e8 100644
--- a/lib/pleroma/plugs/rate_limiter.ex
+++ b/lib/pleroma/plugs/rate_limiter.ex
@@ -44,8 +44,7 @@ defmodule Pleroma.Plugs.RateLimiter do
...
end
"""
-
- import Phoenix.Controller, only: [json: 2]
+ import Pleroma.Web.TranslationHelpers
import Plug.Conn
alias Pleroma.User
@@ -63,7 +62,7 @@ def call(conn, nil), do: conn
def call(conn, opts) do
case check_rate(conn, opts) do
{:ok, _count} -> conn
- {:error, _count} -> render_error(conn)
+ {:error, _count} -> render_throttled_error(conn)
end
end
@@ -85,10 +84,9 @@ def ip(%{remote_ip: remote_ip}) do
|> Enum.join(".")
end
- defp render_error(conn) do
+ defp render_throttled_error(conn) do
conn
- |> put_status(:too_many_requests)
- |> json(%{error: "Throttled"})
+ |> render_error(:too_many_requests, "Throttled")
|> halt()
end
end
diff --git a/lib/pleroma/plugs/uploaded_media.ex b/lib/pleroma/plugs/uploaded_media.ex
index 8d0fac7ee..be2c17c5f 100644
--- a/lib/pleroma/plugs/uploaded_media.ex
+++ b/lib/pleroma/plugs/uploaded_media.ex
@@ -7,6 +7,8 @@ defmodule Pleroma.Plugs.UploadedMedia do
"""
import Plug.Conn
+ import Pleroma.Web.Gettext
+ import Pleroma.Web.TranslationHelpers
require Logger
@behaviour Plug
@@ -45,7 +47,7 @@ def call(%{request_path: <<"/", @path, "/", file::binary>>} = conn, opts) do
else
_ ->
conn
- |> send_resp(500, "Failed")
+ |> send_resp(:internal_server_error, dgettext("errors", "Failed"))
|> halt()
end
end
@@ -64,7 +66,7 @@ defp get_media(conn, {:static_dir, directory}, _, opts) do
conn
else
conn
- |> send_resp(404, "Not found")
+ |> render_error(:not_found, "Not found")
|> halt()
end
end
@@ -84,7 +86,7 @@ defp get_media(conn, unknown, _, _) do
Logger.error("#{__MODULE__}: Unknown get startegy: #{inspect(unknown)}")
conn
- |> send_resp(500, "Internal Error")
+ |> render_error(:internal_server_error, "Internal Error")
|> halt()
end
end
diff --git a/lib/pleroma/plugs/user_is_admin_plug.ex b/lib/pleroma/plugs/user_is_admin_plug.ex
index 04329e919..4c4b3d610 100644
--- a/lib/pleroma/plugs/user_is_admin_plug.ex
+++ b/lib/pleroma/plugs/user_is_admin_plug.ex
@@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.UserIsAdminPlug do
+ import Pleroma.Web.TranslationHelpers
import Plug.Conn
alias Pleroma.User
@@ -16,8 +17,7 @@ def call(%{assigns: %{user: %User{info: %{is_admin: true}}}} = conn, _) do
def call(conn, _) do
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{error: "User is not admin."}))
+ |> render_error(:forbidden, "User is not admin.")
|> halt
end
end
diff --git a/lib/pleroma/uploaders/uploader.ex b/lib/pleroma/uploaders/uploader.ex
index bf15389fc..0af76bc59 100644
--- a/lib/pleroma/uploaders/uploader.ex
+++ b/lib/pleroma/uploaders/uploader.ex
@@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Uploaders.Uploader do
+ import Pleroma.Web.Gettext
+
@moduledoc """
Defines the contract to put and get an uploaded file to any backend.
"""
@@ -66,7 +68,7 @@ defp handle_callback(uploader, upload) do
{:error, error}
end
after
- 30_000 -> {:error, "Uploader callback timeout"}
+ 30_000 -> {:error, dgettext("errors", "Uploader callback timeout")}
end
end
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index 0182bda46..cf5176201 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -31,9 +31,8 @@ def relay_active?(conn, _) do
conn
else
conn
- |> put_status(404)
- |> json(%{error: "not found"})
- |> halt
+ |> render_error(:not_found, "not found")
+ |> halt()
end
end
@@ -190,7 +189,7 @@ def inbox(conn, params) do
Logger.info(inspect(conn.req_headers))
end
- json(conn, "error")
+ json(conn, dgettext("errors", "error"))
end
def relay(conn, _params) do
@@ -218,9 +217,15 @@ def read_inbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = par
|> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("inbox.json", %{user: user, max_id: params["max_id"]}))
else
+ err =
+ dgettext("errors", "can't read inbox of %{nickname} as %{as_nickname}",
+ nickname: nickname,
+ as_nickname: user.nickname
+ )
+
conn
|> put_status(:forbidden)
- |> json("can't read inbox of #{nickname} as #{user.nickname}")
+ |> json(err)
end
end
@@ -246,7 +251,7 @@ def handle_user_activity(user, %{"type" => "Delete"} = params) do
{:ok, delete} <- ActivityPub.delete(object) do
{:ok, delete}
else
- _ -> {:error, "Can't delete object"}
+ _ -> {:error, dgettext("errors", "Can't delete object")}
end
end
@@ -255,12 +260,12 @@ def handle_user_activity(user, %{"type" => "Like"} = params) do
{:ok, activity, _object} <- ActivityPub.like(user, object) do
{:ok, activity}
else
- _ -> {:error, "Can't like object"}
+ _ -> {:error, dgettext("errors", "Can't like object")}
end
end
def handle_user_activity(_, _) do
- {:error, "Unhandled activity type"}
+ {:error, dgettext("errors", "Unhandled activity type")}
end
def update_outbox(
@@ -288,22 +293,28 @@ def update_outbox(
|> json(message)
end
else
+ err =
+ dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",
+ nickname: nickname,
+ as_nickname: user.nickname
+ )
+
conn
|> put_status(:forbidden)
- |> json("can't update outbox of #{nickname} as #{user.nickname}")
+ |> json(err)
end
end
def errors(conn, {:error, :not_found}) do
conn
- |> put_status(404)
- |> json("Not found")
+ |> put_status(:not_found)
+ |> json(dgettext("errors", "Not found"))
end
def errors(conn, _e) do
conn
- |> put_status(500)
- |> json("error")
+ |> put_status(:internal_server_error)
+ |> json(dgettext("errors", "error"))
end
defp set_requester_reachable(%Plug.Conn{} = conn, _) do
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index 0a2482a8c..8b3c3c91f 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -160,9 +160,7 @@ def right_add(conn, %{"permission_group" => permission_group, "nickname" => nick
end
def right_add(conn, _) do
- conn
- |> put_status(404)
- |> json(%{error: "No such permission_group"})
+ render_error(conn, :not_found, "No such permission_group")
end
def right_get(conn, %{"nickname" => nickname}) do
@@ -184,9 +182,7 @@ def right_delete(
)
when permission_group in ["moderator", "admin"] do
if admin_nickname == nickname do
- conn
- |> put_status(403)
- |> json(%{error: "You can't revoke your own admin status."})
+ render_error(conn, :forbidden, "You can't revoke your own admin status.")
else
user = User.get_cached_by_nickname(nickname)
@@ -207,9 +203,7 @@ def right_delete(
end
def right_delete(conn, _) do
- conn
- |> put_status(404)
- |> json(%{error: "No such permission_group"})
+ render_error(conn, :not_found, "No such permission_group")
end
def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
@@ -401,26 +395,26 @@ def config_update(conn, %{"configs" => configs}) do
def errors(conn, {:error, :not_found}) do
conn
- |> put_status(404)
- |> json("Not found")
+ |> put_status(:not_found)
+ |> json(dgettext("errors", "Not found"))
end
def errors(conn, {:error, reason}) do
conn
- |> put_status(400)
+ |> put_status(:bad_request)
|> json(reason)
end
def errors(conn, {:param_cast, _}) do
conn
- |> put_status(400)
- |> json("Invalid parameters")
+ |> put_status(:bad_request)
+ |> json(dgettext("errors", "Invalid parameters"))
end
def errors(conn, _) do
conn
- |> put_status(500)
- |> json("Something went wrong")
+ |> put_status(:internal_server_error)
+ |> json(dgettext("errors", "Something went wrong"))
end
defp page_params(params) do
diff --git a/lib/pleroma/web/admin_api/config.ex b/lib/pleroma/web/admin_api/config.ex
index 8b9b658a9..24674abc5 100644
--- a/lib/pleroma/web/admin_api/config.ex
+++ b/lib/pleroma/web/admin_api/config.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.AdminAPI.Config do
use Ecto.Schema
import Ecto.Changeset
+ import Pleroma.Web.Gettext
alias __MODULE__
alias Pleroma.Repo
@@ -57,7 +58,11 @@ def delete(params) do
with %Config{} = config <- Config.get_by_params(params) do
Repo.delete(config)
else
- nil -> {:error, "Config with params #{inspect(params)} not found"}
+ nil ->
+ err =
+ dgettext("errors", "Config with params %{params} not found", params: inspect(params))
+
+ {:error, err}
end
end
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index f71c67a3d..f1450b113 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -13,6 +13,7 @@ defmodule Pleroma.Web.CommonAPI do
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
+ import Pleroma.Web.Gettext
import Pleroma.Web.CommonAPI.Utils
def follow(follower, followed) do
@@ -74,7 +75,7 @@ def delete(activity_id, user) do
{:ok, delete}
else
_ ->
- {:error, "Could not delete"}
+ {:error, dgettext("errors", "Could not delete")}
end
end
@@ -85,7 +86,7 @@ def repeat(id_or_ap_id, user) do
ActivityPub.announce(user, object)
else
_ ->
- {:error, "Could not repeat"}
+ {:error, dgettext("errors", "Could not repeat")}
end
end
@@ -95,7 +96,7 @@ def unrepeat(id_or_ap_id, user) do
ActivityPub.unannounce(user, object)
else
_ ->
- {:error, "Could not unrepeat"}
+ {:error, dgettext("errors", "Could not unrepeat")}
end
end
@@ -106,7 +107,7 @@ def favorite(id_or_ap_id, user) do
ActivityPub.like(user, object)
else
_ ->
- {:error, "Could not favorite"}
+ {:error, dgettext("errors", "Could not favorite")}
end
end
@@ -116,7 +117,7 @@ def unfavorite(id_or_ap_id, user) do
ActivityPub.unlike(user, object)
else
_ ->
- {:error, "Could not unfavorite"}
+ {:error, dgettext("errors", "Could not unfavorite")}
end
end
@@ -148,10 +149,10 @@ def vote(user, object, choices) do
object = Object.get_cached_by_ap_id(object.data["id"])
{:ok, answer_activities, object}
else
- {:author, _} -> {:error, "Poll's author can't vote"}
- {:existing_votes, _} -> {:error, "Already voted"}
- {:choice_check, {_, false}} -> {:error, "Invalid indices"}
- {:count_check, false} -> {:error, "Too many choices"}
+ {:author, _} -> {:error, dgettext("errors", "Poll's author can't vote")}
+ {:existing_votes, _} -> {:error, dgettext("errors", "Already voted")}
+ {:choice_check, {_, false}} -> {:error, dgettext("errors", "Invalid indices")}
+ {:count_check, false} -> {:error, dgettext("errors", "Too many choices")}
end
end
@@ -248,9 +249,14 @@ def post(user, %{"status" => status} = data) do
res
else
- {:private_to_public, true} -> {:error, "The message visibility must be direct"}
- {:error, _} = e -> e
- e -> {:error, e}
+ {:private_to_public, true} ->
+ {:error, dgettext("errors", "The message visibility must be direct")}
+
+ {:error, _} = e ->
+ e
+
+ e ->
+ {:error, e}
end
end
@@ -301,7 +307,7 @@ def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do
{:error, err}
_ ->
- {:error, "Could not pin"}
+ {:error, dgettext("errors", "Could not pin")}
end
end
@@ -318,7 +324,7 @@ def unpin(id_or_ap_id, user) do
{:error, err}
_ ->
- {:error, "Could not unpin"}
+ {:error, dgettext("errors", "Could not unpin")}
end
end
@@ -326,7 +332,7 @@ def add_mute(user, activity) do
with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]) do
{:ok, activity}
else
- {:error, _} -> {:error, "conversation is already muted"}
+ {:error, _} -> {:error, dgettext("errors", "conversation is already muted")}
end
end
@@ -371,8 +377,8 @@ def report(user, data) do
{:ok, activity}
else
{:error, err} -> {:error, err}
- {:account_id, %{}} -> {:error, "Valid `account_id` required"}
- {:account, nil} -> {:error, "Account not found"}
+ {:account_id, %{}} -> {:error, dgettext("errors", "Valid `account_id` required")}
+ {:account, nil} -> {:error, dgettext("errors", "Account not found")}
end
end
@@ -381,14 +387,9 @@ def update_report_state(activity_id, state) do
{:ok, activity} <- Utils.update_report_state(activity, state) do
{:ok, activity}
else
- nil ->
- {:error, :not_found}
-
- {:error, reason} ->
- {:error, reason}
-
- _ ->
- {:error, "Could not update state"}
+ nil -> {:error, :not_found}
+ {:error, reason} -> {:error, reason}
+ _ -> {:error, dgettext("errors", "Could not update state")}
end
end
@@ -398,11 +399,8 @@ def update_activity_scope(activity_id, opts \\ %{}) do
{:ok, activity} <- set_visibility(activity, opts) do
{:ok, activity}
else
- nil ->
- {:error, :not_found}
-
- {:error, reason} ->
- {:error, reason}
+ nil -> {:error, :not_found}
+ {:error, reason} -> {:error, reason}
end
end
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 8b9477927..8e482eef7 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.CommonAPI.Utils do
+ import Pleroma.Web.Gettext
+
alias Calendar.Strftime
alias Comeonin.Pbkdf2
alias Pleroma.Activity
@@ -372,7 +374,7 @@ def confirm_current_password(user, password) do
true <- Pbkdf2.checkpw(password, db_user.password_hash) do
{:ok, db_user}
else
- _ -> {:error, "Invalid password."}
+ _ -> {:error, dgettext("errors", "Invalid password.")}
end
end
@@ -455,7 +457,8 @@ def make_report_content_html(comment) do
if String.length(comment) <= max_size do
{:ok, format_input(comment, "text/plain")}
else
- {:error, "Comment must be up to #{max_size} characters"}
+ {:error,
+ dgettext("errors", "Comment must be up to %{max_size} characters", max_size: max_size)}
end
end
@@ -490,7 +493,7 @@ def conversation_id_to_context(id) do
context
else
_e ->
- {:error, "No such conversation"}
+ {:error, dgettext("errors", "No such conversation")}
end
end
@@ -512,10 +515,10 @@ def validate_character_limit(full_payload, attachments, limit) do
if length > 0 or Enum.count(attachments) > 0 do
:ok
else
- {:error, "Cannot post an empty status without attachments"}
+ {:error, dgettext("errors", "Cannot post an empty status without attachments")}
end
else
- {:error, "The status is over the character limit"}
+ {:error, dgettext("errors", "The status is over the character limit")}
end
end
end
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 0d3a878bb..82f180635 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -160,10 +160,7 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
AccountView.render("account.json", %{user: user, for: user, with_pleroma_settings: true})
)
else
- _e ->
- conn
- |> put_status(403)
- |> json(%{error: "Invalid request"})
+ _e -> render_error(conn, :forbidden, "Invalid request")
end
end
@@ -258,10 +255,7 @@ def user(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do
account = AccountView.render("account.json", %{user: user, for: for_user})
json(conn, account)
else
- _e ->
- conn
- |> put_status(404)
- |> json(%{error: "Can't find user"})
+ _e -> render_error(conn, :not_found, "Can't find user")
end
end
@@ -509,15 +503,8 @@ def get_poll(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|> put_view(StatusView)
|> try_render("poll.json", %{object: object, for: user})
else
- nil ->
- conn
- |> put_status(404)
- |> json(%{error: "Record not found"})
-
- false ->
- conn
- |> put_status(404)
- |> json(%{error: "Record not found"})
+ nil -> render_error(conn, :not_found, "Record not found")
+ false -> render_error(conn, :not_found, "Record not found")
end
end
@@ -546,18 +533,14 @@ def poll_vote(%{assigns: %{user: user}} = conn, %{"id" => id, "choices" => choic
|> try_render("poll.json", %{object: object, for: user})
else
nil ->
- conn
- |> put_status(404)
- |> json(%{error: "Record not found"})
+ render_error(conn, :not_found, "Record not found")
false ->
- conn
- |> put_status(404)
- |> json(%{error: "Record not found"})
+ render_error(conn, :not_found, "Record not found")
{:error, message} ->
conn
- |> put_status(422)
+ |> put_status(:unprocessable_entity)
|> json(%{error: message})
end
end
@@ -646,10 +629,7 @@ def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
json(conn, %{})
else
- _e ->
- conn
- |> put_status(403)
- |> json(%{error: "Can't delete this post"})
+ _e -> render_error(conn, :forbidden, "Can't delete this post")
end
end
@@ -697,8 +677,8 @@ def pin_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
else
{:error, reason} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(:bad_request, Jason.encode!(%{"error" => reason}))
+ |> put_status(:bad_request)
+ |> json(%{"error" => reason})
end
end
@@ -774,8 +754,8 @@ def get_notification(%{assigns: %{user: user}} = conn, %{"id" => id} = _params)
else
{:error, reason} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => reason}))
+ |> put_status(:forbidden)
+ |> json(%{"error" => reason})
end
end
@@ -790,8 +770,8 @@ def dismiss_notification(%{assigns: %{user: user}} = conn, %{"id" => id} = _para
else
{:error, reason} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => reason}))
+ |> put_status(:forbidden)
+ |> json(%{"error" => reason})
end
end
@@ -869,9 +849,7 @@ def set_mascot(%{assigns: %{user: user}} = conn, %{"file" => file}) do
conn
|> json(rendered)
else
- conn
- |> put_resp_content_type("application/json")
- |> send_resp(415, Jason.encode!(%{"error" => "mascots can only be images"}))
+ render_error(conn, :unsupported_media_type, "mascots can only be images")
end
end
end
@@ -1000,8 +978,8 @@ def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1014,8 +992,8 @@ def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) d
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1032,8 +1010,8 @@ def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1050,8 +1028,8 @@ def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1080,8 +1058,8 @@ def mute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1094,8 +1072,8 @@ def unmute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1116,8 +1094,8 @@ def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1131,8 +1109,8 @@ def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1166,8 +1144,8 @@ def subscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1180,8 +1158,8 @@ def unsubscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do
else
{:error, message} ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ |> put_status(:forbidden)
+ |> json(%{error: message})
end
end
@@ -1229,13 +1207,8 @@ def user_favourites(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params
|> put_view(StatusView)
|> render("index.json", %{activities: activities, for: for_user, as: :activity})
else
- nil ->
- {:error, :not_found}
-
- true ->
- conn
- |> put_status(403)
- |> json(%{error: "Can't get favorites"})
+ nil -> {:error, :not_found}
+ true -> render_error(conn, :forbidden, "Can't get favorites")
end
end
@@ -1267,10 +1240,7 @@ def get_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do
res = ListView.render("list.json", list: list)
json(conn, res)
else
- _e ->
- conn
- |> put_status(404)
- |> json(%{error: "Record not found"})
+ _e -> render_error(conn, :not_found, "Record not found")
end
end
@@ -1286,7 +1256,7 @@ def delete_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do
json(conn, %{})
else
_e ->
- json(conn, "error")
+ json(conn, dgettext("errors", "error"))
end
end
@@ -1337,7 +1307,7 @@ def rename_list(%{assigns: %{user: user}} = conn, %{"id" => id, "title" => title
json(conn, res)
else
_e ->
- json(conn, "error")
+ json(conn, dgettext("errors", "error"))
end
end
@@ -1361,10 +1331,7 @@ def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params)
|> put_view(StatusView)
|> render("index.json", %{activities: activities, for: user, as: :activity})
else
- _e ->
- conn
- |> put_status(403)
- |> json(%{error: "Error."})
+ _e -> render_error(conn, :forbidden, "Error.")
end
end
@@ -1483,8 +1450,8 @@ def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _para
else
e ->
conn
- |> put_resp_content_type("application/json")
- |> send_resp(500, Jason.encode!(%{"error" => inspect(e)}))
+ |> put_status(:internal_server_error)
+ |> json(%{error: inspect(e)})
end
end
@@ -1652,20 +1619,18 @@ def errors(conn, {:error, %Changeset{} = changeset}) do
|> Enum.map_join(", ", fn {_k, v} -> v end)
conn
- |> put_status(422)
+ |> put_status(:unprocessable_entity)
|> json(%{error: error_message})
end
def errors(conn, {:error, :not_found}) do
- conn
- |> put_status(404)
- |> json(%{error: "Record not found"})
+ render_error(conn, :not_found, "Record not found")
end
def errors(conn, _) do
conn
- |> put_status(500)
- |> json("Something went wrong")
+ |> put_status(:internal_server_error)
+ |> json(dgettext("errors", "Something went wrong"))
end
def suggestions(%{assigns: %{user: user}} = conn, _) do
@@ -1785,21 +1750,17 @@ def account_register(
else
{:error, errors} ->
conn
- |> put_status(400)
- |> json(Jason.encode!(errors))
+ |> put_status(:bad_request)
+ |> json(errors)
end
end
def account_register(%{assigns: %{app: _app}} = conn, _params) do
- conn
- |> put_status(400)
- |> json(%{error: "Missing parameters"})
+ render_error(conn, :bad_request, "Missing parameters")
end
def account_register(conn, _) do
- conn
- |> put_status(403)
- |> json(%{error: "Invalid credentials"})
+ render_error(conn, :forbidden, "Invalid credentials")
end
def conversations(%{assigns: %{user: user}} = conn, params) do
@@ -1829,21 +1790,14 @@ def conversation_read(%{assigns: %{user: user}} = conn, %{"id" => participation_
def try_render(conn, target, params)
when is_binary(target) do
- res = render(conn, target, params)
-
- if res == nil do
- conn
- |> put_status(501)
- |> json(%{error: "Can't display this activity"})
- else
- res
+ case render(conn, target, params) do
+ nil -> render_error(conn, :not_implemented, "Can't display this activity")
+ res -> res
end
end
def try_render(conn, _, _) do
- conn
- |> put_status(501)
- |> json(%{error: "Can't display this activity"})
+ render_error(conn, :not_implemented, "Can't display this activity")
end
defp present?(nil), do: false
diff --git a/lib/pleroma/web/mastodon_api/subscription_controller.ex b/lib/pleroma/web/mastodon_api/subscription_controller.ex
index b6c8ff808..255ee2f18 100644
--- a/lib/pleroma/web/mastodon_api/subscription_controller.ex
+++ b/lib/pleroma/web/mastodon_api/subscription_controller.ex
@@ -59,13 +59,13 @@ def delete(%{assigns: %{user: user, token: token}} = conn, _params) do
#
def errors(conn, {:error, :not_found}) do
conn
- |> put_status(404)
- |> json("Not found")
+ |> put_status(:not_found)
+ |> json(dgettext("errors", "Not found"))
end
def errors(conn, _) do
conn
- |> put_status(500)
- |> json("Something went wrong")
+ |> put_status(:internal_server_error)
+ |> json(dgettext("errors", "Something went wrong"))
end
end
diff --git a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex
index 489d5d3a5..b786a521b 100644
--- a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex
+++ b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex
@@ -29,7 +29,7 @@ def check_password(conn, %{"user" => username, "pass" => password}) do
else
false ->
conn
- |> put_status(403)
+ |> put_status(:forbidden)
|> json(false)
_ ->
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
index 869dda5c5..cd9a4f4a8 100644
--- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
+++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
@@ -201,8 +201,6 @@ def nodeinfo(conn, %{"version" => "2.1"}) do
end
def nodeinfo(conn, _) do
- conn
- |> put_status(404)
- |> json(%{error: "Nodeinfo schema version not handled"})
+ render_error(conn, :not_found, "Nodeinfo schema version not handled")
end
end
diff --git a/lib/pleroma/web/oauth/fallback_controller.ex b/lib/pleroma/web/oauth/fallback_controller.ex
index e3984f009..dd7f08bf1 100644
--- a/lib/pleroma/web/oauth/fallback_controller.ex
+++ b/lib/pleroma/web/oauth/fallback_controller.ex
@@ -9,21 +9,24 @@ defmodule Pleroma.Web.OAuth.FallbackController do
def call(conn, {:register, :generic_error}) do
conn
|> put_status(:internal_server_error)
- |> put_flash(:error, "Unknown error, please check the details and try again.")
+ |> put_flash(
+ :error,
+ dgettext("errors", "Unknown error, please check the details and try again.")
+ )
|> OAuthController.registration_details(conn.params)
end
def call(conn, {:register, _error}) do
conn
|> put_status(:unauthorized)
- |> put_flash(:error, "Invalid Username/Password")
+ |> put_flash(:error, dgettext("errors", "Invalid Username/Password"))
|> OAuthController.registration_details(conn.params)
end
def call(conn, _error) do
conn
|> put_status(:unauthorized)
- |> put_flash(:error, "Invalid Username/Password")
+ |> put_flash(:error, dgettext("errors", "Invalid Username/Password"))
|> OAuthController.authorize(conn.params)
end
end
diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex
index 3f8e3b074..ef53b7ae3 100644
--- a/lib/pleroma/web/oauth/oauth_controller.ex
+++ b/lib/pleroma/web/oauth/oauth_controller.ex
@@ -90,7 +90,7 @@ defp handle_existing_authorization(
redirect(conn, external: url)
else
conn
- |> put_flash(:error, "Unlisted redirect_uri.")
+ |> put_flash(:error, dgettext("errors", "Unlisted redirect_uri."))
|> redirect(external: redirect_uri(conn, redirect_uri))
end
end
@@ -128,7 +128,7 @@ def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{
redirect(conn, external: url)
else
conn
- |> put_flash(:error, "Unlisted redirect_uri.")
+ |> put_flash(:error, dgettext("errors", "Unlisted redirect_uri."))
|> redirect(external: redirect_uri(conn, redirect_uri))
end
end
@@ -142,7 +142,7 @@ defp handle_create_authorization_error(
# Per https://github.com/tootsuite/mastodon/blob/
# 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L39
conn
- |> put_flash(:error, "This action is outside the authorized scopes")
+ |> put_flash(:error, dgettext("errors", "This action is outside the authorized scopes"))
|> put_status(:unauthorized)
|> authorize(params)
end
@@ -155,7 +155,7 @@ defp handle_create_authorization_error(
# Per https://github.com/tootsuite/mastodon/blob/
# 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76
conn
- |> put_flash(:error, "Your login is missing a confirmed e-mail address")
+ |> put_flash(:error, dgettext("errors", "Your login is missing a confirmed e-mail address"))
|> put_status(:forbidden)
|> authorize(params)
end
@@ -176,9 +176,7 @@ def token_exchange(
json(conn, Token.Response.build(user, token, response_attrs))
else
- _error ->
- put_status(conn, 400)
- |> json(%{error: "Invalid credentials"})
+ _error -> render_invalid_credentials_error(conn)
end
end
@@ -192,9 +190,7 @@ def token_exchange(%Plug.Conn{} = conn, %{"grant_type" => "authorization_code"}
json(conn, Token.Response.build(user, token, response_attrs))
else
- _error ->
- put_status(conn, 400)
- |> json(%{error: "Invalid credentials"})
+ _error -> render_invalid_credentials_error(conn)
end
end
@@ -214,18 +210,13 @@ def token_exchange(
{:auth_active, false} ->
# Per https://github.com/tootsuite/mastodon/blob/
# 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76
- conn
- |> put_status(:forbidden)
- |> json(%{error: "Your login is missing a confirmed e-mail address"})
+ render_error(conn, :forbidden, "Your login is missing a confirmed e-mail address")
{:user_active, false} ->
- conn
- |> put_status(:forbidden)
- |> json(%{error: "Your account is currently disabled"})
+ render_error(conn, :forbidden, "Your account is currently disabled")
_error ->
- put_status(conn, 400)
- |> json(%{error: "Invalid credentials"})
+ render_invalid_credentials_error(conn)
end
end
@@ -247,9 +238,7 @@ def token_exchange(%Plug.Conn{} = conn, %{"grant_type" => "client_credentials"}
{:ok, token} <- Token.exchange_token(app, auth) do
json(conn, Token.Response.build_for_client_credentials(token))
else
- _error ->
- put_status(conn, 400)
- |> json(%{error: "Invalid credentials"})
+ _error -> render_invalid_credentials_error(conn)
end
end
@@ -271,9 +260,7 @@ def token_revoke(%Plug.Conn{} = conn, params), do: bad_request(conn, params)
# Response for bad request
defp bad_request(%Plug.Conn{} = conn, _) do
- conn
- |> put_status(500)
- |> json(%{error: "Bad request"})
+ render_error(conn, :internal_server_error, "Bad request")
end
@doc "Prepares OAuth request to provider for Ueberauth"
@@ -304,9 +291,11 @@ def prepare_request(%Plug.Conn{} = conn, %{
def request(%Plug.Conn{} = conn, params) do
message =
if params["provider"] do
- "Unsupported OAuth provider: #{params["provider"]}."
+ dgettext("errors", "Unsupported OAuth provider: %{provider}.",
+ provider: params["provider"]
+ )
else
- "Bad OAuth request."
+ dgettext("errors", "Bad OAuth request.")
end
conn
@@ -320,7 +309,10 @@ def callback(%Plug.Conn{assigns: %{ueberauth_failure: failure}} = conn, params)
message = Enum.join(messages, "; ")
conn
- |> put_flash(:error, "Failed to authenticate: #{message}.")
+ |> put_flash(
+ :error,
+ dgettext("errors", "Failed to authenticate: %{message}.", message: message)
+ )
|> redirect(external: redirect_uri(conn, params["redirect_uri"]))
end
@@ -350,7 +342,7 @@ def callback(%Plug.Conn{} = conn, params) do
Logger.debug(inspect(["OAUTH_ERROR", error, conn.assigns]))
conn
- |> put_flash(:error, "Failed to set up user account.")
+ |> put_flash(:error, dgettext("errors", "Failed to set up user account."))
|> redirect(external: redirect_uri(conn, params["redirect_uri"]))
end
end
@@ -468,4 +460,8 @@ def default_redirect_uri(%App{} = app) do
|> String.split()
|> Enum.at(0)
end
+
+ defp render_invalid_credentials_error(conn) do
+ render_error(conn, :bad_request, "Invalid credentials")
+ end
end
diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex
index 2fb6ce41b..372d52899 100644
--- a/lib/pleroma/web/ostatus/ostatus_controller.ex
+++ b/lib/pleroma/web/ostatus/ostatus_controller.ex
@@ -245,14 +245,10 @@ defp represent_activity(conn, _, activity, user) do
end
def errors(conn, {:error, :not_found}) do
- conn
- |> put_status(404)
- |> text("Not found")
+ render_error(conn, :not_found, "Not found")
end
def errors(conn, _) do
- conn
- |> put_status(500)
- |> text("Something went wrong")
+ render_error(conn, :internal_server_error, "Something went wrong")
end
end
diff --git a/lib/pleroma/web/uploader_controller.ex b/lib/pleroma/web/uploader_controller.ex
index d11e8e63e..bf09775e6 100644
--- a/lib/pleroma/web/uploader_controller.ex
+++ b/lib/pleroma/web/uploader_controller.ex
@@ -12,7 +12,7 @@ def callback(conn, %{"upload_path" => upload_path} = params) do
end
def callbacks(conn, _) do
- send_resp(conn, 400, "bad request")
+ render_error(conn, :bad_request, "bad request")
end
defp process_callback(conn, pid, params) when is_pid(pid) do
@@ -24,6 +24,6 @@ defp process_callback(conn, pid, params) when is_pid(pid) do
end
defp process_callback(conn, _, _) do
- send_resp(conn, 400, "bad request")
+ render_error(conn, :bad_request, "bad request")
end
end
diff --git a/priv/gettext/en/LC_MESSAGES/errors.po b/priv/gettext/en/LC_MESSAGES/errors.po
index 2211c98e3..25a2f73e4 100644
--- a/priv/gettext/en/LC_MESSAGES/errors.po
+++ b/priv/gettext/en/LC_MESSAGES/errors.po
@@ -91,3 +91,375 @@ msgstr ""
msgid "must be equal to %{number}"
msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:381
+msgid "Account not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:153
+msgid "Already voted"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:263
+msgid "Bad request"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:254
+msgid "Can't delete object"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:569
+msgid "Can't delete this post"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1731
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1737
+msgid "Can't display this activity"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:195
+msgid "Can't find user"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1148
+msgid "Can't get favorites"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:263
+msgid "Can't like object"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:518
+msgid "Cannot post an empty status without attachments"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:461
+msgid "Comment must be up to %{max_size} characters"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/config.ex:63
+msgid "Config with params %{params} not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:78
+msgid "Could not delete"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:110
+msgid "Could not favorite"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:310
+msgid "Could not pin"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:89
+msgid "Could not repeat"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:120
+msgid "Could not unfavorite"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:327
+msgid "Could not unpin"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:99
+msgid "Could not unrepeat"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:392
+msgid "Could not update state"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1271
+msgid "Error."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/kocaptcha.ex:36
+msgid "Invalid CAPTCHA"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1700
+#: lib/pleroma/web/oauth/oauth_controller.ex:465
+msgid "Invalid credentials"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/ensure_authenticated_plug.ex:20
+msgid "Invalid credentials."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:154
+msgid "Invalid indices"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:411
+msgid "Invalid parameters"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:377
+msgid "Invalid password."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:163
+msgid "Invalid request"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/kocaptcha.ex:16
+msgid "Kocaptcha service unavailable"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1696
+msgid "Missing parameters"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:496
+msgid "No such conversation"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:163
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:206
+msgid "No such permission_group"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:69
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:311
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:399
+#: lib/pleroma/web/mastodon_api/subscription_controller.ex:63
+#: lib/pleroma/web/ostatus/ostatus_controller.ex:248
+msgid "Not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:152
+msgid "Poll's author can't vote"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:443
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:444
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:473
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:476
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1180
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1564
+msgid "Record not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:417
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1570
+#: lib/pleroma/web/mastodon_api/subscription_controller.ex:69
+#: lib/pleroma/web/ostatus/ostatus_controller.ex:252
+msgid "Something went wrong"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:253
+msgid "The message visibility must be direct"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:521
+msgid "The status is over the character limit"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:27
+msgid "This resource requires authentication."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/rate_limiter.ex:89
+msgid "Throttled"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:155
+msgid "Too many choices"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:268
+msgid "Unhandled activity type"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/user_is_admin_plug.ex:20
+msgid "User is not admin."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:380
+msgid "Valid `account_id` required"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:185
+msgid "You can't revoke your own admin status."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:216
+msgid "Your account is currently disabled"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:158
+#: lib/pleroma/web/oauth/oauth_controller.ex:213
+msgid "Your login is missing a confirmed e-mail address"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:221
+msgid "can't read inbox of %{nickname} as %{as_nickname}"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:297
+msgid "can't update outbox of %{nickname} as %{as_nickname}"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:335
+msgid "conversation is already muted"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:192
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:317
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1196
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1247
+msgid "error"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:789
+msgid "mascots can only be images"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:34
+msgid "not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:298
+msgid "Bad OAuth request."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:92
+msgid "CAPTCHA already used"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:89
+msgid "CAPTCHA expired"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:50
+msgid "Failed"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:314
+msgid "Failed to authenticate: %{message}."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:345
+msgid "Failed to set up user account."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/oauth_scopes_plug.ex:37
+msgid "Insufficient permissions: %{permissions}."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:89
+msgid "Internal Error"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/fallback_controller.ex:22
+#: lib/pleroma/web/oauth/fallback_controller.ex:29
+msgid "Invalid Username/Password"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:107
+msgid "Invalid answer data"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:204
+msgid "Nodeinfo schema version not handled"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:145
+msgid "This action is outside the authorized scopes"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/fallback_controller.ex:14
+msgid "Unknown error, please check the details and try again."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:93
+#: lib/pleroma/web/oauth/oauth_controller.ex:131
+msgid "Unlisted redirect_uri."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:294
+msgid "Unsupported OAuth provider: %{provider}."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/uploaders/uploader.ex:71
+msgid "Uploader callback timeout"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/uploader_controller.ex:11
+#: lib/pleroma/web/uploader_controller.ex:23
+msgid "bad request"
+msgstr ""
diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot
index a964f84ec..2fd9c42e3 100644
--- a/priv/gettext/errors.pot
+++ b/priv/gettext/errors.pot
@@ -7,7 +7,6 @@
## Run `mix gettext.extract` to bring this file up to
## date. Leave `msgstr`s empty as changing them here as no
## effect: edit them in PO (`.po`) files instead.
-
## From Ecto.Changeset.cast/4
msgid "can't be blank"
msgstr ""
@@ -89,3 +88,375 @@ msgstr ""
msgid "must be equal to %{number}"
msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:381
+msgid "Account not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:153
+msgid "Already voted"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:263
+msgid "Bad request"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:254
+msgid "Can't delete object"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:569
+msgid "Can't delete this post"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1731
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1737
+msgid "Can't display this activity"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:195
+msgid "Can't find user"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1148
+msgid "Can't get favorites"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:263
+msgid "Can't like object"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:518
+msgid "Cannot post an empty status without attachments"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:461
+msgid "Comment must be up to %{max_size} characters"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/config.ex:63
+msgid "Config with params %{params} not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:78
+msgid "Could not delete"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:110
+msgid "Could not favorite"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:310
+msgid "Could not pin"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:89
+msgid "Could not repeat"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:120
+msgid "Could not unfavorite"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:327
+msgid "Could not unpin"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:99
+msgid "Could not unrepeat"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:392
+msgid "Could not update state"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1271
+msgid "Error."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/kocaptcha.ex:36
+msgid "Invalid CAPTCHA"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1700
+#: lib/pleroma/web/oauth/oauth_controller.ex:465
+msgid "Invalid credentials"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/ensure_authenticated_plug.ex:20
+msgid "Invalid credentials."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:154
+msgid "Invalid indices"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:411
+msgid "Invalid parameters"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:377
+msgid "Invalid password."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:163
+msgid "Invalid request"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/kocaptcha.ex:16
+msgid "Kocaptcha service unavailable"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1696
+msgid "Missing parameters"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:496
+msgid "No such conversation"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:163
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:206
+msgid "No such permission_group"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:69
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:311
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:399
+#: lib/pleroma/web/mastodon_api/subscription_controller.ex:63
+#: lib/pleroma/web/ostatus/ostatus_controller.ex:248
+msgid "Not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:152
+msgid "Poll's author can't vote"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:443
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:444
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:473
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:476
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1180
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1564
+msgid "Record not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:417
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1570
+#: lib/pleroma/web/mastodon_api/subscription_controller.ex:69
+#: lib/pleroma/web/ostatus/ostatus_controller.ex:252
+msgid "Something went wrong"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:253
+msgid "The message visibility must be direct"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:521
+msgid "The status is over the character limit"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:27
+msgid "This resource requires authentication."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/rate_limiter.ex:89
+msgid "Throttled"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:155
+msgid "Too many choices"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:268
+msgid "Unhandled activity type"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/user_is_admin_plug.ex:20
+msgid "User is not admin."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:380
+msgid "Valid `account_id` required"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:185
+msgid "You can't revoke your own admin status."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:216
+msgid "Your account is currently disabled"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:158
+#: lib/pleroma/web/oauth/oauth_controller.ex:213
+msgid "Your login is missing a confirmed e-mail address"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:221
+msgid "can't read inbox of %{nickname} as %{as_nickname}"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:297
+msgid "can't update outbox of %{nickname} as %{as_nickname}"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:335
+msgid "conversation is already muted"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:192
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:317
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1196
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1247
+msgid "error"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:789
+msgid "mascots can only be images"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:34
+msgid "not found"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:298
+msgid "Bad OAuth request."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:92
+msgid "CAPTCHA already used"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:89
+msgid "CAPTCHA expired"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:50
+msgid "Failed"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:314
+msgid "Failed to authenticate: %{message}."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:345
+msgid "Failed to set up user account."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/oauth_scopes_plug.ex:37
+msgid "Insufficient permissions: %{permissions}."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:89
+msgid "Internal Error"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/fallback_controller.ex:22
+#: lib/pleroma/web/oauth/fallback_controller.ex:29
+msgid "Invalid Username/Password"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:107
+msgid "Invalid answer data"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:204
+msgid "Nodeinfo schema version not handled"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:145
+msgid "This action is outside the authorized scopes"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/fallback_controller.ex:14
+msgid "Unknown error, please check the details and try again."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:93
+#: lib/pleroma/web/oauth/oauth_controller.ex:131
+msgid "Unlisted redirect_uri."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:294
+msgid "Unsupported OAuth provider: %{provider}."
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/uploaders/uploader.ex:71
+msgid "Uploader callback timeout"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/uploader_controller.ex:11
+#: lib/pleroma/web/uploader_controller.ex:23
+msgid "bad request"
+msgstr ""
From c2a589d9a3f9b4475661053175f0ff4b8bebc41f Mon Sep 17 00:00:00 2001
From: Egor Kislitsyn
Date: Wed, 10 Jul 2019 16:28:24 +0700
Subject: [PATCH 17/31] Fix credo warning
---
test/plugs/set_locale_plug_test.exs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/plugs/set_locale_plug_test.exs b/test/plugs/set_locale_plug_test.exs
index 3e31b0ae7..b6c4c1cea 100644
--- a/test/plugs/set_locale_plug_test.exs
+++ b/test/plugs/set_locale_plug_test.exs
@@ -6,8 +6,8 @@ defmodule Pleroma.Plugs.SetLocalePlugTest do
use ExUnit.Case, async: true
use Plug.Test
- alias Plug.Conn
alias Pleroma.Plugs.SetLocalePlug
+ alias Plug.Conn
test "default locale is `en`" do
conn =
From a42da8f31159958da3cb46aaa9fa21bc0763c0ed Mon Sep 17 00:00:00 2001
From: Egor Kislitsyn
Date: Wed, 10 Jul 2019 17:40:34 +0700
Subject: [PATCH 18/31] Fix response
---
lib/pleroma/plugs/uploaded_media.ex | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/pleroma/plugs/uploaded_media.ex b/lib/pleroma/plugs/uploaded_media.ex
index be2c17c5f..6da9bdafc 100644
--- a/lib/pleroma/plugs/uploaded_media.ex
+++ b/lib/pleroma/plugs/uploaded_media.ex
@@ -66,7 +66,7 @@ defp get_media(conn, {:static_dir, directory}, _, opts) do
conn
else
conn
- |> render_error(:not_found, "Not found")
+ |> send_resp(:not_found, dgettext("errors", "Not found"))
|> halt()
end
end
@@ -86,7 +86,7 @@ defp get_media(conn, unknown, _, _) do
Logger.error("#{__MODULE__}: Unknown get startegy: #{inspect(unknown)}")
conn
- |> render_error(:internal_server_error, "Internal Error")
+ |> send_resp(:internal_server_error, dgettext("errors", "Internal Error"))
|> halt()
end
end
From 406efb36887a97f9f9b22bcb26bb0a07af370c0c Mon Sep 17 00:00:00 2001
From: Egor Kislitsyn
Date: Wed, 10 Jul 2019 17:58:15 +0700
Subject: [PATCH 19/31] Add Russian translation
---
priv/gettext/ru/LC_MESSAGES/errors.po | 463 ++++++++++++++++++++++++++
1 file changed, 463 insertions(+)
create mode 100644 priv/gettext/ru/LC_MESSAGES/errors.po
diff --git a/priv/gettext/ru/LC_MESSAGES/errors.po b/priv/gettext/ru/LC_MESSAGES/errors.po
new file mode 100644
index 000000000..39f83e8a6
--- /dev/null
+++ b/priv/gettext/ru/LC_MESSAGES/errors.po
@@ -0,0 +1,463 @@
+## `msgid`s in this file come from POT (.pot) files.
+##
+## Do not add, change, or remove `msgid`s manually here as
+## they're tied to the ones in the corresponding POT file
+## (with the same domain).
+##
+## Use `mix gettext.extract --merge` or `mix gettext.merge`
+## to merge POT files into PO files.
+msgid ""
+msgstr ""
+"Language: ru\n"
+"Plural-Forms: nplurals=3\n"
+
+msgid "can't be blank"
+msgstr "не может быть пустым"
+
+msgid "has already been taken"
+msgstr "уже занято"
+
+msgid "is invalid"
+msgstr "неверный"
+
+msgid "has invalid format"
+msgstr "неверный формат"
+
+msgid "has an invalid entry"
+msgstr "содержит неверную запись"
+
+msgid "is reserved"
+msgstr "занято"
+
+msgid "does not match confirmation"
+msgstr "не совпадает"
+
+msgid "is still associated with this entry"
+msgstr "по прежнему связан с этой записью"
+
+msgid "are still associated with this entry"
+msgstr "по прежнему связаны с этой записью"
+
+msgid "should be %{count} character(s)"
+msgid_plural "should be %{count} character(s)"
+msgstr[0] "должен состоять из %{count} символа"
+msgstr[1] "должен состоять из %{count} символов"
+msgstr[2] "должен состоять из %{count} символов"
+
+
+msgid "should have %{count} item(s)"
+msgid_plural "should have %{count} item(s)"
+msgstr[0] "должен содержать %{count} элемент"
+msgstr[1] "должен содержать %{count} элемента"
+msgstr[2] "должен содержать %{count} элементов"
+
+msgid "should be at least %{count} character(s)"
+msgid_plural "should be at least %{count} character(s)"
+msgstr[0] "должен быть не менее чем %{count} символа"
+msgstr[1] "должен быть не менее чем %{count} символов"
+msgstr[2] "должен быть не менее чем %{count} символов"
+
+msgid "should have at least %{count} item(s)"
+msgid_plural "should have at least %{count} item(s)"
+msgstr[0] "должен быть не менее %{count} элемента"
+msgstr[1] "должен быть не менее %{count} элементов"
+msgstr[2] "должен быть не менее %{count} элементов"
+
+msgid "should be at most %{count} character(s)"
+msgid_plural "should be at most %{count} character(s)"
+msgstr[0] "должен быть не более %{count} символа"
+msgstr[1] "должен быть не более %{count} символов"
+msgstr[2] "должен быть не более %{count} символов"
+
+msgid "should have at most %{count} item(s)"
+msgid_plural "should have at most %{count} item(s)"
+msgstr[0] "должен содержать не менее %{count} элемента"
+msgstr[1] "должен содержать не менее %{count} элемента"
+msgstr[2] "должен содержать не менее %{count} элементов"
+
+msgid "must be less than %{number}"
+msgstr "должен быть меньше %{number}"
+
+msgid "must be greater than %{number}"
+msgstr "должен быть больше %{number}"
+
+msgid "must be less than or equal to %{number}"
+msgstr "должен быть меньше или равен %{number}"
+
+msgid "must be greater than or equal to %{number}"
+msgstr "должен быть больше или равен %{number}"
+
+msgid "must be equal to %{number}"
+msgstr "должен быть равным %{number}"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:381
+msgid "Account not found"
+msgstr "Учетная запись не найдена"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:153
+msgid "Already voted"
+msgstr "Уже проголосовал(а)"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:263
+msgid "Bad request"
+msgstr "Неверный запрос"
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:254
+msgid "Can't delete object"
+msgstr "Произошла ошибка при удалении объекта"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:569
+msgid "Can't delete this post"
+msgstr "Произошла ошибка при удалении этой записи"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1731
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1737
+msgid "Can't display this activity"
+msgstr "Произошла ошибка при показе этой записи"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:195
+msgid "Can't find user"
+msgstr "Пользователь не найден"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1148
+msgid "Can't get favorites"
+msgstr "Не в состоянии получить избранное"
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:263
+msgid "Can't like object"
+msgstr "Не могу поставить лайк"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:518
+msgid "Cannot post an empty status without attachments"
+msgstr "Нельзя отправить пустой статус без приложений"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:461
+msgid "Comment must be up to %{max_size} characters"
+msgstr "Комментарий должен быть не более %{max_size} символов"
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/config.ex:63
+msgid "Config with params %{params} not found"
+msgstr "Параметры конфигурации %{params} не найдены"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:78
+msgid "Could not delete"
+msgstr "Не в силах удалить"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:110
+msgid "Could not favorite"
+msgstr "Не в силах добавить в избранное"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:310
+msgid "Could not pin"
+msgstr "Не в силах прикрепить"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:89
+msgid "Could not repeat"
+msgstr "Не в силах повторить"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:120
+msgid "Could not unfavorite"
+msgstr "Не в силах удалить из избранного"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:327
+msgid "Could not unpin"
+msgstr "Не в силах открепить"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:99
+msgid "Could not unrepeat"
+msgstr "Не в силах отменить повтор"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:392
+msgid "Could not update state"
+msgstr "Не в силах обновить состояние"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1271
+msgid "Error."
+msgstr "Ошибка"
+
+#, elixir-format
+#: lib/pleroma/captcha/kocaptcha.ex:36
+msgid "Invalid CAPTCHA"
+msgstr "Неверная CAPTCHA"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1700
+#: lib/pleroma/web/oauth/oauth_controller.ex:465
+msgid "Invalid credentials"
+msgstr "Неверные учетные данные"
+
+#, elixir-format
+#: lib/pleroma/plugs/ensure_authenticated_plug.ex:20
+msgid "Invalid credentials."
+msgstr "Неверные учетные данные"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:154
+msgid "Invalid indices"
+msgstr "Неверные индексы"
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:411
+msgid "Invalid parameters"
+msgstr "Неверны параметры"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:377
+msgid "Invalid password."
+msgstr "Неверный пароль"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:163
+msgid "Invalid request"
+msgstr "Неверный запрос"
+
+#, elixir-format
+#: lib/pleroma/captcha/kocaptcha.ex:16
+msgid "Kocaptcha service unavailable"
+msgstr "Kocaptcha недоступен"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1696
+msgid "Missing parameters"
+msgstr "Не хватает параметров"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:496
+msgid "No such conversation"
+msgstr "Разговор не найден"
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:163
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:206
+msgid "No such permission_group"
+msgstr "Такой группы полномочий не существует"
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:69
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:311
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:399
+#: lib/pleroma/web/mastodon_api/subscription_controller.ex:63
+#: lib/pleroma/web/ostatus/ostatus_controller.ex:248
+msgid "Not found"
+msgstr "Не найден"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:152
+msgid "Poll's author can't vote"
+msgstr "Автор опроса не может голосовать"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:443
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:444
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:473
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:476
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1180
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1564
+msgid "Record not found"
+msgstr "Запись не найдена"
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:417
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1570
+#: lib/pleroma/web/mastodon_api/subscription_controller.ex:69
+#: lib/pleroma/web/ostatus/ostatus_controller.ex:252
+msgid "Something went wrong"
+msgstr "Что-то пошло не так"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:253
+msgid "The message visibility must be direct"
+msgstr "Видимость у сообщения должна быть `Личное`"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/utils.ex:521
+msgid "The status is over the character limit"
+msgstr "Превышена длина статуса"
+
+#, elixir-format
+#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:27
+msgid "This resource requires authentication."
+msgstr "Для этого ресурса требуется аутентификация"
+
+#, elixir-format
+#: lib/pleroma/plugs/rate_limiter.ex:89
+msgid "Throttled"
+msgstr "Ограничено. Превышен лимит запросов."
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:155
+msgid "Too many choices"
+msgstr "Слишком много ответов"
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:268
+msgid "Unhandled activity type"
+msgstr "Неизвестный тип activity"
+
+#, elixir-format
+#: lib/pleroma/plugs/user_is_admin_plug.ex:20
+msgid "User is not admin."
+msgstr "Пользователь не обладает правами администратора"
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:380
+msgid "Valid `account_id` required"
+msgstr "Требуется корректный `account_id`"
+
+#, elixir-format
+#: lib/pleroma/web/admin_api/admin_api_controller.ex:185
+msgid "You can't revoke your own admin status."
+msgstr "Вы не можете отозвать статус администратора у вашей учетной записи"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:216
+msgid "Your account is currently disabled"
+msgstr "Ваша учетная запись отключена"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:158
+#: lib/pleroma/web/oauth/oauth_controller.ex:213
+msgid "Your login is missing a confirmed e-mail address"
+msgstr "Ваш e-mail адрес не подтвержден"
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:221
+msgid "can't read inbox of %{nickname} as %{as_nickname}"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:297
+msgid "can't update outbox of %{nickname} as %{as_nickname}"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/common_api/common_api.ex:335
+msgid "conversation is already muted"
+msgstr "разговор уже игнорируется"
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:192
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:317
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1196
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:1247
+msgid "error"
+msgstr "ошибка"
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/mastodon_api_controller.ex:789
+msgid "mascots can only be images"
+msgstr "маскоты должны быть картинками"
+
+#, elixir-format
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:34
+msgid "not found"
+msgstr "не найдено"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:298
+msgid "Bad OAuth request."
+msgstr "Неверный OAuth запрос"
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:92
+msgid "CAPTCHA already used"
+msgstr "CAPTCHA уже использована"
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:89
+msgid "CAPTCHA expired"
+msgstr "CAPTCHA устарела"
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:50
+msgid "Failed"
+msgstr "Ошибка"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:314
+msgid "Failed to authenticate: %{message}."
+msgstr "Ошибка при входе: %{message}"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:345
+msgid "Failed to set up user account."
+msgstr "Ошибка при создании учетной записи"
+
+#, elixir-format
+#: lib/pleroma/plugs/oauth_scopes_plug.ex:37
+msgid "Insufficient permissions: %{permissions}."
+msgstr "Недостаточно полномочий: %{permissions}"
+
+#, elixir-format
+#: lib/pleroma/plugs/uploaded_media.ex:89
+msgid "Internal Error"
+msgstr "Внутренняя ошибка"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/fallback_controller.ex:22
+#: lib/pleroma/web/oauth/fallback_controller.ex:29
+msgid "Invalid Username/Password"
+msgstr "Неверное имя пользователя или пароль"
+
+#, elixir-format
+#: lib/pleroma/captcha/captcha.ex:107
+msgid "Invalid answer data"
+msgstr "Неверный ответ"
+
+#, elixir-format
+#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:204
+msgid "Nodeinfo schema version not handled"
+msgstr "Версия схемы Nodeinfo не учитывается"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:145
+msgid "This action is outside the authorized scopes"
+msgstr "Это действие выходит за рамки доступных полномочий"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/fallback_controller.ex:14
+msgid "Unknown error, please check the details and try again."
+msgstr "Неизвестная ошибка. Пожалуйста, проверьте данные и попробуйте снова."
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:93
+#: lib/pleroma/web/oauth/oauth_controller.ex:131
+msgid "Unlisted redirect_uri."
+msgstr "Неизвестный redirect_uri"
+
+#, elixir-format
+#: lib/pleroma/web/oauth/oauth_controller.ex:294
+msgid "Unsupported OAuth provider: %{provider}."
+msgstr "Неизвестный OAuth провайдер: %{provider}"
+
+#, elixir-format
+#: lib/pleroma/uploaders/uploader.ex:71
+msgid "Uploader callback timeout"
+msgstr "Тайм-аут при загрузке"
+
+#, elixir-format
+#: lib/pleroma/web/uploader_controller.ex:11
+#: lib/pleroma/web/uploader_controller.ex:23
+msgid "bad request"
+msgstr "неправильный запрос"
From ed8ce21a224b7b8d7753f2c4f66bfdfd3d9a0e68 Mon Sep 17 00:00:00 2001
From: Egor Kislitsyn
Date: Wed, 10 Jul 2019 18:00:22 +0700
Subject: [PATCH 20/31] Fix unused import warning
---
lib/pleroma/plugs/uploaded_media.ex | 1 -
1 file changed, 1 deletion(-)
diff --git a/lib/pleroma/plugs/uploaded_media.ex b/lib/pleroma/plugs/uploaded_media.ex
index 6da9bdafc..69c1ab942 100644
--- a/lib/pleroma/plugs/uploaded_media.ex
+++ b/lib/pleroma/plugs/uploaded_media.ex
@@ -8,7 +8,6 @@ defmodule Pleroma.Plugs.UploadedMedia do
import Plug.Conn
import Pleroma.Web.Gettext
- import Pleroma.Web.TranslationHelpers
require Logger
@behaviour Plug
From ff55e3c16fa5764b37ca1ec85c26e819d07f0242 Mon Sep 17 00:00:00 2001
From: Sergey Suprunenko
Date: Wed, 10 Jul 2019 13:29:50 +0000
Subject: [PATCH 21/31] Create mentions only for explicitly mentioned users
---
.../web/mastodon_api/views/status_view.ex | 8 ++-
test/support/factory.ex | 2 +
.../admin_api/admin_api_controller_test.exs | 1 -
test/web/mastodon_api/status_view_test.exs | 67 ++++++++++++++++++-
4 files changed, 73 insertions(+), 5 deletions(-)
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index a070bc942..06a7251d8 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -149,8 +149,14 @@ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity
tags = object.data["tag"] || []
sensitive = object.data["sensitive"] || Enum.member?(tags, "nsfw")
+ tag_mentions =
+ tags
+ |> Enum.filter(fn tag -> is_map(tag) and tag["type"] == "Mention" end)
+ |> Enum.map(fn tag -> tag["href"] end)
+
mentions =
- activity.recipients
+ (object.data["to"] ++ tag_mentions)
+ |> Enum.uniq()
|> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end)
|> Enum.filter(& &1)
|> Enum.map(fn user -> AccountView.render("mention.json", %{user: user}) end)
diff --git a/test/support/factory.ex b/test/support/factory.ex
index 0e3c900c9..a9f750eec 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -117,6 +117,7 @@ def direct_note_activity_factory do
def note_activity_factory(attrs \\ %{}) do
user = attrs[:user] || insert(:user)
note = attrs[:note] || insert(:note, user: user)
+ attrs = Map.drop(attrs, [:user, :note])
data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
@@ -133,6 +134,7 @@ def note_activity_factory(attrs \\ %{}) do
actor: data["actor"],
recipients: data["to"]
}
+ |> Map.merge(attrs)
end
def article_activity_factory do
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs
index 4ea33a6cc..0e04e7e94 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/admin_api_controller_test.exs
@@ -1234,7 +1234,6 @@ test "returns created dm", %{conn: conn} do
recipients = Enum.map(response["mentions"], & &1["username"])
- assert conn.assigns[:user].nickname in recipients
assert reporter.nickname in recipients
assert response["content"] == "I will check it out"
assert response["visibility"] == "direct"
diff --git a/test/web/mastodon_api/status_view_test.exs b/test/web/mastodon_api/status_view_test.exs
index 49b4c529f..ac42819d8 100644
--- a/test/web/mastodon_api/status_view_test.exs
+++ b/test/web/mastodon_api/status_view_test.exs
@@ -203,10 +203,71 @@ test "contains mentions" do
status = StatusView.render("status.json", %{activity: activity})
- actor = User.get_cached_by_ap_id(activity.actor)
-
assert status.mentions ==
- Enum.map([user, actor], fn u -> AccountView.render("mention.json", %{user: u}) end)
+ Enum.map([user], fn u -> AccountView.render("mention.json", %{user: u}) end)
+ end
+
+ test "create mentions from the 'to' field" do
+ %User{ap_id: recipient_ap_id} = insert(:user)
+ cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
+
+ object =
+ insert(:note, %{
+ data: %{
+ "to" => [recipient_ap_id],
+ "cc" => cc
+ }
+ })
+
+ activity =
+ insert(:note_activity, %{
+ note: object,
+ recipients: [recipient_ap_id | cc]
+ })
+
+ assert length(activity.recipients) == 3
+
+ %{mentions: [mention] = mentions} = StatusView.render("status.json", %{activity: activity})
+
+ assert length(mentions) == 1
+ assert mention.url == recipient_ap_id
+ end
+
+ test "create mentions from the 'tag' field" do
+ recipient = insert(:user)
+ cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
+
+ object =
+ insert(:note, %{
+ data: %{
+ "cc" => cc,
+ "tag" => [
+ %{
+ "href" => recipient.ap_id,
+ "name" => recipient.nickname,
+ "type" => "Mention"
+ },
+ %{
+ "href" => "https://example.com/search?tag=test",
+ "name" => "#test",
+ "type" => "Hashtag"
+ }
+ ]
+ }
+ })
+
+ activity =
+ insert(:note_activity, %{
+ note: object,
+ recipients: [recipient.ap_id | cc]
+ })
+
+ assert length(activity.recipients) == 3
+
+ %{mentions: [mention] = mentions} = StatusView.render("status.json", %{activity: activity})
+
+ assert length(mentions) == 1
+ assert mention.url == recipient.ap_id
end
test "attachments" do
From f8786fa6f27b1934b48b69fce5d285ebddefda92 Mon Sep 17 00:00:00 2001
From: Alex S
Date: Wed, 10 Jul 2019 16:01:32 +0300
Subject: [PATCH 22/31] adding following_address field to user
---
lib/pleroma/user.ex | 30 ++++++++++++++-----
lib/pleroma/web/activity_pub/activity_pub.ex | 1 +
...10115833_add_following_address_to_user.exs | 9 ++++++
...51_add_following_address_index_to_user.exs | 8 +++++
test/web/activity_pub/transmogrifier_test.exs | 1 +
5 files changed, 41 insertions(+), 8 deletions(-)
create mode 100644 priv/repo/migrations/20190710115833_add_following_address_to_user.exs
create mode 100644 priv/repo/migrations/20190710125051_add_following_address_index_to_user.exs
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 034c414bf..81efb4f13 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -52,6 +52,7 @@ defmodule Pleroma.User do
field(:avatar, :map)
field(:local, :boolean, default: true)
field(:follower_address, :string)
+ field(:following_address, :string)
field(:search_rank, :float, virtual: true)
field(:search_type, :integer, virtual: true)
field(:tags, {:array, :string}, default: [])
@@ -162,9 +163,10 @@ def remote_user_creation(params) do
if changes.valid? do
case info_cng.changes[:source_data] do
- %{"followers" => followers} ->
+ %{"followers" => followers, "following" => following} ->
changes
|> put_change(:follower_address, followers)
+ |> put_change(:following_address, following)
_ ->
followers = User.ap_followers(%User{nickname: changes.changes[:nickname]})
@@ -196,7 +198,14 @@ def upgrade_changeset(struct, params \\ %{}) do
|> User.Info.user_upgrade(params[:info])
struct
- |> cast(params, [:bio, :name, :follower_address, :avatar, :last_refreshed_at])
+ |> cast(params, [
+ :bio,
+ :name,
+ :follower_address,
+ :following_address,
+ :avatar,
+ :last_refreshed_at
+ ])
|> unique_constraint(:nickname)
|> validate_format(:nickname, local_nickname_regex())
|> validate_length(:bio, max: 5000)
@@ -1039,15 +1048,20 @@ def sync_follow_counters(opts \\ []) do
end
end
+ @spec external_users_query() :: Ecto.Query.t()
+ def external_users_query do
+ User.Query.build(%{
+ external: true,
+ active: true,
+ order_by: :id
+ })
+ end
+
@spec external_users(keyword()) :: [User.t()]
def external_users(opts \\ []) do
query =
- User.Query.build(%{
- external: true,
- active: true,
- order_by: :id,
- select: [:id, :ap_id, :info]
- })
+ external_users_query()
+ |> select([u], struct(u, [:id, :ap_id, :info]))
query =
if opts[:max_id],
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 41b55bbab..a3174a787 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -994,6 +994,7 @@ defp object_to_user_data(data) do
avatar: avatar,
name: data["name"],
follower_address: data["followers"],
+ following_address: data["following"],
bio: data["summary"]
}
diff --git a/priv/repo/migrations/20190710115833_add_following_address_to_user.exs b/priv/repo/migrations/20190710115833_add_following_address_to_user.exs
new file mode 100644
index 000000000..fe30472a1
--- /dev/null
+++ b/priv/repo/migrations/20190710115833_add_following_address_to_user.exs
@@ -0,0 +1,9 @@
+defmodule Pleroma.Repo.Migrations.AddFollowingAddressToUser do
+ use Ecto.Migration
+
+ def change do
+ alter table(:users) do
+ add(:following_address, :string, unique: true)
+ end
+ end
+end
diff --git a/priv/repo/migrations/20190710125051_add_following_address_index_to_user.exs b/priv/repo/migrations/20190710125051_add_following_address_index_to_user.exs
new file mode 100644
index 000000000..0cbfb71f4
--- /dev/null
+++ b/priv/repo/migrations/20190710125051_add_following_address_index_to_user.exs
@@ -0,0 +1,8 @@
+defmodule Pleroma.Repo.Migrations.AddFollowingAddressIndexToUser do
+ use Ecto.Migration
+
+ @disable_ddl_transaction true
+ def change do
+ create(index(:users, [:following_address], concurrently: true))
+ end
+end
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
index 825e99879..6d05138fb 100644
--- a/test/web/activity_pub/transmogrifier_test.exs
+++ b/test/web/activity_pub/transmogrifier_test.exs
@@ -1121,6 +1121,7 @@ test "it upgrades a user to activitypub" do
assert user.info.ap_enabled
assert user.info.note_count == 1
assert user.follower_address == "https://niu.moe/users/rye/followers"
+ assert user.following_address == "https://niu.moe/users/rye/following"
user = User.get_cached_by_id(user.id)
assert user.info.note_count == 1
From 936050257d7899202ed78c455247adcd076f60e8 Mon Sep 17 00:00:00 2001
From: Alex S
Date: Wed, 10 Jul 2019 16:02:22 +0300
Subject: [PATCH 23/31] saving following_address for existing users
---
...add_following_address_from_source_data.exs | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 priv/repo/migrations/20190710125158_add_following_address_from_source_data.exs
diff --git a/priv/repo/migrations/20190710125158_add_following_address_from_source_data.exs b/priv/repo/migrations/20190710125158_add_following_address_from_source_data.exs
new file mode 100644
index 000000000..779aa382e
--- /dev/null
+++ b/priv/repo/migrations/20190710125158_add_following_address_from_source_data.exs
@@ -0,0 +1,20 @@
+defmodule Pleroma.Repo.Migrations.AddFollowingAddressFromSourceData do
+ use Ecto.Migration
+ import Ecto.Query
+ alias Pleroma.User
+
+ def change do
+ query =
+ User.external_users_query()
+ |> select([u], struct(u, [:id, :ap_id, :info]))
+
+ Pleroma.Repo.stream(query)
+ |> Enum.each(fn
+ %{info: %{source_data: source_data}} = user ->
+ Ecto.Changeset.cast(user, %{following_address: source_data["following"]}, [
+ :following_address
+ ])
+ |> Pleroma.Repo.update()
+ end)
+ end
+end
From ade213cb35c8dc1a6f86e7b3836bc2ca86c8ff54 Mon Sep 17 00:00:00 2001
From: Alex S
Date: Wed, 10 Jul 2019 16:37:39 +0300
Subject: [PATCH 24/31] robots txt test fix
---
test/tasks/robots_txt_test.exs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/tasks/robots_txt_test.exs b/test/tasks/robots_txt_test.exs
index 97147a919..78a3f17b4 100644
--- a/test/tasks/robots_txt_test.exs
+++ b/test/tasks/robots_txt_test.exs
@@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.RobotsTxtTest do
- use ExUnit.Case, async: true
+ use ExUnit.Case
alias Mix.Tasks.Pleroma.RobotsTxt
test "creates new dir" do
From beba7bbc8550aca07874e105b784b7a3cbe89838 Mon Sep 17 00:00:00 2001
From: Alex S
Date: Wed, 10 Jul 2019 17:39:07 +0300
Subject: [PATCH 25/31] removing synchronization worker
---
config/config.exs | 8 +-
docs/config.md | 6 +-
lib/pleroma/application.ex | 6 +-
lib/pleroma/user.ex | 32 +-----
lib/pleroma/user/synchronization.ex | 60 ----------
lib/pleroma/user/synchronization_worker.ex | 32 ------
.../web/activity_pub/transmogrifier.ex | 27 +++++
test/support/factory.ex | 1 +
test/user/synchronization_test.exs | 104 ------------------
test/user/synchronization_worker_test.exs | 49 ---------
test/user_test.exs | 54 ++-------
test/web/activity_pub/transmogrifier_test.exs | 28 +++++
12 files changed, 72 insertions(+), 335 deletions(-)
delete mode 100644 lib/pleroma/user/synchronization.ex
delete mode 100644 lib/pleroma/user/synchronization_worker.ex
delete mode 100644 test/user/synchronization_test.exs
delete mode 100644 test/user/synchronization_worker_test.exs
diff --git a/config/config.exs b/config/config.exs
index 0d3419102..f00191a6d 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -250,13 +250,7 @@
skip_thread_containment: true,
limit_to_local_content: :unauthenticated,
dynamic_configuration: false,
- external_user_synchronization: [
- enabled: false,
- # every 2 hours
- interval: 60 * 60 * 2,
- max_retries: 3,
- limit: 500
- ]
+ external_user_synchronization: false
config :pleroma, :markup,
# XXX - unfortunately, inline images must be enabled by default right now, because
diff --git a/docs/config.md b/docs/config.md
index 01730ec16..140789d87 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -126,11 +126,7 @@ config :pleroma, Pleroma.Emails.Mailer,
* `skip_thread_containment`: Skip filter out broken threads. The default is `false`.
* `limit_to_local_content`: Limit unauthenticated users to search for local statutes and users only. Possible values: `:unauthenticated`, `:all` and `false`. The default is `:unauthenticated`.
* `dynamic_configuration`: Allow transferring configuration to DB with the subsequent customization from Admin api.
-* `external_user_synchronization`: Following/followers counters synchronization settings.
- * `enabled`: Enables synchronization
- * `interval`: Interval between synchronization.
- * `max_retries`: Max rettries for host. After exceeding the limit, the check will not be carried out for users from this host.
- * `limit`: Users batch size for processing in one time.
+* `external_user_synchronization`: Enabling following/followers counters synchronization for external users.
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index 86c348a0d..ba4cf8486 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -151,11 +151,7 @@ def start(_type, _args) do
start: {Pleroma.Web.Endpoint, :start_link, []},
type: :supervisor
},
- %{id: Pleroma.Gopher.Server, start: {Pleroma.Gopher.Server, :start_link, []}},
- %{
- id: Pleroma.User.SynchronizationWorker,
- start: {Pleroma.User.SynchronizationWorker, :start_link, []}
- }
+ %{id: Pleroma.Gopher.Server, start: {Pleroma.Gopher.Server, :start_link, []}}
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 81efb4f13..e5a6c2529 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -108,6 +108,10 @@ def ap_id(%User{nickname: nickname}) do
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
+ @spec ap_following(User.t()) :: Sring.t()
+ def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa
+ def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
+
def user_info(%User{} = user, args \\ %{}) do
following_count =
if args[:following_count], do: args[:following_count], else: following_count(user)
@@ -129,6 +133,7 @@ def set_info_cache(user, args) do
Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user, args))
end
+ @spec restrict_deactivated(Ecto.Query.t()) :: Ecto.Query.t()
def restrict_deactivated(query) do
from(u in query,
where: not fragment("? \\? 'deactivated' AND ?->'deactivated' @> 'true'", u.info, u.info)
@@ -1021,33 +1026,6 @@ def perform(:follow_import, %User{} = follower, followed_identifiers)
)
end
- @spec sync_follow_counter() :: :ok
- def sync_follow_counter,
- do: PleromaJobQueue.enqueue(:background, __MODULE__, [:sync_follow_counters])
-
- @spec perform(:sync_follow_counters) :: :ok
- def perform(:sync_follow_counters) do
- {:ok, _pid} = Agent.start_link(fn -> %{} end, name: :domain_errors)
- config = Pleroma.Config.get([:instance, :external_user_synchronization])
-
- :ok = sync_follow_counters(config)
- Agent.stop(:domain_errors)
- end
-
- @spec sync_follow_counters(keyword()) :: :ok
- def sync_follow_counters(opts \\ []) do
- users = external_users(opts)
-
- if length(users) > 0 do
- errors = Agent.get(:domain_errors, fn state -> state end)
- {last, updated_errors} = User.Synchronization.call(users, errors, opts)
- Agent.update(:domain_errors, fn _state -> updated_errors end)
- sync_follow_counters(max_id: last.id, limit: opts[:limit])
- else
- :ok
- end
- end
-
@spec external_users_query() :: Ecto.Query.t()
def external_users_query do
User.Query.build(%{
diff --git a/lib/pleroma/user/synchronization.ex b/lib/pleroma/user/synchronization.ex
deleted file mode 100644
index 93660e08c..000000000
--- a/lib/pleroma/user/synchronization.ex
+++ /dev/null
@@ -1,60 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2018 Pleroma Authors
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.User.Synchronization do
- alias Pleroma.HTTP
- alias Pleroma.User
-
- @spec call([User.t()], map(), keyword()) :: {User.t(), map()}
- def call(users, errors, opts \\ []) do
- do_call(users, errors, opts)
- end
-
- defp do_call([user | []], errors, opts) do
- updated = fetch_counters(user, errors, opts)
- {user, updated}
- end
-
- defp do_call([user | others], errors, opts) do
- updated = fetch_counters(user, errors, opts)
- do_call(others, updated, opts)
- end
-
- defp fetch_counters(user, errors, opts) do
- %{host: host} = URI.parse(user.ap_id)
-
- info = %{}
- {following, errors} = fetch_counter(user.ap_id <> "/following", host, errors, opts)
- info = if following, do: Map.put(info, :following_count, following), else: info
-
- {followers, errors} = fetch_counter(user.ap_id <> "/followers", host, errors, opts)
- info = if followers, do: Map.put(info, :follower_count, followers), else: info
-
- User.set_info_cache(user, info)
- errors
- end
-
- defp available_domain?(domain, errors, opts) do
- max_retries = Keyword.get(opts, :max_retries, 3)
- not (Map.has_key?(errors, domain) && errors[domain] >= max_retries)
- end
-
- defp fetch_counter(url, host, errors, opts) do
- with true <- available_domain?(host, errors, opts),
- {:ok, %{body: body, status: code}} when code in 200..299 <-
- HTTP.get(
- url,
- [{:Accept, "application/activity+json"}]
- ),
- {:ok, data} <- Jason.decode(body) do
- {data["totalItems"], errors}
- else
- false ->
- {nil, errors}
-
- _ ->
- {nil, Map.update(errors, host, 1, &(&1 + 1))}
- end
- end
-end
diff --git a/lib/pleroma/user/synchronization_worker.ex b/lib/pleroma/user/synchronization_worker.ex
deleted file mode 100644
index ba9cc3556..000000000
--- a/lib/pleroma/user/synchronization_worker.ex
+++ /dev/null
@@ -1,32 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2018 Pleroma Authors
-# SPDX-License-Identifier: AGPL-3.0-onl
-
-defmodule Pleroma.User.SynchronizationWorker do
- use GenServer
-
- def start_link do
- config = Pleroma.Config.get([:instance, :external_user_synchronization])
-
- if config[:enabled] do
- GenServer.start_link(__MODULE__, interval: config[:interval])
- else
- :ignore
- end
- end
-
- def init(opts) do
- schedule_next(opts)
- {:ok, opts}
- end
-
- def handle_info(:sync_follow_counters, opts) do
- Pleroma.User.sync_follow_counter()
- schedule_next(opts)
- {:noreply, opts}
- end
-
- defp schedule_next(opts) do
- Process.send_after(self(), :sync_follow_counters, opts[:interval])
- end
-end
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index e34fe6611..d14490bb5 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -1087,6 +1087,10 @@ def upgrade_user_from_ap_id(ap_id) do
PleromaJobQueue.enqueue(:transmogrifier, __MODULE__, [:user_upgrade, user])
end
+ if Pleroma.Config.get([:instance, :external_user_synchronization]) do
+ update_following_followers_counters(user)
+ end
+
{:ok, user}
else
%User{} = user -> {:ok, user}
@@ -1119,4 +1123,27 @@ def maybe_fix_user_object(data) do
data
|> maybe_fix_user_url
end
+
+ def update_following_followers_counters(user) do
+ info = %{}
+
+ following = fetch_counter(user.following_address)
+ info = if following, do: Map.put(info, :following_count, following), else: info
+
+ followers = fetch_counter(user.follower_address)
+ info = if followers, do: Map.put(info, :follower_count, followers), else: info
+
+ User.set_info_cache(user, info)
+ end
+
+ defp fetch_counter(url) do
+ with {:ok, %{body: body, status: code}} when code in 200..299 <-
+ Pleroma.HTTP.get(
+ url,
+ [{:Accept, "application/activity+json"}]
+ ),
+ {:ok, data} <- Jason.decode(body) do
+ data["totalItems"]
+ end
+ end
end
diff --git a/test/support/factory.ex b/test/support/factory.ex
index a9f750eec..531eb81e4 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -38,6 +38,7 @@ def user_factory do
user
| ap_id: User.ap_id(user),
follower_address: User.ap_followers(user),
+ following_address: User.ap_following(user),
following: [User.ap_id(user)]
}
end
diff --git a/test/user/synchronization_test.exs b/test/user/synchronization_test.exs
deleted file mode 100644
index 67b669431..000000000
--- a/test/user/synchronization_test.exs
+++ /dev/null
@@ -1,104 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2018 Pleroma Authors
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.User.SynchronizationTest do
- use Pleroma.DataCase
- import Pleroma.Factory
- alias Pleroma.User
- alias Pleroma.User.Synchronization
-
- setup do
- Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
- :ok
- end
-
- test "update following/followers counters" do
- user1 =
- insert(:user,
- local: false,
- ap_id: "http://localhost:4001/users/masto_closed"
- )
-
- user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
-
- users = User.external_users()
- assert length(users) == 2
- {user, %{}} = Synchronization.call(users, %{})
- assert user == List.last(users)
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
- assert followers == 437
- assert following == 152
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
- assert followers == 527
- assert following == 267
- end
-
- test "don't check host if errors exist" do
- user1 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser1")
-
- user2 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser2")
-
- users = User.external_users()
- assert length(users) == 2
-
- {user, %{"domain-with-errors" => 2}} =
- Synchronization.call(users, %{"domain-with-errors" => 2}, max_retries: 2)
-
- assert user == List.last(users)
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
- assert followers == 0
- assert following == 0
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
- assert followers == 0
- assert following == 0
- end
-
- test "don't check host if errors appeared" do
- user1 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser1")
-
- user2 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser2")
-
- users = User.external_users()
- assert length(users) == 2
-
- {user, %{"domain-with-errors" => 2}} = Synchronization.call(users, %{}, max_retries: 2)
-
- assert user == List.last(users)
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
- assert followers == 0
- assert following == 0
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
- assert followers == 0
- assert following == 0
- end
-
- test "other users after error appeared" do
- user1 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser1")
- user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
-
- users = User.external_users()
- assert length(users) == 2
-
- {user, %{"domain-with-errors" => 2}} = Synchronization.call(users, %{}, max_retries: 2)
- assert user == List.last(users)
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
- assert followers == 0
- assert following == 0
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
- assert followers == 527
- assert following == 267
- end
-end
diff --git a/test/user/synchronization_worker_test.exs b/test/user/synchronization_worker_test.exs
deleted file mode 100644
index 835c5327f..000000000
--- a/test/user/synchronization_worker_test.exs
+++ /dev/null
@@ -1,49 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2018 Pleroma Authors
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.User.SynchronizationWorkerTest do
- use Pleroma.DataCase
- import Pleroma.Factory
-
- setup do
- Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
-
- config = Pleroma.Config.get([:instance, :external_user_synchronization])
-
- for_update = [enabled: true, interval: 1000]
-
- Pleroma.Config.put([:instance, :external_user_synchronization], for_update)
-
- on_exit(fn ->
- Pleroma.Config.put([:instance, :external_user_synchronization], config)
- end)
-
- :ok
- end
-
- test "sync follow counters" do
- user1 =
- insert(:user,
- local: false,
- ap_id: "http://localhost:4001/users/masto_closed"
- )
-
- user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
-
- {:ok, _} = Pleroma.User.SynchronizationWorker.start_link()
- :timer.sleep(1500)
-
- %{follower_count: followers, following_count: following} =
- Pleroma.User.get_cached_user_info(user1)
-
- assert followers == 437
- assert following == 152
-
- %{follower_count: followers, following_count: following} =
- Pleroma.User.get_cached_user_info(user2)
-
- assert followers == 527
- assert following == 267
- end
-end
diff --git a/test/user_test.exs b/test/user_test.exs
index 62be79b4f..7c3fe976d 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -54,6 +54,14 @@ test "ap_followers returns the followers collection for the user" do
assert expected_followers_collection == User.ap_followers(user)
end
+ test "ap_following returns the following collection for the user" do
+ user = UserBuilder.build()
+
+ expected_followers_collection = "#{User.ap_id(user)}/following"
+
+ assert expected_followers_collection == User.ap_following(user)
+ end
+
test "returns all pending follow requests" do
unlocked = insert(:user)
locked = insert(:user, %{info: %{locked: true}})
@@ -1240,52 +1248,6 @@ test "external_users/1 external active users with limit", %{user1: user1, user2:
assert User.external_users(max_id: fdb_user2.id, limit: 1) == []
end
-
- test "sync_follow_counters/1", %{user1: user1, user2: user2} do
- {:ok, _pid} = Agent.start_link(fn -> %{} end, name: :domain_errors)
-
- :ok = User.sync_follow_counters()
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
- assert followers == 437
- assert following == 152
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
- assert followers == 527
- assert following == 267
-
- Agent.stop(:domain_errors)
- end
-
- test "sync_follow_counters/1 in separate batches", %{user1: user1, user2: user2} do
- {:ok, _pid} = Agent.start_link(fn -> %{} end, name: :domain_errors)
-
- :ok = User.sync_follow_counters(limit: 1)
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
- assert followers == 437
- assert following == 152
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
- assert followers == 527
- assert following == 267
-
- Agent.stop(:domain_errors)
- end
-
- test "perform/1 with :sync_follow_counters", %{user1: user1, user2: user2} do
- :ok = User.perform(:sync_follow_counters)
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
- assert followers == 437
- assert following == 152
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
- assert followers == 527
- assert following == 267
- end
end
describe "set_info_cache/2" do
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
index 6d05138fb..b896a532b 100644
--- a/test/web/activity_pub/transmogrifier_test.exs
+++ b/test/web/activity_pub/transmogrifier_test.exs
@@ -1359,4 +1359,32 @@ test "removes recipient's follower collection from cc", %{user: user} do
refute recipient.follower_address in fixed_object["to"]
end
end
+
+ test "update_following_followers_counters/1" do
+ user1 =
+ insert(:user,
+ local: false,
+ follower_address: "http://localhost:4001/users/masto_closed/followers",
+ following_address: "http://localhost:4001/users/masto_closed/following"
+ )
+
+ user2 =
+ insert(:user,
+ local: false,
+ follower_address: "http://localhost:4001/users/fuser2/followers",
+ following_address: "http://localhost:4001/users/fuser2/following"
+ )
+
+ Transmogrifier.update_following_followers_counters(user1)
+ Transmogrifier.update_following_followers_counters(user2)
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
+ assert followers == 437
+ assert following == 152
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
+
+ assert followers == 527
+ assert following == 267
+ end
end
From 252e129b1e784147cf29868bcc191f88a9b7d5b9 Mon Sep 17 00:00:00 2001
From: "Haelwenn (lanodan) Monnier"
Date: Sun, 30 Jun 2019 01:05:28 +0200
Subject: [PATCH 26/31] MastoAPI: Add categories to custom emojis
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Note: This isn’t in a release yet, can be seen in mastofe on the
rebase/glitch-soc branch.
---
CHANGELOG.md | 1 +
lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 4 +++-
test/web/mastodon_api/mastodon_api_controller_test.exs | 1 +
3 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9ec8a5551..f27446f36 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@ Configuration: `federation_incoming_replies_max_depth` option
- Admin API: Allow querying user by ID
- Added synchronization of following/followers counters for external users
- Configuration: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`.
+- Mastodon API: Add support for categories for custom emojis by reusing the group feature.
## [1.0.0] - 2019-06-29
### Security
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 82f180635..8c2033c3a 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -299,7 +299,9 @@ defp mastodonized_emoji do
"static_url" => url,
"visible_in_picker" => true,
"url" => url,
- "tags" => tags
+ "tags" => tags,
+ # Assuming that a comma is authorized in the category name
+ "category" => (tags -- ["Custom"]) |> Enum.join(",")
}
end)
end
diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs
index 64f14f794..8afb1497b 100644
--- a/test/web/mastodon_api/mastodon_api_controller_test.exs
+++ b/test/web/mastodon_api/mastodon_api_controller_test.exs
@@ -2958,6 +2958,7 @@ test "with tags", %{conn: conn} do
assert Map.has_key?(emoji, "static_url")
assert Map.has_key?(emoji, "tags")
assert is_list(emoji["tags"])
+ assert Map.has_key?(emoji, "category")
assert Map.has_key?(emoji, "url")
assert Map.has_key?(emoji, "visible_in_picker")
end
From a237c6a2d4b60a6f15429eb860b995ed2df8d327 Mon Sep 17 00:00:00 2001
From: Alexander Strizhakov
Date: Wed, 10 Jul 2019 15:23:25 +0000
Subject: [PATCH 27/31] support for idna domains
---
lib/pleroma/user/search.ex | 17 ++++--
.../host-meta-zetsubou.xn--q9jyb4c.xml | 5 ++
test/fixtures/lain.xml | 12 +++++
test/support/http_request_mock.ex | 39 ++++++++++++++
test/user_search_test.exs | 52 +++++++++++++++++++
test/web/web_finger/web_finger_test.exs | 11 ++++
6 files changed, 133 insertions(+), 3 deletions(-)
create mode 100644 test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml
create mode 100644 test/fixtures/lain.xml
diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex
index 64eb6d2bc..e0fc6daa6 100644
--- a/lib/pleroma/user/search.ex
+++ b/lib/pleroma/user/search.ex
@@ -18,8 +18,7 @@ def search(query_string, opts \\ []) do
for_user = Keyword.get(opts, :for_user)
- # Strip the beginning @ off if there is a query
- query_string = String.trim_leading(query_string, "@")
+ query_string = format_query(query_string)
maybe_resolve(resolve, for_user, query_string)
@@ -40,6 +39,18 @@ def search(query_string, opts \\ []) do
results
end
+ defp format_query(query_string) do
+ # Strip the beginning @ off if there is a query
+ query_string = String.trim_leading(query_string, "@")
+
+ with [name, domain] <- String.split(query_string, "@"),
+ formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:]+/, "") do
+ name <> "@" <> to_string(:idna.encode(formatted_domain))
+ else
+ _ -> query_string
+ end
+ end
+
defp search_query(query_string, for_user, following) do
for_user
|> base_query(following)
@@ -151,7 +162,7 @@ defp boost_search_rank_query(query, for_user) do
defp fts_search_subquery(query, term) do
processed_query =
String.trim_trailing(term, "@" <> local_domain())
- |> String.replace(~r/\W+/, " ")
+ |> String.replace(~r/[!-\/|@|[-`|{-~|:-?]+/, " ")
|> String.trim()
|> String.split()
|> Enum.map(&(&1 <> ":*"))
diff --git a/test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml b/test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml
new file mode 100644
index 000000000..df64d44b0
--- /dev/null
+++ b/test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/test/fixtures/lain.xml b/test/fixtures/lain.xml
new file mode 100644
index 000000000..332b3b28d
--- /dev/null
+++ b/test/fixtures/lain.xml
@@ -0,0 +1,12 @@
+
+
+ acct:lain@zetsubou.xn--q9jyb4c
+ https://zetsubou.xn--q9jyb4c/users/lain
+
+
+
+
+
+
+
diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex
index c593a5e4a..ff6bb78f9 100644
--- a/test/support/http_request_mock.ex
+++ b/test/support/http_request_mock.ex
@@ -840,6 +840,45 @@ def get("http://404.site" <> _, _, _, _) do
}}
end
+ def get(
+ "https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=lain@zetsubou.xn--q9jyb4c",
+ _,
+ _,
+ Accept: "application/xrd+xml,application/jrd+json"
+ ) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/lain.xml")
+ }}
+ end
+
+ def get(
+ "https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=https://zetsubou.xn--q9jyb4c/users/lain",
+ _,
+ _,
+ Accept: "application/xrd+xml,application/jrd+json"
+ ) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/lain.xml")
+ }}
+ end
+
+ def get(
+ "https://zetsubou.xn--q9jyb4c/.well-known/host-meta",
+ _,
+ _,
+ _
+ ) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml")
+ }}
+ end
+
def get(url, query, body, headers) do
{:error,
"Not implemented the mock response for get #{inspect(url)}, #{query}, #{inspect(body)}, #{
diff --git a/test/user_search_test.exs b/test/user_search_test.exs
index 1f0162486..4de6c82a5 100644
--- a/test/user_search_test.exs
+++ b/test/user_search_test.exs
@@ -248,5 +248,57 @@ test "local user search with users" do
[result] = User.search("lain@localhost", resolve: true, for_user: user)
assert Map.put(result, :search_rank, nil) |> Map.put(:search_type, nil) == local_user
end
+
+ test "works with idna domains" do
+ user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
+
+ results = User.search("lain@zetsubou.みんな", resolve: false, for_user: user)
+
+ result = List.first(results)
+
+ assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
+ end
+
+ test "works with idna domains converted input" do
+ user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
+
+ results =
+ User.search("lain@zetsubou." <> to_string(:idna.encode("zetsubou.みんな")),
+ resolve: false,
+ for_user: user
+ )
+
+ result = List.first(results)
+
+ assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
+ end
+
+ test "works with idna domains and bad chars in domain" do
+ user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
+
+ results =
+ User.search("lain@zetsubou!@#$%^&*()+,-/:;<=>?[]'_{}|~`.みんな",
+ resolve: false,
+ for_user: user
+ )
+
+ result = List.first(results)
+
+ assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
+ end
+
+ test "works with idna domains and query as link" do
+ user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
+
+ results =
+ User.search("https://zetsubou.みんな/users/lain",
+ resolve: false,
+ for_user: user
+ )
+
+ result = List.first(results)
+
+ assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
+ end
end
end
diff --git a/test/web/web_finger/web_finger_test.exs b/test/web/web_finger/web_finger_test.exs
index 335c95b18..0578b4b8e 100644
--- a/test/web/web_finger/web_finger_test.exs
+++ b/test/web/web_finger/web_finger_test.exs
@@ -104,5 +104,16 @@ test "it gets the xrd endpoint for statusnet" do
assert template == "http://status.alpicola.com/main/xrd?uri={uri}"
end
+
+ test "it works with idna domains as nickname" do
+ nickname = "lain@" <> to_string(:idna.encode("zetsubou.みんな"))
+
+ {:ok, _data} = WebFinger.finger(nickname)
+ end
+
+ test "it works with idna domains as link" do
+ ap_id = "https://" <> to_string(:idna.encode("zetsubou.みんな")) <> "/users/lain"
+ {:ok, _data} = WebFinger.finger(ap_id)
+ end
end
end
From 59e16fc45a2fe1fa6bfeaecaa35f485b8a34bb6d Mon Sep 17 00:00:00 2001
From: Alex S
Date: Wed, 10 Jul 2019 18:55:11 +0300
Subject: [PATCH 28/31] enable synchronization by default
---
config/config.exs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config/config.exs b/config/config.exs
index f00191a6d..99b500993 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -250,7 +250,7 @@
skip_thread_containment: true,
limit_to_local_content: :unauthenticated,
dynamic_configuration: false,
- external_user_synchronization: false
+ external_user_synchronization: true
config :pleroma, :markup,
# XXX - unfortunately, inline images must be enabled by default right now, because
From 347a1823fdce8271acb7adac9595f5c8754b8f5c Mon Sep 17 00:00:00 2001
From: Ariadne Conill
Date: Thu, 11 Jul 2019 08:57:30 +0000
Subject: [PATCH 29/31] add mailmap [ci skip]
---
.mailmap | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 .mailmap
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 000000000..e4ca5f9b5
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,2 @@
+Ariadne Conill
+Ariadne Conill
From 846ad9a463e7d6767170305f32eef7bbd09f8a6b Mon Sep 17 00:00:00 2001
From: Alexander Strizhakov
Date: Thu, 11 Jul 2019 13:02:13 +0000
Subject: [PATCH 30/31] admin api configure changes
---
CHANGELOG.md | 5 +
docs/api/admin_api.md | 48 +-
.../web/admin_api/admin_api_controller.ex | 8 +-
lib/pleroma/web/admin_api/config.ex | 107 ++---
.../web/admin_api/views/config_view.ex | 2 +-
.../admin_api/admin_api_controller_test.exs | 388 ++++++++++++----
test/web/admin_api/config_test.exs | 433 +++++++++++++-----
7 files changed, 691 insertions(+), 300 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f27446f36..da2aee883 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,10 +25,15 @@ Configuration: `federation_incoming_replies_max_depth` option
- Admin API: Return users' tags when querying reports
- Admin API: Return avatar and display name when querying users
- Admin API: Allow querying user by ID
+- Admin API: Added support for `tuples`.
- Added synchronization of following/followers counters for external users
- Configuration: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`.
- Mastodon API: Add support for categories for custom emojis by reusing the group feature.
+### Changed
+- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
+- Admin API: changed json structure for saving config settings.
+
## [1.0.0] - 2019-06-29
### Security
- Mastodon API: Fix display names not being sanitized
diff --git a/docs/api/admin_api.md b/docs/api/admin_api.md
index bce5e399b..c429da822 100644
--- a/docs/api/admin_api.md
+++ b/docs/api/admin_api.md
@@ -573,7 +573,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
configs: [
{
"group": string,
- "key": string,
+ "key": string or string with leading `:` for atoms,
"value": string or {} or [] or {"tuple": []}
}
]
@@ -583,10 +583,11 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
## `/api/pleroma/admin/config`
### Update config settings
Module name can be passed as string, which starts with `Pleroma`, e.g. `"Pleroma.Upload"`.
-Atom or boolean value can be passed with `:` in the beginning, e.g. `":true"`, `":upload"`. For keys it is not needed.
-Integer with `i:`, e.g. `"i:150"`.
-Tuple with more than 2 values with `{"tuple": ["first_val", Pleroma.Module, []]}`.
+Atom keys and values can be passed with `:` in the beginning, e.g. `":upload"`.
+Tuples can be passed as `{"tuple": ["first_val", Pleroma.Module, []]}`.
`{"tuple": ["some_string", "Pleroma.Some.Module", []]}` will be converted to `{"some_string", Pleroma.Some.Module, []}`.
+Keywords can be passed as lists with 2 child tuples, e.g.
+`[{"tuple": ["first_val", Pleroma.Module]}, {"tuple": ["second_val", true]}]`.
Compile time settings (need instance reboot):
- all settings by this keys:
@@ -603,7 +604,7 @@ Compile time settings (need instance reboot):
- Params:
- `configs` => [
- `group` (string)
- - `key` (string)
+ - `key` (string or string with leading `:` for atoms)
- `value` (string, [], {} or {"tuple": []})
- `delete` = true (optional, if parameter must be deleted)
]
@@ -616,24 +617,25 @@ Compile time settings (need instance reboot):
{
"group": "pleroma",
"key": "Pleroma.Upload",
- "value": {
- "uploader": "Pleroma.Uploaders.Local",
- "filters": ["Pleroma.Upload.Filter.Dedupe"],
- "link_name": ":true",
- "proxy_remote": ":false",
- "proxy_opts": {
- "redirect_on_failure": ":false",
- "max_body_length": "i:1048576",
- "http": {
- "follow_redirect": ":true",
- "pool": ":upload"
- }
- },
- "dispatch": {
+ "value": [
+ {"tuple": [":uploader", "Pleroma.Uploaders.Local"]},
+ {"tuple": [":filters", ["Pleroma.Upload.Filter.Dedupe"]]},
+ {"tuple": [":link_name", true]},
+ {"tuple": [":proxy_remote", false]},
+ {"tuple": [":proxy_opts", [
+ {"tuple": [":redirect_on_failure", false]},
+ {"tuple": [":max_body_length", 1048576]},
+ {"tuple": [":http": [
+ {"tuple": [":follow_redirect", true]},
+ {"tuple": [":pool", ":upload"]},
+ ]]}
+ ]
+ ]},
+ {"tuple": [":dispatch", {
"tuple": ["/api/v1/streaming", "Pleroma.Web.MastodonAPI.WebsocketHandler", []]
- }
- }
- }
+ }]}
+ ]
+ }
]
}
@@ -644,7 +646,7 @@ Compile time settings (need instance reboot):
configs: [
{
"group": string,
- "key": string,
+ "key": string or string with leading `:` for atoms,
"value": string or {} or [] or {"tuple": []}
}
]
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index 8b3c3c91f..4a0bf4823 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -371,13 +371,13 @@ def config_update(conn, %{"configs" => configs}) do
if Pleroma.Config.get([:instance, :dynamic_configuration]) do
updated =
Enum.map(configs, fn
- %{"group" => group, "key" => key, "value" => value} ->
- {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
- config
-
%{"group" => group, "key" => key, "delete" => "true"} ->
{:ok, _} = Config.delete(%{group: group, key: key})
nil
+
+ %{"group" => group, "key" => key, "value" => value} ->
+ {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
+ config
end)
|> Enum.reject(&is_nil(&1))
diff --git a/lib/pleroma/web/admin_api/config.ex b/lib/pleroma/web/admin_api/config.ex
index 24674abc5..b4eb8e002 100644
--- a/lib/pleroma/web/admin_api/config.ex
+++ b/lib/pleroma/web/admin_api/config.ex
@@ -67,99 +67,86 @@ def delete(params) do
end
@spec from_binary(binary()) :: term()
- def from_binary(value), do: :erlang.binary_to_term(value)
+ def from_binary(binary), do: :erlang.binary_to_term(binary)
- @spec from_binary_to_map(binary()) :: any()
- def from_binary_to_map(binary) do
+ @spec from_binary_with_convert(binary()) :: any()
+ def from_binary_with_convert(binary) do
from_binary(binary)
|> do_convert()
end
- defp do_convert([{k, v}] = value) when is_list(value) and length(value) == 1,
- do: %{k => do_convert(v)}
+ defp do_convert(entity) when is_list(entity) do
+ for v <- entity, into: [], do: do_convert(v)
+ end
- defp do_convert(values) when is_list(values), do: for(val <- values, do: do_convert(val))
+ defp do_convert(entity) when is_map(entity) do
+ for {k, v} <- entity, into: %{}, do: {do_convert(k), do_convert(v)}
+ end
- defp do_convert({k, v} = value) when is_tuple(value),
- do: %{k => do_convert(v)}
+ defp do_convert({:dispatch, [entity]}), do: %{"tuple" => [":dispatch", [inspect(entity)]]}
- defp do_convert(value) when is_tuple(value), do: %{"tuple" => do_convert(Tuple.to_list(value))}
+ defp do_convert(entity) when is_tuple(entity),
+ do: %{"tuple" => do_convert(Tuple.to_list(entity))}
- defp do_convert(value) when is_binary(value) or is_map(value) or is_number(value), do: value
+ defp do_convert(entity) when is_boolean(entity) or is_number(entity) or is_nil(entity),
+ do: entity
- defp do_convert(value) when is_atom(value) do
- string = to_string(value)
+ defp do_convert(entity) when is_atom(entity) do
+ string = to_string(entity)
if String.starts_with?(string, "Elixir."),
- do: String.trim_leading(string, "Elixir."),
- else: value
+ do: do_convert(string),
+ else: ":" <> string
end
+ defp do_convert("Elixir." <> module_name), do: module_name
+
+ defp do_convert(entity) when is_binary(entity), do: entity
+
@spec transform(any()) :: binary()
- def transform(%{"tuple" => _} = entity), do: :erlang.term_to_binary(do_transform(entity))
-
- def transform(entity) when is_map(entity) do
- tuples =
- for {k, v} <- entity,
- into: [],
- do: {if(is_atom(k), do: k, else: String.to_atom(k)), do_transform(v)}
-
- Enum.reject(tuples, fn {_k, v} -> is_nil(v) end)
- |> Enum.sort()
- |> :erlang.term_to_binary()
- end
-
- def transform(entity) when is_list(entity) do
- list = Enum.map(entity, &do_transform(&1))
- :erlang.term_to_binary(list)
+ def transform(entity) when is_binary(entity) or is_map(entity) or is_list(entity) do
+ :erlang.term_to_binary(do_transform(entity))
end
def transform(entity), do: :erlang.term_to_binary(entity)
- defp do_transform(%Regex{} = value) when is_map(value), do: value
+ defp do_transform(%Regex{} = entity) when is_map(entity), do: entity
- defp do_transform(%{"tuple" => [k, values] = entity}) when length(entity) == 2 do
- {do_transform(k), do_transform(values)}
+ defp do_transform(%{"tuple" => [":dispatch", [entity]]}) do
+ cleaned_string = String.replace(entity, ~r/[^\w|^{:,[|^,|^[|^\]^}|^\/|^\.|^"]^\s/, "")
+ {dispatch_settings, []} = Code.eval_string(cleaned_string, [], requires: [], macros: [])
+ {:dispatch, [dispatch_settings]}
end
- defp do_transform(%{"tuple" => values}) do
- Enum.reduce(values, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end)
+ defp do_transform(%{"tuple" => entity}) do
+ Enum.reduce(entity, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end)
end
- defp do_transform(value) when is_map(value) do
- values = for {key, val} <- value, into: [], do: {String.to_atom(key), do_transform(val)}
-
- Enum.sort(values)
+ defp do_transform(entity) when is_map(entity) do
+ for {k, v} <- entity, into: %{}, do: {do_transform(k), do_transform(v)}
end
- defp do_transform(value) when is_list(value) do
- Enum.map(value, &do_transform(&1))
+ defp do_transform(entity) when is_list(entity) do
+ for v <- entity, into: [], do: do_transform(v)
end
- defp do_transform(entity) when is_list(entity) and length(entity) == 1, do: hd(entity)
-
- defp do_transform(value) when is_binary(value) do
- String.trim(value)
+ defp do_transform(entity) when is_binary(entity) do
+ String.trim(entity)
|> do_transform_string()
end
- defp do_transform(value), do: value
+ defp do_transform(entity), do: entity
- defp do_transform_string(value) when byte_size(value) == 0, do: nil
+ defp do_transform_string("~r/" <> pattern) do
+ pattern = String.trim_trailing(pattern, "/")
+ ~r/#{pattern}/
+ end
+
+ defp do_transform_string(":" <> atom), do: String.to_atom(atom)
defp do_transform_string(value) do
- cond do
- String.starts_with?(value, "Pleroma") or String.starts_with?(value, "Phoenix") ->
- String.to_existing_atom("Elixir." <> value)
-
- String.starts_with?(value, ":") ->
- String.replace(value, ":", "") |> String.to_existing_atom()
-
- String.starts_with?(value, "i:") ->
- String.replace(value, "i:", "") |> String.to_integer()
-
- true ->
- value
- end
+ if String.starts_with?(value, "Pleroma") or String.starts_with?(value, "Phoenix"),
+ do: String.to_existing_atom("Elixir." <> value),
+ else: value
end
end
diff --git a/lib/pleroma/web/admin_api/views/config_view.ex b/lib/pleroma/web/admin_api/views/config_view.ex
index a31f1041f..49add0b6e 100644
--- a/lib/pleroma/web/admin_api/views/config_view.ex
+++ b/lib/pleroma/web/admin_api/views/config_view.ex
@@ -15,7 +15,7 @@ def render("show.json", %{config: config}) do
%{
key: config.key,
group: config.group,
- value: Pleroma.Web.AdminAPI.Config.from_binary_to_map(config.value)
+ value: Pleroma.Web.AdminAPI.Config.from_binary_with_convert(config.value)
}
end
end
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs
index 0e04e7e94..1b71cbff3 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/admin_api_controller_test.exs
@@ -1407,14 +1407,19 @@ test "create new config setting in db", %{conn: conn} do
post(conn, "/api/pleroma/admin/config", %{
configs: [
%{group: "pleroma", key: "key1", value: "value1"},
+ %{
+ group: "ueberauth",
+ key: "Ueberauth.Strategy.Twitter.OAuth",
+ value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
+ },
%{
group: "pleroma",
key: "key2",
value: %{
- "nested_1" => "nested_value1",
- "nested_2" => [
- %{"nested_22" => "nested_value222"},
- %{"nested_33" => %{"nested_44" => "nested_444"}}
+ ":nested_1" => "nested_value1",
+ ":nested_2" => [
+ %{":nested_22" => "nested_value222"},
+ %{":nested_33" => %{":nested_44" => "nested_444"}}
]
}
},
@@ -1423,13 +1428,13 @@ test "create new config setting in db", %{conn: conn} do
key: "key3",
value: [
%{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
- %{"nested_4" => ":true"}
+ %{"nested_4" => true}
]
},
%{
group: "pleroma",
key: "key4",
- value: %{"nested_5" => ":upload", "endpoint" => "https://example.com"}
+ value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
},
%{
group: "idna",
@@ -1446,31 +1451,34 @@ test "create new config setting in db", %{conn: conn} do
"key" => "key1",
"value" => "value1"
},
+ %{
+ "group" => "ueberauth",
+ "key" => "Ueberauth.Strategy.Twitter.OAuth",
+ "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}]
+ },
%{
"group" => "pleroma",
"key" => "key2",
- "value" => [
- %{"nested_1" => "nested_value1"},
- %{
- "nested_2" => [
- %{"nested_22" => "nested_value222"},
- %{"nested_33" => %{"nested_44" => "nested_444"}}
- ]
- }
- ]
+ "value" => %{
+ ":nested_1" => "nested_value1",
+ ":nested_2" => [
+ %{":nested_22" => "nested_value222"},
+ %{":nested_33" => %{":nested_44" => "nested_444"}}
+ ]
+ }
},
%{
"group" => "pleroma",
"key" => "key3",
"value" => [
- [%{"nested_3" => "nested_3"}, %{"nested_33" => "nested_33"}],
+ %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
%{"nested_4" => true}
]
},
%{
"group" => "pleroma",
"key" => "key4",
- "value" => [%{"endpoint" => "https://example.com"}, %{"nested_5" => "upload"}]
+ "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"}
},
%{
"group" => "idna",
@@ -1482,23 +1490,23 @@ test "create new config setting in db", %{conn: conn} do
assert Application.get_env(:pleroma, :key1) == "value1"
- assert Application.get_env(:pleroma, :key2) == [
+ assert Application.get_env(:pleroma, :key2) == %{
nested_1: "nested_value1",
nested_2: [
- [nested_22: "nested_value222"],
- [nested_33: [nested_44: "nested_444"]]
+ %{nested_22: "nested_value222"},
+ %{nested_33: %{nested_44: "nested_444"}}
]
- ]
+ }
assert Application.get_env(:pleroma, :key3) == [
- [nested_3: :nested_3, nested_33: "nested_33"],
- [nested_4: true]
+ %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
+ %{"nested_4" => true}
]
- assert Application.get_env(:pleroma, :key4) == [
- endpoint: "https://example.com",
+ assert Application.get_env(:pleroma, :key4) == %{
+ "endpoint" => "https://example.com",
nested_5: :upload
- ]
+ }
assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
end
@@ -1507,11 +1515,22 @@ test "update config setting & delete", %{conn: conn} do
config1 = insert(:config, key: "keyaa1")
config2 = insert(:config, key: "keyaa2")
+ insert(:config,
+ group: "ueberauth",
+ key: "Ueberauth.Strategy.Microsoft.OAuth",
+ value: :erlang.term_to_binary([])
+ )
+
conn =
post(conn, "/api/pleroma/admin/config", %{
configs: [
%{group: config1.group, key: config1.key, value: "another_value"},
- %{group: config2.group, key: config2.key, delete: "true"}
+ %{group: config2.group, key: config2.key, delete: "true"},
+ %{
+ group: "ueberauth",
+ key: "Ueberauth.Strategy.Microsoft.OAuth",
+ delete: "true"
+ }
]
})
@@ -1536,11 +1555,13 @@ test "common config example", %{conn: conn} do
%{
"group" => "pleroma",
"key" => "Pleroma.Captcha.NotReal",
- "value" => %{
- "enabled" => ":false",
- "method" => "Pleroma.Captcha.Kocaptcha",
- "seconds_valid" => "i:60"
- }
+ "value" => [
+ %{"tuple" => [":enabled", false]},
+ %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
+ %{"tuple" => [":seconds_valid", 60]},
+ %{"tuple" => [":path", ""]},
+ %{"tuple" => [":key1", nil]}
+ ]
}
]
})
@@ -1551,9 +1572,11 @@ test "common config example", %{conn: conn} do
"group" => "pleroma",
"key" => "Pleroma.Captcha.NotReal",
"value" => [
- %{"enabled" => false},
- %{"method" => "Pleroma.Captcha.Kocaptcha"},
- %{"seconds_valid" => 60}
+ %{"tuple" => [":enabled", false]},
+ %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
+ %{"tuple" => [":seconds_valid", 60]},
+ %{"tuple" => [":path", ""]},
+ %{"tuple" => [":key1", nil]}
]
}
]
@@ -1569,51 +1592,57 @@ test "tuples with more than two values", %{conn: conn} do
"key" => "Pleroma.Web.Endpoint.NotReal",
"value" => [
%{
- "http" => %{
- "dispatch" => [
+ "tuple" => [
+ ":http",
+ [
%{
"tuple" => [
- ":_",
+ ":key2",
[
- %{
- "tuple" => [
- "/api/v1/streaming",
- "Pleroma.Web.MastodonAPI.WebsocketHandler",
- []
- ]
- },
- %{
- "tuple" => [
- "/websocket",
- "Phoenix.Endpoint.CowboyWebSocket",
- %{
- "tuple" => [
- "Phoenix.Transports.WebSocket",
- %{
- "tuple" => [
- "Pleroma.Web.Endpoint",
- "Pleroma.Web.UserSocket",
- []
- ]
- }
- ]
- }
- ]
- },
%{
"tuple" => [
":_",
- "Phoenix.Endpoint.Cowboy2Handler",
- %{
- "tuple" => ["Pleroma.Web.Endpoint", []]
- }
+ [
+ %{
+ "tuple" => [
+ "/api/v1/streaming",
+ "Pleroma.Web.MastodonAPI.WebsocketHandler",
+ []
+ ]
+ },
+ %{
+ "tuple" => [
+ "/websocket",
+ "Phoenix.Endpoint.CowboyWebSocket",
+ %{
+ "tuple" => [
+ "Phoenix.Transports.WebSocket",
+ %{
+ "tuple" => [
+ "Pleroma.Web.Endpoint",
+ "Pleroma.Web.UserSocket",
+ []
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ %{
+ "tuple" => [
+ ":_",
+ "Phoenix.Endpoint.Cowboy2Handler",
+ %{"tuple" => ["Pleroma.Web.Endpoint", []]}
+ ]
+ }
+ ]
]
}
]
]
}
]
- }
+ ]
}
]
}
@@ -1627,41 +1656,206 @@ test "tuples with more than two values", %{conn: conn} do
"key" => "Pleroma.Web.Endpoint.NotReal",
"value" => [
%{
- "http" => %{
- "dispatch" => %{
- "_" => [
- %{
- "tuple" => [
- "/api/v1/streaming",
- "Pleroma.Web.MastodonAPI.WebsocketHandler",
- []
- ]
- },
- %{
- "tuple" => [
- "/websocket",
- "Phoenix.Endpoint.CowboyWebSocket",
+ "tuple" => [
+ ":http",
+ [
+ %{
+ "tuple" => [
+ ":key2",
+ [
%{
- "Elixir.Phoenix.Transports.WebSocket" => %{
- "tuple" => [
- "Pleroma.Web.Endpoint",
- "Pleroma.Web.UserSocket",
- []
+ "tuple" => [
+ ":_",
+ [
+ %{
+ "tuple" => [
+ "/api/v1/streaming",
+ "Pleroma.Web.MastodonAPI.WebsocketHandler",
+ []
+ ]
+ },
+ %{
+ "tuple" => [
+ "/websocket",
+ "Phoenix.Endpoint.CowboyWebSocket",
+ %{
+ "tuple" => [
+ "Phoenix.Transports.WebSocket",
+ %{
+ "tuple" => [
+ "Pleroma.Web.Endpoint",
+ "Pleroma.Web.UserSocket",
+ []
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ %{
+ "tuple" => [
+ ":_",
+ "Phoenix.Endpoint.Cowboy2Handler",
+ %{"tuple" => ["Pleroma.Web.Endpoint", []]}
+ ]
+ }
]
- }
+ ]
}
]
- },
- %{
- "tuple" => [
- "_",
- "Phoenix.Endpoint.Cowboy2Handler",
- %{"Elixir.Pleroma.Web.Endpoint" => []}
- ]
+ ]
+ }
+ ]
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ end
+
+ test "settings with nesting map", %{conn: conn} do
+ conn =
+ post(conn, "/api/pleroma/admin/config", %{
+ configs: [
+ %{
+ "group" => "pleroma",
+ "key" => "key1",
+ "value" => [
+ %{"tuple" => [":key2", "some_val"]},
+ %{
+ "tuple" => [
+ ":key3",
+ %{
+ ":max_options" => 20,
+ ":max_option_chars" => 200,
+ ":min_expiration" => 0,
+ ":max_expiration" => 31_536_000,
+ "nested" => %{
+ ":max_options" => 20,
+ ":max_option_chars" => 200,
+ ":min_expiration" => 0,
+ ":max_expiration" => 31_536_000
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ })
+
+ assert json_response(conn, 200) ==
+ %{
+ "configs" => [
+ %{
+ "group" => "pleroma",
+ "key" => "key1",
+ "value" => [
+ %{"tuple" => [":key2", "some_val"]},
+ %{
+ "tuple" => [
+ ":key3",
+ %{
+ ":max_expiration" => 31_536_000,
+ ":max_option_chars" => 200,
+ ":max_options" => 20,
+ ":min_expiration" => 0,
+ "nested" => %{
+ ":max_expiration" => 31_536_000,
+ ":max_option_chars" => 200,
+ ":max_options" => 20,
+ ":min_expiration" => 0
}
- ]
- }
+ }
+ ]
}
+ ]
+ }
+ ]
+ }
+ end
+
+ test "value as map", %{conn: conn} do
+ conn =
+ post(conn, "/api/pleroma/admin/config", %{
+ configs: [
+ %{
+ "group" => "pleroma",
+ "key" => "key1",
+ "value" => %{"key" => "some_val"}
+ }
+ ]
+ })
+
+ assert json_response(conn, 200) ==
+ %{
+ "configs" => [
+ %{
+ "group" => "pleroma",
+ "key" => "key1",
+ "value" => %{"key" => "some_val"}
+ }
+ ]
+ }
+ end
+
+ test "dispatch setting", %{conn: conn} do
+ conn =
+ post(conn, "/api/pleroma/admin/config", %{
+ configs: [
+ %{
+ "group" => "pleroma",
+ "key" => "Pleroma.Web.Endpoint.NotReal",
+ "value" => [
+ %{
+ "tuple" => [
+ ":http",
+ [
+ %{"tuple" => [":ip", %{"tuple" => [127, 0, 0, 1]}]},
+ %{"tuple" => [":dispatch", ["{:_,
+ [
+ {\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
+ {\"/websocket\", Phoenix.Endpoint.CowboyWebSocket,
+ {Phoenix.Transports.WebSocket,
+ {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}},
+ {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
+ ]}"]]}
+ ]
+ ]
+ }
+ ]
+ }
+ ]
+ })
+
+ dispatch_string =
+ "{:_, [{\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, " <>
+ "{\"/websocket\", Phoenix.Endpoint.CowboyWebSocket, {Phoenix.Transports.WebSocket, " <>
+ "{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}}, " <>
+ "{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}]}"
+
+ assert json_response(conn, 200) == %{
+ "configs" => [
+ %{
+ "group" => "pleroma",
+ "key" => "Pleroma.Web.Endpoint.NotReal",
+ "value" => [
+ %{
+ "tuple" => [
+ ":http",
+ [
+ %{"tuple" => [":ip", %{"tuple" => [127, 0, 0, 1]}]},
+ %{
+ "tuple" => [
+ ":dispatch",
+ [
+ dispatch_string
+ ]
+ ]
+ }
+ ]
+ ]
}
]
}
diff --git a/test/web/admin_api/config_test.exs b/test/web/admin_api/config_test.exs
index b281831e3..d41666ef3 100644
--- a/test/web/admin_api/config_test.exs
+++ b/test/web/admin_api/config_test.exs
@@ -61,117 +61,306 @@ test "string" do
assert Config.from_binary(binary) == "value as string"
end
+ test "boolean" do
+ binary = Config.transform(false)
+ assert binary == :erlang.term_to_binary(false)
+ assert Config.from_binary(binary) == false
+ end
+
+ test "nil" do
+ binary = Config.transform(nil)
+ assert binary == :erlang.term_to_binary(nil)
+ assert Config.from_binary(binary) == nil
+ end
+
+ test "integer" do
+ binary = Config.transform(150)
+ assert binary == :erlang.term_to_binary(150)
+ assert Config.from_binary(binary) == 150
+ end
+
+ test "atom" do
+ binary = Config.transform(":atom")
+ assert binary == :erlang.term_to_binary(:atom)
+ assert Config.from_binary(binary) == :atom
+ end
+
+ test "pleroma module" do
+ binary = Config.transform("Pleroma.Bookmark")
+ assert binary == :erlang.term_to_binary(Pleroma.Bookmark)
+ assert Config.from_binary(binary) == Pleroma.Bookmark
+ end
+
+ test "phoenix module" do
+ binary = Config.transform("Phoenix.Socket.V1.JSONSerializer")
+ assert binary == :erlang.term_to_binary(Phoenix.Socket.V1.JSONSerializer)
+ assert Config.from_binary(binary) == Phoenix.Socket.V1.JSONSerializer
+ end
+
+ test "sigil" do
+ binary = Config.transform("~r/comp[lL][aA][iI][nN]er/")
+ assert binary == :erlang.term_to_binary(~r/comp[lL][aA][iI][nN]er/)
+ assert Config.from_binary(binary) == ~r/comp[lL][aA][iI][nN]er/
+ end
+
+ test "2 child tuple" do
+ binary = Config.transform(%{"tuple" => ["v1", ":v2"]})
+ assert binary == :erlang.term_to_binary({"v1", :v2})
+ assert Config.from_binary(binary) == {"v1", :v2}
+ end
+
+ test "tuple with n childs" do
+ binary =
+ Config.transform(%{
+ "tuple" => [
+ "v1",
+ ":v2",
+ "Pleroma.Bookmark",
+ 150,
+ false,
+ "Phoenix.Socket.V1.JSONSerializer"
+ ]
+ })
+
+ assert binary ==
+ :erlang.term_to_binary(
+ {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer}
+ )
+
+ assert Config.from_binary(binary) ==
+ {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer}
+ end
+
+ test "tuple with dispatch key" do
+ binary = Config.transform(%{"tuple" => [":dispatch", ["{:_,
+ [
+ {\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
+ {\"/websocket\", Phoenix.Endpoint.CowboyWebSocket,
+ {Phoenix.Transports.WebSocket,
+ {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}},
+ {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
+ ]}"]]})
+
+ assert binary ==
+ :erlang.term_to_binary(
+ {:dispatch,
+ [
+ {:_,
+ [
+ {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
+ {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
+ {Phoenix.Transports.WebSocket,
+ {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: "/websocket"]}}},
+ {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
+ ]}
+ ]}
+ )
+
+ assert Config.from_binary(binary) ==
+ {:dispatch,
+ [
+ {:_,
+ [
+ {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
+ {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
+ {Phoenix.Transports.WebSocket,
+ {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: "/websocket"]}}},
+ {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
+ ]}
+ ]}
+ end
+
+ test "map with string key" do
+ binary = Config.transform(%{"key" => "value"})
+ assert binary == :erlang.term_to_binary(%{"key" => "value"})
+ assert Config.from_binary(binary) == %{"key" => "value"}
+ end
+
+ test "map with atom key" do
+ binary = Config.transform(%{":key" => "value"})
+ assert binary == :erlang.term_to_binary(%{key: "value"})
+ assert Config.from_binary(binary) == %{key: "value"}
+ end
+
+ test "list of strings" do
+ binary = Config.transform(["v1", "v2", "v3"])
+ assert binary == :erlang.term_to_binary(["v1", "v2", "v3"])
+ assert Config.from_binary(binary) == ["v1", "v2", "v3"]
+ end
+
test "list of modules" do
binary = Config.transform(["Pleroma.Repo", "Pleroma.Activity"])
assert binary == :erlang.term_to_binary([Pleroma.Repo, Pleroma.Activity])
assert Config.from_binary(binary) == [Pleroma.Repo, Pleroma.Activity]
end
- test "list of strings" do
- binary = Config.transform(["string1", "string2"])
- assert binary == :erlang.term_to_binary(["string1", "string2"])
- assert Config.from_binary(binary) == ["string1", "string2"]
+ test "list of atoms" do
+ binary = Config.transform([":v1", ":v2", ":v3"])
+ assert binary == :erlang.term_to_binary([:v1, :v2, :v3])
+ assert Config.from_binary(binary) == [:v1, :v2, :v3]
end
- test "map" do
+ test "list of mixed values" do
binary =
- Config.transform(%{
- "types" => "Pleroma.PostgresTypes",
- "telemetry_event" => ["Pleroma.Repo.Instrumenter"],
- "migration_lock" => ""
- })
+ Config.transform([
+ "v1",
+ ":v2",
+ "Pleroma.Repo",
+ "Phoenix.Socket.V1.JSONSerializer",
+ 15,
+ false
+ ])
assert binary ==
- :erlang.term_to_binary(
- telemetry_event: [Pleroma.Repo.Instrumenter],
- types: Pleroma.PostgresTypes
- )
+ :erlang.term_to_binary([
+ "v1",
+ :v2,
+ Pleroma.Repo,
+ Phoenix.Socket.V1.JSONSerializer,
+ 15,
+ false
+ ])
assert Config.from_binary(binary) == [
- telemetry_event: [Pleroma.Repo.Instrumenter],
- types: Pleroma.PostgresTypes
+ "v1",
+ :v2,
+ Pleroma.Repo,
+ Phoenix.Socket.V1.JSONSerializer,
+ 15,
+ false
]
end
- test "complex map with nested integers, lists and atoms" do
- binary =
- Config.transform(%{
- "uploader" => "Pleroma.Uploaders.Local",
- "filters" => ["Pleroma.Upload.Filter.Dedupe"],
- "link_name" => ":true",
- "proxy_remote" => ":false",
- "proxy_opts" => %{
- "redirect_on_failure" => ":false",
- "max_body_length" => "i:1048576",
- "http" => %{
- "follow_redirect" => ":true",
- "pool" => ":upload"
- }
- }
- })
-
- assert binary ==
- :erlang.term_to_binary(
- filters: [Pleroma.Upload.Filter.Dedupe],
- link_name: true,
- proxy_opts: [
- http: [
- follow_redirect: true,
- pool: :upload
- ],
- max_body_length: 1_048_576,
- redirect_on_failure: false
- ],
- proxy_remote: false,
- uploader: Pleroma.Uploaders.Local
- )
-
- assert Config.from_binary(binary) ==
- [
- filters: [Pleroma.Upload.Filter.Dedupe],
- link_name: true,
- proxy_opts: [
- http: [
- follow_redirect: true,
- pool: :upload
- ],
- max_body_length: 1_048_576,
- redirect_on_failure: false
- ],
- proxy_remote: false,
- uploader: Pleroma.Uploaders.Local
- ]
+ test "simple keyword" do
+ binary = Config.transform([%{"tuple" => [":key", "value"]}])
+ assert binary == :erlang.term_to_binary([{:key, "value"}])
+ assert Config.from_binary(binary) == [{:key, "value"}]
+ assert Config.from_binary(binary) == [key: "value"]
end
test "keyword" do
binary =
- Config.transform(%{
- "level" => ":warn",
- "meta" => [":all"],
- "webhook_url" => "https://hooks.slack.com/services/YOUR-KEY-HERE"
- })
+ Config.transform([
+ %{"tuple" => [":types", "Pleroma.PostgresTypes"]},
+ %{"tuple" => [":telemetry_event", ["Pleroma.Repo.Instrumenter"]]},
+ %{"tuple" => [":migration_lock", nil]},
+ %{"tuple" => [":key1", 150]},
+ %{"tuple" => [":key2", "string"]}
+ ])
+
+ assert binary ==
+ :erlang.term_to_binary(
+ types: Pleroma.PostgresTypes,
+ telemetry_event: [Pleroma.Repo.Instrumenter],
+ migration_lock: nil,
+ key1: 150,
+ key2: "string"
+ )
+
+ assert Config.from_binary(binary) == [
+ types: Pleroma.PostgresTypes,
+ telemetry_event: [Pleroma.Repo.Instrumenter],
+ migration_lock: nil,
+ key1: 150,
+ key2: "string"
+ ]
+ end
+
+ test "complex keyword with nested mixed childs" do
+ binary =
+ Config.transform([
+ %{"tuple" => [":uploader", "Pleroma.Uploaders.Local"]},
+ %{"tuple" => [":filters", ["Pleroma.Upload.Filter.Dedupe"]]},
+ %{"tuple" => [":link_name", true]},
+ %{"tuple" => [":proxy_remote", false]},
+ %{"tuple" => [":common_map", %{":key" => "value"}]},
+ %{
+ "tuple" => [
+ ":proxy_opts",
+ [
+ %{"tuple" => [":redirect_on_failure", false]},
+ %{"tuple" => [":max_body_length", 1_048_576]},
+ %{
+ "tuple" => [
+ ":http",
+ [%{"tuple" => [":follow_redirect", true]}, %{"tuple" => [":pool", ":upload"]}]
+ ]
+ }
+ ]
+ ]
+ }
+ ])
+
+ assert binary ==
+ :erlang.term_to_binary(
+ uploader: Pleroma.Uploaders.Local,
+ filters: [Pleroma.Upload.Filter.Dedupe],
+ link_name: true,
+ proxy_remote: false,
+ common_map: %{key: "value"},
+ proxy_opts: [
+ redirect_on_failure: false,
+ max_body_length: 1_048_576,
+ http: [
+ follow_redirect: true,
+ pool: :upload
+ ]
+ ]
+ )
+
+ assert Config.from_binary(binary) ==
+ [
+ uploader: Pleroma.Uploaders.Local,
+ filters: [Pleroma.Upload.Filter.Dedupe],
+ link_name: true,
+ proxy_remote: false,
+ common_map: %{key: "value"},
+ proxy_opts: [
+ redirect_on_failure: false,
+ max_body_length: 1_048_576,
+ http: [
+ follow_redirect: true,
+ pool: :upload
+ ]
+ ]
+ ]
+ end
+
+ test "common keyword" do
+ binary =
+ Config.transform([
+ %{"tuple" => [":level", ":warn"]},
+ %{"tuple" => [":meta", [":all"]]},
+ %{"tuple" => [":path", ""]},
+ %{"tuple" => [":val", nil]},
+ %{"tuple" => [":webhook_url", "https://hooks.slack.com/services/YOUR-KEY-HERE"]}
+ ])
assert binary ==
:erlang.term_to_binary(
level: :warn,
meta: [:all],
+ path: "",
+ val: nil,
webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
)
assert Config.from_binary(binary) == [
level: :warn,
meta: [:all],
+ path: "",
+ val: nil,
webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
]
end
- test "complex map with sigil" do
+ test "complex keyword with sigil" do
binary =
- Config.transform(%{
- federated_timeline_removal: [],
- reject: [~r/comp[lL][aA][iI][nN]er/],
- replace: []
- })
+ Config.transform([
+ %{"tuple" => [":federated_timeline_removal", []]},
+ %{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]},
+ %{"tuple" => [":replace", []]}
+ ])
assert binary ==
:erlang.term_to_binary(
@@ -184,54 +373,68 @@ test "complex map with sigil" do
[federated_timeline_removal: [], reject: [~r/comp[lL][aA][iI][nN]er/], replace: []]
end
- test "complex map with tuples with more than 2 values" do
+ test "complex keyword with tuples with more than 2 values" do
binary =
- Config.transform(%{
- "http" => %{
- "dispatch" => [
- %{
- "tuple" => [
- ":_",
- [
- %{
- "tuple" => [
- "/api/v1/streaming",
- "Pleroma.Web.MastodonAPI.WebsocketHandler",
- []
- ]
- },
- %{
- "tuple" => [
- "/websocket",
- "Phoenix.Endpoint.CowboyWebSocket",
- %{
- "tuple" => [
- "Phoenix.Transports.WebSocket",
- %{"tuple" => ["Pleroma.Web.Endpoint", "Pleroma.Web.UserSocket", []]}
+ Config.transform([
+ %{
+ "tuple" => [
+ ":http",
+ [
+ %{
+ "tuple" => [
+ ":key1",
+ [
+ %{
+ "tuple" => [
+ ":_",
+ [
+ %{
+ "tuple" => [
+ "/api/v1/streaming",
+ "Pleroma.Web.MastodonAPI.WebsocketHandler",
+ []
+ ]
+ },
+ %{
+ "tuple" => [
+ "/websocket",
+ "Phoenix.Endpoint.CowboyWebSocket",
+ %{
+ "tuple" => [
+ "Phoenix.Transports.WebSocket",
+ %{
+ "tuple" => [
+ "Pleroma.Web.Endpoint",
+ "Pleroma.Web.UserSocket",
+ []
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ %{
+ "tuple" => [
+ ":_",
+ "Phoenix.Endpoint.Cowboy2Handler",
+ %{"tuple" => ["Pleroma.Web.Endpoint", []]}
+ ]
+ }
]
- }
- ]
- },
- %{
- "tuple" => [
- ":_",
- "Phoenix.Endpoint.Cowboy2Handler",
- %{
- "tuple" => ["Pleroma.Web.Endpoint", []]
- }
- ]
- }
+ ]
+ }
+ ]
]
- ]
- }
+ }
+ ]
]
}
- })
+ ])
assert binary ==
:erlang.term_to_binary(
http: [
- dispatch: [
+ key1: [
_: [
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
{"/websocket", Phoenix.Endpoint.CowboyWebSocket,
@@ -245,7 +448,7 @@ test "complex map with tuples with more than 2 values" do
assert Config.from_binary(binary) == [
http: [
- dispatch: [
+ key1: [
{:_,
[
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
From 4198c3ac390edaab04a61a179b1f8bc5adaf89de Mon Sep 17 00:00:00 2001
From: Eugenij
Date: Thu, 11 Jul 2019 13:55:31 +0000
Subject: [PATCH 31/31] Extend Pleroma.Pagination to support offset-based
pagination, use async/await to execute status and account search in parallel
---
CHANGELOG.md | 1 +
config/test.exs | 4 +-
lib/pleroma/activity.ex | 2 +-
lib/pleroma/activity/search.ex | 21 +++-
lib/pleroma/pagination.ex | 29 ++++-
lib/pleroma/user/search.ex | 8 +-
.../web/mastodon_api/search_controller.ex | 116 +++++++++++-------
.../mastodon_api/search_controller_test.exs | 74 ++++++++++-
8 files changed, 193 insertions(+), 62 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index da2aee883..942733ab6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Metadata rendering errors resulting in the entire page being inaccessible
- Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`)
- Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity
+- Mastodon API: Add `account_id`, `type`, `offset`, and `limit` to search API (`/api/v1/search` and `/api/v2/search`)
### Added
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
diff --git a/config/test.exs b/config/test.exs
index 19d7cca5f..96ecf3592 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -65,7 +65,9 @@
total_user_limit: 3,
enabled: false
-config :pleroma, :rate_limit, app_account_creation: {10_000, 5}
+config :pleroma, :rate_limit,
+ search: [{1000, 30}, {1000, 30}],
+ app_account_creation: {10_000, 5}
config :pleroma, :http_security, report_uri: "https://endpoint.com"
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index 6db41fe6e..46552c7be 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -344,5 +344,5 @@ def restrict_deactivated_users(query) do
)
end
- defdelegate search(user, query), to: Pleroma.Activity.Search
+ defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
end
diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/activity/search.ex
index 0aa2aab23..0cc3770a7 100644
--- a/lib/pleroma/activity/search.ex
+++ b/lib/pleroma/activity/search.ex
@@ -5,14 +5,17 @@
defmodule Pleroma.Activity.Search do
alias Pleroma.Activity
alias Pleroma.Object.Fetcher
- alias Pleroma.Repo
+ alias Pleroma.Pagination
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Visibility
import Ecto.Query
- def search(user, search_query) do
+ def search(user, search_query, options \\ []) do
index_type = if Pleroma.Config.get([:database, :rum_enabled]), do: :rum, else: :gin
+ limit = Enum.min([Keyword.get(options, :limit), 40])
+ offset = Keyword.get(options, :offset, 0)
+ author = Keyword.get(options, :author)
Activity
|> Activity.with_preloaded_object()
@@ -20,15 +23,23 @@ def search(user, search_query) do
|> restrict_public()
|> query_with(index_type, search_query)
|> maybe_restrict_local(user)
- |> Repo.all()
+ |> maybe_restrict_author(author)
+ |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => limit}, :offset)
|> maybe_fetch(user, search_query)
end
+ def maybe_restrict_author(query, %User{} = author) do
+ from([a, o] in query,
+ where: a.actor == ^author.ap_id
+ )
+ end
+
+ def maybe_restrict_author(query, _), do: query
+
defp restrict_public(q) do
from([a, o] in q,
where: fragment("?->>'type' = 'Create'", a.data),
- where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
- limit: 40
+ where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients
)
end
diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex
index 3d7dd9e6a..2b869ccdc 100644
--- a/lib/pleroma/pagination.ex
+++ b/lib/pleroma/pagination.ex
@@ -14,16 +14,28 @@ defmodule Pleroma.Pagination do
@default_limit 20
- def fetch_paginated(query, params) do
+ def fetch_paginated(query, params, type \\ :keyset)
+
+ def fetch_paginated(query, params, :keyset) do
options = cast_params(params)
query
- |> paginate(options)
+ |> paginate(options, :keyset)
|> Repo.all()
|> enforce_order(options)
end
- def paginate(query, options) do
+ def fetch_paginated(query, params, :offset) do
+ options = cast_params(params)
+
+ query
+ |> paginate(options, :offset)
+ |> Repo.all()
+ end
+
+ def paginate(query, options, method \\ :keyset)
+
+ def paginate(query, options, :keyset) do
query
|> restrict(:min_id, options)
|> restrict(:since_id, options)
@@ -32,11 +44,18 @@ def paginate(query, options) do
|> restrict(:limit, options)
end
+ def paginate(query, options, :offset) do
+ query
+ |> restrict(:offset, options)
+ |> restrict(:limit, options)
+ end
+
defp cast_params(params) do
param_types = %{
min_id: :string,
since_id: :string,
max_id: :string,
+ offset: :integer,
limit: :integer
}
@@ -70,6 +89,10 @@ defp restrict(query, :order, _options) do
order_by(query, [u], fragment("? desc nulls last", u.id))
end
+ defp restrict(query, :offset, %{offset: offset}) do
+ offset(query, ^offset)
+ end
+
defp restrict(query, :limit, options) do
limit = Map.get(options, :limit, @default_limit)
diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex
index e0fc6daa6..46620b89a 100644
--- a/lib/pleroma/user/search.ex
+++ b/lib/pleroma/user/search.ex
@@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.Search do
+ alias Pleroma.Pagination
alias Pleroma.Repo
alias Pleroma.User
import Ecto.Query
@@ -32,8 +33,7 @@ def search(query_string, opts \\ []) do
query_string
|> search_query(for_user, following)
- |> paginate(result_limit, offset)
- |> Repo.all()
+ |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset)
end)
results
@@ -87,10 +87,6 @@ defp filter_blocked_domains(query, %User{info: %{domain_blocks: domain_blocks}})
defp filter_blocked_domains(query, _), do: query
- defp paginate(query, limit, offset) do
- from(q in query, limit: ^limit, offset: ^offset)
- end
-
defp union_subqueries({fts_subquery, trigram_subquery}) do
from(s in trigram_subquery, union_all: ^fts_subquery)
end
diff --git a/lib/pleroma/web/mastodon_api/search_controller.ex b/lib/pleroma/web/mastodon_api/search_controller.ex
index 939f7f6cb..9072aa7a4 100644
--- a/lib/pleroma/web/mastodon_api/search_controller.ex
+++ b/lib/pleroma/web/mastodon_api/search_controller.ex
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
alias Pleroma.Activity
alias Pleroma.Plugs.RateLimiter
+ alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web
alias Pleroma.Web.ControllerHelper
@@ -16,43 +17,6 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
require Logger
plug(RateLimiter, :search when action in [:search, :search2, :account_search])
- def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
- accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, [])
- statuses = with_fallback(fn -> Activity.search(user, query) end, [])
-
- tags_path = Web.base_url() <> "/tag/"
-
- tags =
- query
- |> prepare_tags
- |> Enum.map(fn tag -> %{name: tag, url: tags_path <> tag} end)
-
- res = %{
- "accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
- "statuses" =>
- StatusView.render("index.json", activities: statuses, for: user, as: :activity),
- "hashtags" => tags
- }
-
- json(conn, res)
- end
-
- def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
- accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end)
- statuses = with_fallback(fn -> Activity.search(user, query) end)
-
- tags = prepare_tags(query)
-
- res = %{
- "accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
- "statuses" =>
- StatusView.render("index.json", activities: statuses, for: user, as: :activity),
- "hashtags" => tags
- }
-
- json(conn, res)
- end
-
def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = User.search(query, search_options(params, user))
res = AccountView.render("accounts.json", users: accounts, for: user, as: :user)
@@ -60,12 +24,36 @@ def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) d
json(conn, res)
end
- defp prepare_tags(query) do
- query
- |> String.split()
- |> Enum.uniq()
- |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
- |> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
+ def search2(conn, params), do: do_search(:v2, conn, params)
+ def search(conn, params), do: do_search(:v1, conn, params)
+
+ defp do_search(version, %{assigns: %{user: user}} = conn, %{"q" => query} = params) do
+ options = search_options(params, user)
+ timeout = Keyword.get(Repo.config(), :timeout, 15_000)
+ default_values = %{"statuses" => [], "accounts" => [], "hashtags" => []}
+
+ result =
+ default_values
+ |> Enum.map(fn {resource, default_value} ->
+ if params["type"] == nil or params["type"] == resource do
+ {resource, fn -> resource_search(version, resource, query, options) end}
+ else
+ {resource, fn -> default_value end}
+ end
+ end)
+ |> Task.async_stream(fn {resource, f} -> {resource, with_fallback(f)} end,
+ timeout: timeout,
+ on_timeout: :kill_task
+ )
+ |> Enum.reduce(default_values, fn
+ {:ok, {resource, result}}, acc ->
+ Map.put(acc, resource, result)
+
+ _error, acc ->
+ acc
+ end)
+
+ json(conn, result)
end
defp search_options(params, user) do
@@ -74,8 +62,45 @@ defp search_options(params, user) do
following: params["following"] == "true",
limit: ControllerHelper.fetch_integer_param(params, "limit"),
offset: ControllerHelper.fetch_integer_param(params, "offset"),
+ type: params["type"],
+ author: get_author(params),
for_user: user
]
+ |> Enum.filter(&elem(&1, 1))
+ end
+
+ defp resource_search(_, "accounts", query, options) do
+ accounts = with_fallback(fn -> User.search(query, options) end)
+ AccountView.render("accounts.json", users: accounts, for: options[:for_user], as: :user)
+ end
+
+ defp resource_search(_, "statuses", query, options) do
+ statuses = with_fallback(fn -> Activity.search(options[:for_user], query, options) end)
+ StatusView.render("index.json", activities: statuses, for: options[:for_user], as: :activity)
+ end
+
+ defp resource_search(:v2, "hashtags", query, _options) do
+ tags_path = Web.base_url() <> "/tag/"
+
+ query
+ |> prepare_tags()
+ |> Enum.map(fn tag ->
+ tag = String.trim_leading(tag, "#")
+ %{name: tag, url: tags_path <> tag}
+ end)
+ end
+
+ defp resource_search(:v1, "hashtags", query, _options) do
+ query
+ |> prepare_tags()
+ |> Enum.map(fn tag -> String.trim_leading(tag, "#") end)
+ end
+
+ defp prepare_tags(query) do
+ query
+ |> String.split()
+ |> Enum.uniq()
+ |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
end
defp with_fallback(f, fallback \\ []) do
@@ -87,4 +112,9 @@ defp with_fallback(f, fallback \\ []) do
fallback
end
end
+
+ defp get_author(%{"account_id" => account_id}) when is_binary(account_id),
+ do: User.get_cached_by_id(account_id)
+
+ defp get_author(_params), do: nil
end
diff --git a/test/web/mastodon_api/search_controller_test.exs b/test/web/mastodon_api/search_controller_test.exs
index ea534b393..9f50c09f4 100644
--- a/test/web/mastodon_api/search_controller_test.exs
+++ b/test/web/mastodon_api/search_controller_test.exs
@@ -22,7 +22,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
test "it returns empty result if user or status search return undefined error", %{conn: conn} do
with_mocks [
{Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]},
- {Pleroma.Activity, [], [search: fn _u, _q -> raise "Oops" end]}
+ {Pleroma.Activity, [], [search: fn _u, _q, _o -> raise "Oops" end]}
] do
conn = get(conn, "/api/v2/search", %{"q" => "2hu"})
@@ -51,7 +51,6 @@ test "search", %{conn: conn} do
conn = get(conn, "/api/v2/search", %{"q" => "2hu #private"})
assert results = json_response(conn, 200)
- # IO.inspect results
[account | _] = results["accounts"]
assert account["id"] == to_string(user_three.id)
@@ -98,7 +97,7 @@ test "account search", %{conn: conn} do
test "it returns empty result if user or status search return undefined error", %{conn: conn} do
with_mocks [
{Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]},
- {Pleroma.Activity, [], [search: fn _u, _q -> raise "Oops" end]}
+ {Pleroma.Activity, [], [search: fn _u, _q, _o -> raise "Oops" end]}
] do
conn =
conn
@@ -195,5 +194,74 @@ test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} d
assert results = json_response(conn, 200)
assert [] == results["accounts"]
end
+
+ test "search with limit and offset", %{conn: conn} do
+ user = insert(:user)
+ _user_two = insert(:user, %{nickname: "shp@shitposter.club"})
+ _user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
+
+ {:ok, _activity1} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
+ {:ok, _activity2} = CommonAPI.post(user, %{"status" => "This is also about 2hu"})
+
+ result =
+ conn
+ |> get("/api/v1/search", %{"q" => "2hu", "limit" => 1})
+
+ assert results = json_response(result, 200)
+ assert [%{"id" => activity_id1}] = results["statuses"]
+ assert [_] = results["accounts"]
+
+ results =
+ conn
+ |> get("/api/v1/search", %{"q" => "2hu", "limit" => 1, "offset" => 1})
+ |> json_response(200)
+
+ assert [%{"id" => activity_id2}] = results["statuses"]
+ assert [] = results["accounts"]
+
+ assert activity_id1 != activity_id2
+ end
+
+ test "search returns results only for the given type", %{conn: conn} do
+ user = insert(:user)
+ _user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
+
+ {:ok, _activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
+
+ assert %{"statuses" => [_activity], "accounts" => [], "hashtags" => []} =
+ conn
+ |> get("/api/v1/search", %{"q" => "2hu", "type" => "statuses"})
+ |> json_response(200)
+
+ assert %{"statuses" => [], "accounts" => [_user_two], "hashtags" => []} =
+ conn
+ |> get("/api/v1/search", %{"q" => "2hu", "type" => "accounts"})
+ |> json_response(200)
+ end
+
+ test "search uses account_id to filter statuses by the author", %{conn: conn} do
+ user = insert(:user, %{nickname: "shp@shitposter.club"})
+ user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
+
+ {:ok, activity1} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
+ {:ok, activity2} = CommonAPI.post(user_two, %{"status" => "This is also about 2hu"})
+
+ results =
+ conn
+ |> get("/api/v1/search", %{"q" => "2hu", "account_id" => user.id})
+ |> json_response(200)
+
+ assert [%{"id" => activity_id1}] = results["statuses"]
+ assert activity_id1 == activity1.id
+ assert [_] = results["accounts"]
+
+ results =
+ conn
+ |> get("/api/v1/search", %{"q" => "2hu", "account_id" => user_two.id})
+ |> json_response(200)
+
+ assert [%{"id" => activity_id2}] = results["statuses"]
+ assert activity_id2 == activity2.id
+ end
end
end