forked from AkkomaGang/akkoma
Merge branch 'develop' into global-status-expiration
This commit is contained in:
commit
da22119c2f
82 changed files with 5293 additions and 3565 deletions
|
@ -22,8 +22,21 @@ defmodule Pleroma.LoadTesting.Activities do
|
||||||
@max_concurrency 10
|
@max_concurrency 10
|
||||||
|
|
||||||
@visibility ~w(public private direct unlisted)
|
@visibility ~w(public private direct unlisted)
|
||||||
@types ~w(simple emoji mentions hell_thread attachment tag like reblog simple_thread remote)
|
@types [
|
||||||
@groups ~w(user friends non_friends)
|
:simple,
|
||||||
|
:emoji,
|
||||||
|
:mentions,
|
||||||
|
:hell_thread,
|
||||||
|
:attachment,
|
||||||
|
:tag,
|
||||||
|
:like,
|
||||||
|
:reblog,
|
||||||
|
:simple_thread
|
||||||
|
]
|
||||||
|
@groups [:friends_local, :friends_remote, :non_friends_local, :non_friends_local]
|
||||||
|
@remote_groups [:friends_remote, :non_friends_remote]
|
||||||
|
@friends_groups [:friends_local, :friends_remote]
|
||||||
|
@non_friends_groups [:non_friends_local, :non_friends_remote]
|
||||||
|
|
||||||
@spec generate(User.t(), keyword()) :: :ok
|
@spec generate(User.t(), keyword()) :: :ok
|
||||||
def generate(user, opts \\ []) do
|
def generate(user, opts \\ []) do
|
||||||
|
@ -34,33 +47,24 @@ def generate(user, opts \\ []) do
|
||||||
|
|
||||||
opts = Keyword.merge(@defaults, opts)
|
opts = Keyword.merge(@defaults, opts)
|
||||||
|
|
||||||
friends =
|
users = Users.prepare_users(user, opts)
|
||||||
user
|
|
||||||
|> Users.get_users(limit: opts[:friends_used], local: :local, friends?: true)
|
|
||||||
|> Enum.shuffle()
|
|
||||||
|
|
||||||
non_friends =
|
{:ok, _} = Agent.start_link(fn -> users[:non_friends_remote] end, name: :non_friends_remote)
|
||||||
user
|
|
||||||
|> Users.get_users(limit: opts[:non_friends_used], local: :local, friends?: false)
|
|
||||||
|> Enum.shuffle()
|
|
||||||
|
|
||||||
task_data =
|
task_data =
|
||||||
for visibility <- @visibility,
|
for visibility <- @visibility,
|
||||||
type <- @types,
|
type <- @types,
|
||||||
group <- @groups,
|
group <- [:user | @groups],
|
||||||
do: {visibility, type, group}
|
do: {visibility, type, group}
|
||||||
|
|
||||||
IO.puts("Starting generating #{opts[:iterations]} iterations of activities...")
|
IO.puts("Starting generating #{opts[:iterations]} iterations of activities...")
|
||||||
|
|
||||||
friends_thread = Enum.take(friends, 5)
|
|
||||||
non_friends_thread = Enum.take(friends, 5)
|
|
||||||
|
|
||||||
public_long_thread = fn ->
|
public_long_thread = fn ->
|
||||||
generate_long_thread("public", user, friends_thread, non_friends_thread, opts)
|
generate_long_thread("public", users, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
private_long_thread = fn ->
|
private_long_thread = fn ->
|
||||||
generate_long_thread("private", user, friends_thread, non_friends_thread, opts)
|
generate_long_thread("private", users, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
iterations = opts[:iterations]
|
iterations = opts[:iterations]
|
||||||
|
@ -73,10 +77,10 @@ def generate(user, opts \\ []) do
|
||||||
i when i == iterations - 2 ->
|
i when i == iterations - 2 ->
|
||||||
spawn(public_long_thread)
|
spawn(public_long_thread)
|
||||||
spawn(private_long_thread)
|
spawn(private_long_thread)
|
||||||
generate_activities(user, friends, non_friends, Enum.shuffle(task_data), opts)
|
generate_activities(users, Enum.shuffle(task_data), opts)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
generate_activities(user, friends, non_friends, Enum.shuffle(task_data), opts)
|
generate_activities(users, Enum.shuffle(task_data), opts)
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
@ -127,16 +131,16 @@ def generate_tagged_activities(opts \\ []) do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp generate_long_thread(visibility, user, friends, non_friends, _opts) do
|
defp generate_long_thread(visibility, users, _opts) do
|
||||||
group =
|
group =
|
||||||
if visibility == "public",
|
if visibility == "public",
|
||||||
do: "friends",
|
do: :friends_local,
|
||||||
else: "user"
|
else: :user
|
||||||
|
|
||||||
tasks = get_reply_tasks(visibility, group) |> Stream.cycle() |> Enum.take(50)
|
tasks = get_reply_tasks(visibility, group) |> Stream.cycle() |> Enum.take(50)
|
||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
CommonAPI.post(user, %{
|
CommonAPI.post(users[:user], %{
|
||||||
status: "Start of #{visibility} long thread",
|
status: "Start of #{visibility} long thread",
|
||||||
visibility: visibility
|
visibility: visibility
|
||||||
})
|
})
|
||||||
|
@ -150,31 +154,28 @@ defp generate_long_thread(visibility, user, friends, non_friends, _opts) do
|
||||||
Map.put(state, key, activity)
|
Map.put(state, key, activity)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
acc = {activity.id, ["@" <> user.nickname, "reply to long thread"]}
|
acc = {activity.id, ["@" <> users[:user].nickname, "reply to long thread"]}
|
||||||
insert_replies_for_long_thread(tasks, visibility, user, friends, non_friends, acc)
|
insert_replies_for_long_thread(tasks, visibility, users, acc)
|
||||||
IO.puts("Generating #{visibility} long thread ended\n")
|
IO.puts("Generating #{visibility} long thread ended\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_replies_for_long_thread(tasks, visibility, user, friends, non_friends, acc) do
|
defp insert_replies_for_long_thread(tasks, visibility, users, acc) do
|
||||||
Enum.reduce(tasks, acc, fn
|
Enum.reduce(tasks, acc, fn
|
||||||
"friend", {id, data} ->
|
:user, {id, data} ->
|
||||||
friend = Enum.random(friends)
|
user = users[:user]
|
||||||
insert_reply(friend, List.delete(data, "@" <> friend.nickname), id, visibility)
|
|
||||||
|
|
||||||
"non_friend", {id, data} ->
|
|
||||||
non_friend = Enum.random(non_friends)
|
|
||||||
insert_reply(non_friend, List.delete(data, "@" <> non_friend.nickname), id, visibility)
|
|
||||||
|
|
||||||
"user", {id, data} ->
|
|
||||||
insert_reply(user, List.delete(data, "@" <> user.nickname), id, visibility)
|
insert_reply(user, List.delete(data, "@" <> user.nickname), id, visibility)
|
||||||
|
|
||||||
|
group, {id, data} ->
|
||||||
|
replier = Enum.random(users[group])
|
||||||
|
insert_reply(replier, List.delete(data, "@" <> replier.nickname), id, visibility)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp generate_activities(user, friends, non_friends, task_data, opts) do
|
defp generate_activities(users, task_data, opts) do
|
||||||
Task.async_stream(
|
Task.async_stream(
|
||||||
task_data,
|
task_data,
|
||||||
fn {visibility, type, group} ->
|
fn {visibility, type, group} ->
|
||||||
insert_activity(type, visibility, group, user, friends, non_friends, opts)
|
insert_activity(type, visibility, group, users, opts)
|
||||||
end,
|
end,
|
||||||
max_concurrency: @max_concurrency,
|
max_concurrency: @max_concurrency,
|
||||||
timeout: 30_000
|
timeout: 30_000
|
||||||
|
@ -182,47 +183,46 @@ defp generate_activities(user, friends, non_friends, task_data, opts) do
|
||||||
|> Stream.run()
|
|> Stream.run()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("simple", visibility, group, user, friends, non_friends, _opts) do
|
defp insert_local_activity(visibility, group, users, status) do
|
||||||
{:ok, _activity} =
|
{:ok, _} =
|
||||||
group
|
group
|
||||||
|> get_actor(user, friends, non_friends)
|
|> get_actor(users)
|
||||||
|> CommonAPI.post(%{status: "Simple status", visibility: visibility})
|
|> CommonAPI.post(%{status: status, visibility: visibility})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("emoji", visibility, group, user, friends, non_friends, _opts) do
|
defp insert_remote_activity(visibility, group, users, status) do
|
||||||
{:ok, _activity} =
|
actor = get_actor(group, users)
|
||||||
group
|
{act_data, obj_data} = prepare_activity_data(actor, visibility, users[:user])
|
||||||
|> get_actor(user, friends, non_friends)
|
{activity_data, object_data} = other_data(actor, status)
|
||||||
|> CommonAPI.post(%{
|
|
||||||
status: "Simple status with emoji :firefox:",
|
activity_data
|
||||||
visibility: visibility
|
|> Map.merge(act_data)
|
||||||
})
|
|> Map.put("object", Map.merge(object_data, obj_data))
|
||||||
|
|> Pleroma.Web.ActivityPub.ActivityPub.insert(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("mentions", visibility, group, user, friends, non_friends, _opts) do
|
defp user_mentions(users) do
|
||||||
user_mentions =
|
user_mentions =
|
||||||
get_random_mentions(friends, Enum.random(0..3)) ++
|
Enum.reduce(
|
||||||
get_random_mentions(non_friends, Enum.random(0..3))
|
@groups,
|
||||||
|
[],
|
||||||
|
fn group, acc ->
|
||||||
|
acc ++ get_random_mentions(users[group], Enum.random(0..2))
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
user_mentions =
|
|
||||||
if Enum.random([true, false]),
|
if Enum.random([true, false]),
|
||||||
do: ["@" <> user.nickname | user_mentions],
|
do: ["@" <> users[:user].nickname | user_mentions],
|
||||||
else: user_mentions
|
else: user_mentions
|
||||||
|
|
||||||
{:ok, _activity} =
|
|
||||||
group
|
|
||||||
|> get_actor(user, friends, non_friends)
|
|
||||||
|> CommonAPI.post(%{
|
|
||||||
status: Enum.join(user_mentions, ", ") <> " simple status with mentions",
|
|
||||||
visibility: visibility
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("hell_thread", visibility, group, user, friends, non_friends, _opts) do
|
defp hell_thread_mentions(users) do
|
||||||
mentions =
|
|
||||||
with {:ok, nil} <- Cachex.get(:user_cache, "hell_thread_mentions") do
|
with {:ok, nil} <- Cachex.get(:user_cache, "hell_thread_mentions") do
|
||||||
cached =
|
cached =
|
||||||
([user | Enum.take(friends, 10)] ++ Enum.take(non_friends, 10))
|
@groups
|
||||||
|
|> Enum.reduce([users[:user]], fn group, acc ->
|
||||||
|
acc ++ Enum.take(users[group], 5)
|
||||||
|
end)
|
||||||
|> Enum.map(&"@#{&1.nickname}")
|
|> Enum.map(&"@#{&1.nickname}")
|
||||||
|> Enum.join(", ")
|
|> Enum.join(", ")
|
||||||
|
|
||||||
|
@ -231,18 +231,56 @@ defp insert_activity("hell_thread", visibility, group, user, friends, non_friend
|
||||||
else
|
else
|
||||||
{:ok, cached} -> cached
|
{:ok, cached} -> cached
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok, _activity} =
|
|
||||||
group
|
|
||||||
|> get_actor(user, friends, non_friends)
|
|
||||||
|> CommonAPI.post(%{
|
|
||||||
status: mentions <> " hell thread status",
|
|
||||||
visibility: visibility
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("attachment", visibility, group, user, friends, non_friends, _opts) do
|
defp insert_activity(:simple, visibility, group, users, _opts)
|
||||||
actor = get_actor(group, user, friends, non_friends)
|
when group in @remote_groups do
|
||||||
|
insert_remote_activity(visibility, group, users, "Remote status")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:simple, visibility, group, users, _opts) do
|
||||||
|
insert_local_activity(visibility, group, users, "Simple status")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:emoji, visibility, group, users, _opts)
|
||||||
|
when group in @remote_groups do
|
||||||
|
insert_remote_activity(visibility, group, users, "Remote status with emoji :firefox:")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:emoji, visibility, group, users, _opts) do
|
||||||
|
insert_local_activity(visibility, group, users, "Simple status with emoji :firefox:")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:mentions, visibility, group, users, _opts)
|
||||||
|
when group in @remote_groups do
|
||||||
|
mentions = user_mentions(users)
|
||||||
|
|
||||||
|
status = Enum.join(mentions, ", ") <> " remote status with mentions"
|
||||||
|
|
||||||
|
insert_remote_activity(visibility, group, users, status)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:mentions, visibility, group, users, _opts) do
|
||||||
|
mentions = user_mentions(users)
|
||||||
|
|
||||||
|
status = Enum.join(mentions, ", ") <> " simple status with mentions"
|
||||||
|
insert_remote_activity(visibility, group, users, status)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:hell_thread, visibility, group, users, _)
|
||||||
|
when group in @remote_groups do
|
||||||
|
mentions = hell_thread_mentions(users)
|
||||||
|
insert_remote_activity(visibility, group, users, mentions <> " remote hell thread status")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:hell_thread, visibility, group, users, _opts) do
|
||||||
|
mentions = hell_thread_mentions(users)
|
||||||
|
|
||||||
|
insert_local_activity(visibility, group, users, mentions <> " hell thread status")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_activity(:attachment, visibility, group, users, _opts) do
|
||||||
|
actor = get_actor(group, users)
|
||||||
|
|
||||||
obj_data = %{
|
obj_data = %{
|
||||||
"actor" => actor.ap_id,
|
"actor" => actor.ap_id,
|
||||||
|
@ -268,67 +306,54 @@ defp insert_activity("attachment", visibility, group, user, friends, non_friends
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("tag", visibility, group, user, friends, non_friends, _opts) do
|
defp insert_activity(:tag, visibility, group, users, _opts) do
|
||||||
{:ok, _activity} =
|
insert_local_activity(visibility, group, users, "Status with #tag")
|
||||||
group
|
|
||||||
|> get_actor(user, friends, non_friends)
|
|
||||||
|> CommonAPI.post(%{status: "Status with #tag", visibility: visibility})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("like", visibility, group, user, friends, non_friends, opts) do
|
defp insert_activity(:like, visibility, group, users, opts) do
|
||||||
actor = get_actor(group, user, friends, non_friends)
|
actor = get_actor(group, users)
|
||||||
|
|
||||||
with activity_id when not is_nil(activity_id) <- get_random_create_activity_id(),
|
with activity_id when not is_nil(activity_id) <- get_random_create_activity_id(),
|
||||||
{:ok, _activity} <- CommonAPI.favorite(actor, activity_id) do
|
{:ok, _activity} <- CommonAPI.favorite(actor, activity_id) do
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
{:error, _} ->
|
{:error, _} ->
|
||||||
insert_activity("like", visibility, group, user, friends, non_friends, opts)
|
insert_activity(:like, visibility, group, users, opts)
|
||||||
|
|
||||||
nil ->
|
nil ->
|
||||||
Process.sleep(15)
|
Process.sleep(15)
|
||||||
insert_activity("like", visibility, group, user, friends, non_friends, opts)
|
insert_activity(:like, visibility, group, users, opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("reblog", visibility, group, user, friends, non_friends, opts) do
|
defp insert_activity(:reblog, visibility, group, users, opts) do
|
||||||
actor = get_actor(group, user, friends, non_friends)
|
actor = get_actor(group, users)
|
||||||
|
|
||||||
with activity_id when not is_nil(activity_id) <- get_random_create_activity_id(),
|
with activity_id when not is_nil(activity_id) <- get_random_create_activity_id(),
|
||||||
{:ok, _activity, _object} <- CommonAPI.repeat(activity_id, actor) do
|
{:ok, _activity} <- CommonAPI.repeat(activity_id, actor) do
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
{:error, _} ->
|
{:error, _} ->
|
||||||
insert_activity("reblog", visibility, group, user, friends, non_friends, opts)
|
insert_activity(:reblog, visibility, group, users, opts)
|
||||||
|
|
||||||
nil ->
|
nil ->
|
||||||
Process.sleep(15)
|
Process.sleep(15)
|
||||||
insert_activity("reblog", visibility, group, user, friends, non_friends, opts)
|
insert_activity(:reblog, visibility, group, users, opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("simple_thread", visibility, group, user, friends, non_friends, _opts)
|
defp insert_activity(:simple_thread, "direct", group, users, _opts) do
|
||||||
when visibility in ["public", "unlisted", "private"] do
|
actor = get_actor(group, users)
|
||||||
actor = get_actor(group, user, friends, non_friends)
|
|
||||||
tasks = get_reply_tasks(visibility, group)
|
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "Simple status", visibility: visibility})
|
|
||||||
|
|
||||||
acc = {activity.id, ["@" <> actor.nickname, "reply to status"]}
|
|
||||||
insert_replies(tasks, visibility, user, friends, non_friends, acc)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp insert_activity("simple_thread", "direct", group, user, friends, non_friends, _opts) do
|
|
||||||
actor = get_actor(group, user, friends, non_friends)
|
|
||||||
tasks = get_reply_tasks("direct", group)
|
tasks = get_reply_tasks("direct", group)
|
||||||
|
|
||||||
list =
|
list =
|
||||||
case group do
|
case group do
|
||||||
"non_friends" ->
|
:user ->
|
||||||
Enum.take(non_friends, 3)
|
group = Enum.random(@friends_groups)
|
||||||
|
Enum.take(users[group], 3)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
Enum.take(friends, 3)
|
Enum.take(users[group], 3)
|
||||||
end
|
end
|
||||||
|
|
||||||
data = Enum.map(list, &("@" <> &1.nickname))
|
data = Enum.map(list, &("@" <> &1.nickname))
|
||||||
|
@ -339,40 +364,30 @@ defp insert_activity("simple_thread", "direct", group, user, friends, non_friend
|
||||||
visibility: "direct"
|
visibility: "direct"
|
||||||
})
|
})
|
||||||
|
|
||||||
acc = {activity.id, ["@" <> user.nickname | data] ++ ["reply to status"]}
|
acc = {activity.id, ["@" <> users[:user].nickname | data] ++ ["reply to status"]}
|
||||||
insert_direct_replies(tasks, user, list, acc)
|
insert_direct_replies(tasks, users[:user], list, acc)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("remote", _, "user", _, _, _, _), do: :ok
|
defp insert_activity(:simple_thread, visibility, group, users, _opts) do
|
||||||
|
actor = get_actor(group, users)
|
||||||
|
tasks = get_reply_tasks(visibility, group)
|
||||||
|
|
||||||
defp insert_activity("remote", visibility, group, user, _friends, _non_friends, opts) do
|
{:ok, activity} =
|
||||||
remote_friends =
|
CommonAPI.post(users[:user], %{status: "Simple status", visibility: visibility})
|
||||||
Users.get_users(user, limit: opts[:friends_used], local: :external, friends?: true)
|
|
||||||
|
|
||||||
remote_non_friends =
|
acc = {activity.id, ["@" <> actor.nickname, "reply to status"]}
|
||||||
Users.get_users(user, limit: opts[:non_friends_used], local: :external, friends?: false)
|
insert_replies(tasks, visibility, users, acc)
|
||||||
|
|
||||||
actor = get_actor(group, user, remote_friends, remote_non_friends)
|
|
||||||
|
|
||||||
{act_data, obj_data} = prepare_activity_data(actor, visibility, user)
|
|
||||||
{activity_data, object_data} = other_data(actor)
|
|
||||||
|
|
||||||
activity_data
|
|
||||||
|> Map.merge(act_data)
|
|
||||||
|> Map.put("object", Map.merge(object_data, obj_data))
|
|
||||||
|> Pleroma.Web.ActivityPub.ActivityPub.insert(false)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_actor("user", user, _friends, _non_friends), do: user
|
defp get_actor(:user, %{user: user}), do: user
|
||||||
defp get_actor("friends", _user, friends, _non_friends), do: Enum.random(friends)
|
defp get_actor(group, users), do: Enum.random(users[group])
|
||||||
defp get_actor("non_friends", _user, _friends, non_friends), do: Enum.random(non_friends)
|
|
||||||
|
|
||||||
defp other_data(actor) do
|
defp other_data(actor, content) do
|
||||||
%{host: host} = URI.parse(actor.ap_id)
|
%{host: host} = URI.parse(actor.ap_id)
|
||||||
datetime = DateTime.utc_now()
|
datetime = DateTime.utc_now()
|
||||||
context_id = "http://#{host}:4000/contexts/#{UUID.generate()}"
|
context_id = "https://#{host}/contexts/#{UUID.generate()}"
|
||||||
activity_id = "http://#{host}:4000/activities/#{UUID.generate()}"
|
activity_id = "https://#{host}/activities/#{UUID.generate()}"
|
||||||
object_id = "http://#{host}:4000/objects/#{UUID.generate()}"
|
object_id = "https://#{host}/objects/#{UUID.generate()}"
|
||||||
|
|
||||||
activity_data = %{
|
activity_data = %{
|
||||||
"actor" => actor.ap_id,
|
"actor" => actor.ap_id,
|
||||||
|
@ -389,7 +404,7 @@ defp other_data(actor) do
|
||||||
"attributedTo" => actor.ap_id,
|
"attributedTo" => actor.ap_id,
|
||||||
"bcc" => [],
|
"bcc" => [],
|
||||||
"bto" => [],
|
"bto" => [],
|
||||||
"content" => "Remote post",
|
"content" => content,
|
||||||
"context" => context_id,
|
"context" => context_id,
|
||||||
"conversation" => context_id,
|
"conversation" => context_id,
|
||||||
"emoji" => %{},
|
"emoji" => %{},
|
||||||
|
@ -475,51 +490,65 @@ defp prepare_activity_data(_actor, "direct", mention) do
|
||||||
{act_data, obj_data}
|
{act_data, obj_data}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_reply_tasks("public", "user"), do: ~w(friend non_friend user)
|
defp get_reply_tasks("public", :user) do
|
||||||
defp get_reply_tasks("public", "friends"), do: ~w(non_friend user friend)
|
[:friends_local, :friends_remote, :non_friends_local, :non_friends_remote, :user]
|
||||||
defp get_reply_tasks("public", "non_friends"), do: ~w(user friend non_friend)
|
end
|
||||||
|
|
||||||
defp get_reply_tasks(visibility, "user") when visibility in ["unlisted", "private"],
|
defp get_reply_tasks("public", group) when group in @friends_groups do
|
||||||
do: ~w(friend user friend)
|
[:non_friends_local, :non_friends_remote, :user, :friends_local, :friends_remote]
|
||||||
|
end
|
||||||
|
|
||||||
defp get_reply_tasks(visibility, "friends") when visibility in ["unlisted", "private"],
|
defp get_reply_tasks("public", group) when group in @non_friends_groups do
|
||||||
do: ~w(user friend user)
|
[:user, :friends_local, :friends_remote, :non_friends_local, :non_friends_remote]
|
||||||
|
end
|
||||||
|
|
||||||
defp get_reply_tasks(visibility, "non_friends") when visibility in ["unlisted", "private"],
|
defp get_reply_tasks(visibility, :user) when visibility in ["unlisted", "private"] do
|
||||||
|
[:friends_local, :friends_remote, :user, :friends_local, :friends_remote]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_reply_tasks(visibility, group)
|
||||||
|
when visibility in ["unlisted", "private"] and group in @friends_groups do
|
||||||
|
[:user, :friends_remote, :friends_local, :user]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_reply_tasks(visibility, group)
|
||||||
|
when visibility in ["unlisted", "private"] and
|
||||||
|
group in @non_friends_groups,
|
||||||
do: []
|
do: []
|
||||||
|
|
||||||
defp get_reply_tasks("direct", "user"), do: ~w(friend user friend)
|
defp get_reply_tasks("direct", :user), do: [:friends_local, :user, :friends_remote]
|
||||||
defp get_reply_tasks("direct", "friends"), do: ~w(user friend user)
|
|
||||||
defp get_reply_tasks("direct", "non_friends"), do: ~w(user non_friend user)
|
|
||||||
|
|
||||||
defp insert_replies(tasks, visibility, user, friends, non_friends, acc) do
|
defp get_reply_tasks("direct", group) when group in @friends_groups,
|
||||||
|
do: [:user, group, :user]
|
||||||
|
|
||||||
|
defp get_reply_tasks("direct", group) when group in @non_friends_groups do
|
||||||
|
[:user, :non_friends_remote, :user, :non_friends_local]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp insert_replies(tasks, visibility, users, acc) do
|
||||||
Enum.reduce(tasks, acc, fn
|
Enum.reduce(tasks, acc, fn
|
||||||
"friend", {id, data} ->
|
:user, {id, data} ->
|
||||||
friend = Enum.random(friends)
|
insert_reply(users[:user], data, id, visibility)
|
||||||
insert_reply(friend, data, id, visibility)
|
|
||||||
|
|
||||||
"non_friend", {id, data} ->
|
group, {id, data} ->
|
||||||
non_friend = Enum.random(non_friends)
|
replier = Enum.random(users[group])
|
||||||
insert_reply(non_friend, data, id, visibility)
|
insert_reply(replier, data, id, visibility)
|
||||||
|
|
||||||
"user", {id, data} ->
|
|
||||||
insert_reply(user, data, id, visibility)
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_direct_replies(tasks, user, list, acc) do
|
defp insert_direct_replies(tasks, user, list, acc) do
|
||||||
Enum.reduce(tasks, acc, fn
|
Enum.reduce(tasks, acc, fn
|
||||||
group, {id, data} when group in ["friend", "non_friend"] ->
|
:user, {id, data} ->
|
||||||
|
{reply_id, _} = insert_reply(user, List.delete(data, "@" <> user.nickname), id, "direct")
|
||||||
|
{reply_id, data}
|
||||||
|
|
||||||
|
_, {id, data} ->
|
||||||
actor = Enum.random(list)
|
actor = Enum.random(list)
|
||||||
|
|
||||||
{reply_id, _} =
|
{reply_id, _} =
|
||||||
insert_reply(actor, List.delete(data, "@" <> actor.nickname), id, "direct")
|
insert_reply(actor, List.delete(data, "@" <> actor.nickname), id, "direct")
|
||||||
|
|
||||||
{reply_id, data}
|
{reply_id, data}
|
||||||
|
|
||||||
"user", {id, data} ->
|
|
||||||
{reply_id, _} = insert_reply(user, List.delete(data, "@" <> user.nickname), id, "direct")
|
|
||||||
{reply_id, data}
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ defp fetch_timelines(user) do
|
||||||
fetch_home_timeline(user)
|
fetch_home_timeline(user)
|
||||||
fetch_direct_timeline(user)
|
fetch_direct_timeline(user)
|
||||||
fetch_public_timeline(user)
|
fetch_public_timeline(user)
|
||||||
|
fetch_public_timeline(user, :with_blocks)
|
||||||
fetch_public_timeline(user, :local)
|
fetch_public_timeline(user, :local)
|
||||||
fetch_public_timeline(user, :tag)
|
fetch_public_timeline(user, :tag)
|
||||||
fetch_notifications(user)
|
fetch_notifications(user)
|
||||||
|
@ -51,12 +52,12 @@ defp render_views(user) do
|
||||||
|
|
||||||
defp opts_for_home_timeline(user) do
|
defp opts_for_home_timeline(user) do
|
||||||
%{
|
%{
|
||||||
"blocking_user" => user,
|
blocking_user: user,
|
||||||
"count" => "20",
|
count: "20",
|
||||||
"muting_user" => user,
|
muting_user: user,
|
||||||
"type" => ["Create", "Announce"],
|
type: ["Create", "Announce"],
|
||||||
"user" => user,
|
user: user,
|
||||||
"with_muted" => "true"
|
with_muted: true
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -69,17 +70,17 @@ defp fetch_home_timeline(user) do
|
||||||
ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse() |> List.last()
|
ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse() |> List.last()
|
||||||
|
|
||||||
second_page_last =
|
second_page_last =
|
||||||
ActivityPub.fetch_activities(recipients, Map.put(opts, "max_id", first_page_last.id))
|
ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, first_page_last.id))
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|> List.last()
|
|> List.last()
|
||||||
|
|
||||||
third_page_last =
|
third_page_last =
|
||||||
ActivityPub.fetch_activities(recipients, Map.put(opts, "max_id", second_page_last.id))
|
ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, second_page_last.id))
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|> List.last()
|
|> List.last()
|
||||||
|
|
||||||
forth_page_last =
|
forth_page_last =
|
||||||
ActivityPub.fetch_activities(recipients, Map.put(opts, "max_id", third_page_last.id))
|
ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, third_page_last.id))
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|> List.last()
|
|> List.last()
|
||||||
|
|
||||||
|
@ -89,19 +90,19 @@ defp fetch_home_timeline(user) do
|
||||||
},
|
},
|
||||||
inputs: %{
|
inputs: %{
|
||||||
"1 page" => opts,
|
"1 page" => opts,
|
||||||
"2 page" => Map.put(opts, "max_id", first_page_last.id),
|
"2 page" => Map.put(opts, :max_id, first_page_last.id),
|
||||||
"3 page" => Map.put(opts, "max_id", second_page_last.id),
|
"3 page" => Map.put(opts, :max_id, second_page_last.id),
|
||||||
"4 page" => Map.put(opts, "max_id", third_page_last.id),
|
"4 page" => Map.put(opts, :max_id, third_page_last.id),
|
||||||
"5 page" => Map.put(opts, "max_id", forth_page_last.id),
|
"5 page" => Map.put(opts, :max_id, forth_page_last.id),
|
||||||
"1 page only media" => Map.put(opts, "only_media", "true"),
|
"1 page only media" => Map.put(opts, :only_media, true),
|
||||||
"2 page only media" =>
|
"2 page only media" =>
|
||||||
Map.put(opts, "max_id", first_page_last.id) |> Map.put("only_media", "true"),
|
Map.put(opts, :max_id, first_page_last.id) |> Map.put(:only_media, true),
|
||||||
"3 page only media" =>
|
"3 page only media" =>
|
||||||
Map.put(opts, "max_id", second_page_last.id) |> Map.put("only_media", "true"),
|
Map.put(opts, :max_id, second_page_last.id) |> Map.put(:only_media, true),
|
||||||
"4 page only media" =>
|
"4 page only media" =>
|
||||||
Map.put(opts, "max_id", third_page_last.id) |> Map.put("only_media", "true"),
|
Map.put(opts, :max_id, third_page_last.id) |> Map.put(:only_media, true),
|
||||||
"5 page only media" =>
|
"5 page only media" =>
|
||||||
Map.put(opts, "max_id", forth_page_last.id) |> Map.put("only_media", "true")
|
Map.put(opts, :max_id, forth_page_last.id) |> Map.put(:only_media, true)
|
||||||
},
|
},
|
||||||
formatters: formatters()
|
formatters: formatters()
|
||||||
)
|
)
|
||||||
|
@ -109,12 +110,12 @@ defp fetch_home_timeline(user) do
|
||||||
|
|
||||||
defp opts_for_direct_timeline(user) do
|
defp opts_for_direct_timeline(user) do
|
||||||
%{
|
%{
|
||||||
:visibility => "direct",
|
visibility: "direct",
|
||||||
"blocking_user" => user,
|
blocking_user: user,
|
||||||
"count" => "20",
|
count: "20",
|
||||||
"type" => "Create",
|
type: "Create",
|
||||||
"user" => user,
|
user: user,
|
||||||
"with_muted" => "true"
|
with_muted: true
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -129,7 +130,7 @@ defp fetch_direct_timeline(user) do
|
||||||
|> Pagination.fetch_paginated(opts)
|
|> Pagination.fetch_paginated(opts)
|
||||||
|> List.last()
|
|> List.last()
|
||||||
|
|
||||||
opts2 = Map.put(opts, "max_id", first_page_last.id)
|
opts2 = Map.put(opts, :max_id, first_page_last.id)
|
||||||
|
|
||||||
second_page_last =
|
second_page_last =
|
||||||
recipients
|
recipients
|
||||||
|
@ -137,7 +138,7 @@ defp fetch_direct_timeline(user) do
|
||||||
|> Pagination.fetch_paginated(opts2)
|
|> Pagination.fetch_paginated(opts2)
|
||||||
|> List.last()
|
|> List.last()
|
||||||
|
|
||||||
opts3 = Map.put(opts, "max_id", second_page_last.id)
|
opts3 = Map.put(opts, :max_id, second_page_last.id)
|
||||||
|
|
||||||
third_page_last =
|
third_page_last =
|
||||||
recipients
|
recipients
|
||||||
|
@ -145,7 +146,7 @@ defp fetch_direct_timeline(user) do
|
||||||
|> Pagination.fetch_paginated(opts3)
|
|> Pagination.fetch_paginated(opts3)
|
||||||
|> List.last()
|
|> List.last()
|
||||||
|
|
||||||
opts4 = Map.put(opts, "max_id", third_page_last.id)
|
opts4 = Map.put(opts, :max_id, third_page_last.id)
|
||||||
|
|
||||||
forth_page_last =
|
forth_page_last =
|
||||||
recipients
|
recipients
|
||||||
|
@ -164,7 +165,7 @@ defp fetch_direct_timeline(user) do
|
||||||
"2 page" => opts2,
|
"2 page" => opts2,
|
||||||
"3 page" => opts3,
|
"3 page" => opts3,
|
||||||
"4 page" => opts4,
|
"4 page" => opts4,
|
||||||
"5 page" => Map.put(opts4, "max_id", forth_page_last.id)
|
"5 page" => Map.put(opts4, :max_id, forth_page_last.id)
|
||||||
},
|
},
|
||||||
formatters: formatters()
|
formatters: formatters()
|
||||||
)
|
)
|
||||||
|
@ -172,34 +173,34 @@ defp fetch_direct_timeline(user) do
|
||||||
|
|
||||||
defp opts_for_public_timeline(user) do
|
defp opts_for_public_timeline(user) do
|
||||||
%{
|
%{
|
||||||
"type" => ["Create", "Announce"],
|
type: ["Create", "Announce"],
|
||||||
"local_only" => false,
|
local_only: false,
|
||||||
"blocking_user" => user,
|
blocking_user: user,
|
||||||
"muting_user" => user
|
muting_user: user
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp opts_for_public_timeline(user, :local) do
|
defp opts_for_public_timeline(user, :local) do
|
||||||
%{
|
%{
|
||||||
"type" => ["Create", "Announce"],
|
type: ["Create", "Announce"],
|
||||||
"local_only" => true,
|
local_only: true,
|
||||||
"blocking_user" => user,
|
blocking_user: user,
|
||||||
"muting_user" => user
|
muting_user: user
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp opts_for_public_timeline(user, :tag) do
|
defp opts_for_public_timeline(user, :tag) do
|
||||||
%{
|
%{
|
||||||
"blocking_user" => user,
|
blocking_user: user,
|
||||||
"count" => "20",
|
count: "20",
|
||||||
"local_only" => nil,
|
local_only: nil,
|
||||||
"muting_user" => user,
|
muting_user: user,
|
||||||
"tag" => ["tag"],
|
tag: ["tag"],
|
||||||
"tag_all" => [],
|
tag_all: [],
|
||||||
"tag_reject" => [],
|
tag_reject: [],
|
||||||
"type" => "Create",
|
type: "Create",
|
||||||
"user" => user,
|
user: user,
|
||||||
"with_muted" => "true"
|
with_muted: true
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -222,24 +223,72 @@ defp fetch_public_timeline(user, :tag) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fetch_public_timeline(user, :only_media) do
|
defp fetch_public_timeline(user, :only_media) do
|
||||||
opts = opts_for_public_timeline(user) |> Map.put("only_media", "true")
|
opts = opts_for_public_timeline(user) |> Map.put(:only_media, true)
|
||||||
|
|
||||||
fetch_public_timeline(opts, "public timeline only media")
|
fetch_public_timeline(opts, "public timeline only media")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp fetch_public_timeline(user, :with_blocks) do
|
||||||
|
opts = opts_for_public_timeline(user)
|
||||||
|
|
||||||
|
remote_non_friends = Agent.get(:non_friends_remote, & &1)
|
||||||
|
|
||||||
|
Benchee.run(%{
|
||||||
|
"public timeline without blocks" => fn ->
|
||||||
|
ActivityPub.fetch_public_activities(opts)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
Enum.each(remote_non_friends, fn non_friend ->
|
||||||
|
{:ok, _} = User.block(user, non_friend)
|
||||||
|
end)
|
||||||
|
|
||||||
|
user = User.get_by_id(user.id)
|
||||||
|
|
||||||
|
opts = Map.put(opts, :blocking_user, user)
|
||||||
|
|
||||||
|
Benchee.run(%{
|
||||||
|
"public timeline with user block" => fn ->
|
||||||
|
ActivityPub.fetch_public_activities(opts)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
domains =
|
||||||
|
Enum.reduce(remote_non_friends, [], fn non_friend, domains ->
|
||||||
|
{:ok, _user} = User.unblock(user, non_friend)
|
||||||
|
%{host: host} = URI.parse(non_friend.ap_id)
|
||||||
|
[host | domains]
|
||||||
|
end)
|
||||||
|
|
||||||
|
domains = Enum.uniq(domains)
|
||||||
|
|
||||||
|
Enum.each(domains, fn domain ->
|
||||||
|
{:ok, _} = User.block_domain(user, domain)
|
||||||
|
end)
|
||||||
|
|
||||||
|
user = User.get_by_id(user.id)
|
||||||
|
opts = Map.put(opts, :blocking_user, user)
|
||||||
|
|
||||||
|
Benchee.run(%{
|
||||||
|
"public timeline with domain block" => fn ->
|
||||||
|
ActivityPub.fetch_public_activities(opts)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
defp fetch_public_timeline(opts, title) when is_binary(title) do
|
defp fetch_public_timeline(opts, title) when is_binary(title) do
|
||||||
first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
|
first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
|
||||||
|
|
||||||
second_page_last =
|
second_page_last =
|
||||||
ActivityPub.fetch_public_activities(Map.put(opts, "max_id", first_page_last.id))
|
ActivityPub.fetch_public_activities(Map.put(opts, :max_id, first_page_last.id))
|
||||||
|> List.last()
|
|> List.last()
|
||||||
|
|
||||||
third_page_last =
|
third_page_last =
|
||||||
ActivityPub.fetch_public_activities(Map.put(opts, "max_id", second_page_last.id))
|
ActivityPub.fetch_public_activities(Map.put(opts, :max_id, second_page_last.id))
|
||||||
|> List.last()
|
|> List.last()
|
||||||
|
|
||||||
forth_page_last =
|
forth_page_last =
|
||||||
ActivityPub.fetch_public_activities(Map.put(opts, "max_id", third_page_last.id))
|
ActivityPub.fetch_public_activities(Map.put(opts, :max_id, third_page_last.id))
|
||||||
|> List.last()
|
|> List.last()
|
||||||
|
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
|
@ -250,17 +299,17 @@ defp fetch_public_timeline(opts, title) when is_binary(title) do
|
||||||
},
|
},
|
||||||
inputs: %{
|
inputs: %{
|
||||||
"1 page" => opts,
|
"1 page" => opts,
|
||||||
"2 page" => Map.put(opts, "max_id", first_page_last.id),
|
"2 page" => Map.put(opts, :max_id, first_page_last.id),
|
||||||
"3 page" => Map.put(opts, "max_id", second_page_last.id),
|
"3 page" => Map.put(opts, :max_id, second_page_last.id),
|
||||||
"4 page" => Map.put(opts, "max_id", third_page_last.id),
|
"4 page" => Map.put(opts, :max_id, third_page_last.id),
|
||||||
"5 page" => Map.put(opts, "max_id", forth_page_last.id)
|
"5 page" => Map.put(opts, :max_id, forth_page_last.id)
|
||||||
},
|
},
|
||||||
formatters: formatters()
|
formatters: formatters()
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp opts_for_notifications do
|
defp opts_for_notifications do
|
||||||
%{"count" => "20", "with_muted" => "true"}
|
%{count: "20", with_muted: true}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fetch_notifications(user) do
|
defp fetch_notifications(user) do
|
||||||
|
@ -269,15 +318,15 @@ defp fetch_notifications(user) do
|
||||||
first_page_last = MastodonAPI.get_notifications(user, opts) |> List.last()
|
first_page_last = MastodonAPI.get_notifications(user, opts) |> List.last()
|
||||||
|
|
||||||
second_page_last =
|
second_page_last =
|
||||||
MastodonAPI.get_notifications(user, Map.put(opts, "max_id", first_page_last.id))
|
MastodonAPI.get_notifications(user, Map.put(opts, :max_id, first_page_last.id))
|
||||||
|> List.last()
|
|> List.last()
|
||||||
|
|
||||||
third_page_last =
|
third_page_last =
|
||||||
MastodonAPI.get_notifications(user, Map.put(opts, "max_id", second_page_last.id))
|
MastodonAPI.get_notifications(user, Map.put(opts, :max_id, second_page_last.id))
|
||||||
|> List.last()
|
|> List.last()
|
||||||
|
|
||||||
forth_page_last =
|
forth_page_last =
|
||||||
MastodonAPI.get_notifications(user, Map.put(opts, "max_id", third_page_last.id))
|
MastodonAPI.get_notifications(user, Map.put(opts, :max_id, third_page_last.id))
|
||||||
|> List.last()
|
|> List.last()
|
||||||
|
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
|
@ -288,10 +337,10 @@ defp fetch_notifications(user) do
|
||||||
},
|
},
|
||||||
inputs: %{
|
inputs: %{
|
||||||
"1 page" => opts,
|
"1 page" => opts,
|
||||||
"2 page" => Map.put(opts, "max_id", first_page_last.id),
|
"2 page" => Map.put(opts, :max_id, first_page_last.id),
|
||||||
"3 page" => Map.put(opts, "max_id", second_page_last.id),
|
"3 page" => Map.put(opts, :max_id, second_page_last.id),
|
||||||
"4 page" => Map.put(opts, "max_id", third_page_last.id),
|
"4 page" => Map.put(opts, :max_id, third_page_last.id),
|
||||||
"5 page" => Map.put(opts, "max_id", forth_page_last.id)
|
"5 page" => Map.put(opts, :max_id, forth_page_last.id)
|
||||||
},
|
},
|
||||||
formatters: formatters()
|
formatters: formatters()
|
||||||
)
|
)
|
||||||
|
@ -301,13 +350,13 @@ defp fetch_favourites(user) do
|
||||||
first_page_last = ActivityPub.fetch_favourites(user) |> List.last()
|
first_page_last = ActivityPub.fetch_favourites(user) |> List.last()
|
||||||
|
|
||||||
second_page_last =
|
second_page_last =
|
||||||
ActivityPub.fetch_favourites(user, %{"max_id" => first_page_last.id}) |> List.last()
|
ActivityPub.fetch_favourites(user, %{:max_id => first_page_last.id}) |> List.last()
|
||||||
|
|
||||||
third_page_last =
|
third_page_last =
|
||||||
ActivityPub.fetch_favourites(user, %{"max_id" => second_page_last.id}) |> List.last()
|
ActivityPub.fetch_favourites(user, %{:max_id => second_page_last.id}) |> List.last()
|
||||||
|
|
||||||
forth_page_last =
|
forth_page_last =
|
||||||
ActivityPub.fetch_favourites(user, %{"max_id" => third_page_last.id}) |> List.last()
|
ActivityPub.fetch_favourites(user, %{:max_id => third_page_last.id}) |> List.last()
|
||||||
|
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
%{
|
%{
|
||||||
|
@ -317,10 +366,10 @@ defp fetch_favourites(user) do
|
||||||
},
|
},
|
||||||
inputs: %{
|
inputs: %{
|
||||||
"1 page" => %{},
|
"1 page" => %{},
|
||||||
"2 page" => %{"max_id" => first_page_last.id},
|
"2 page" => %{:max_id => first_page_last.id},
|
||||||
"3 page" => %{"max_id" => second_page_last.id},
|
"3 page" => %{:max_id => second_page_last.id},
|
||||||
"4 page" => %{"max_id" => third_page_last.id},
|
"4 page" => %{:max_id => third_page_last.id},
|
||||||
"5 page" => %{"max_id" => forth_page_last.id}
|
"5 page" => %{:max_id => forth_page_last.id}
|
||||||
},
|
},
|
||||||
formatters: formatters()
|
formatters: formatters()
|
||||||
)
|
)
|
||||||
|
@ -328,8 +377,8 @@ defp fetch_favourites(user) do
|
||||||
|
|
||||||
defp opts_for_long_thread(user) do
|
defp opts_for_long_thread(user) do
|
||||||
%{
|
%{
|
||||||
"blocking_user" => user,
|
blocking_user: user,
|
||||||
"user" => user
|
user: user
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -339,9 +388,9 @@ defp fetch_long_thread(user) do
|
||||||
|
|
||||||
opts = opts_for_long_thread(user)
|
opts = opts_for_long_thread(user)
|
||||||
|
|
||||||
private_input = {private.data["context"], Map.put(opts, "exclude_id", private.id)}
|
private_input = {private.data["context"], Map.put(opts, :exclude_id, private.id)}
|
||||||
|
|
||||||
public_input = {public.data["context"], Map.put(opts, "exclude_id", public.id)}
|
public_input = {public.data["context"], Map.put(opts, :exclude_id, public.id)}
|
||||||
|
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
%{
|
%{
|
||||||
|
@ -461,13 +510,13 @@ defp render_long_thread(user) do
|
||||||
public_context =
|
public_context =
|
||||||
ActivityPub.fetch_activities_for_context(
|
ActivityPub.fetch_activities_for_context(
|
||||||
public.data["context"],
|
public.data["context"],
|
||||||
Map.put(fetch_opts, "exclude_id", public.id)
|
Map.put(fetch_opts, :exclude_id, public.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
private_context =
|
private_context =
|
||||||
ActivityPub.fetch_activities_for_context(
|
ActivityPub.fetch_activities_for_context(
|
||||||
private.data["context"],
|
private.data["context"],
|
||||||
Map.put(fetch_opts, "exclude_id", private.id)
|
Map.put(fetch_opts, :exclude_id, private.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
|
@ -498,14 +547,14 @@ defp fetch_timelines_with_reply_filtering(user) do
|
||||||
end,
|
end,
|
||||||
"Public timeline with reply filtering - following" => fn ->
|
"Public timeline with reply filtering - following" => fn ->
|
||||||
public_params
|
public_params
|
||||||
|> Map.put("reply_visibility", "following")
|
|> Map.put(:reply_visibility, "following")
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
end,
|
end,
|
||||||
"Public timeline with reply filtering - self" => fn ->
|
"Public timeline with reply filtering - self" => fn ->
|
||||||
public_params
|
public_params
|
||||||
|> Map.put("reply_visibility", "self")
|
|> Map.put(:reply_visibility, "self")
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
@ -524,16 +573,16 @@ defp fetch_timelines_with_reply_filtering(user) do
|
||||||
"Home timeline with reply filtering - following" => fn ->
|
"Home timeline with reply filtering - following" => fn ->
|
||||||
private_params =
|
private_params =
|
||||||
private_params
|
private_params
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|> Map.put("reply_visibility", "following")
|
|> Map.put(:reply_visibility, "following")
|
||||||
|
|
||||||
ActivityPub.fetch_activities(recipients, private_params)
|
ActivityPub.fetch_activities(recipients, private_params)
|
||||||
end,
|
end,
|
||||||
"Home timeline with reply filtering - self" => fn ->
|
"Home timeline with reply filtering - self" => fn ->
|
||||||
private_params =
|
private_params =
|
||||||
private_params
|
private_params
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|> Map.put("reply_visibility", "self")
|
|> Map.put(:reply_visibility, "self")
|
||||||
|
|
||||||
ActivityPub.fetch_activities(recipients, private_params)
|
ActivityPub.fetch_activities(recipients, private_params)
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,7 +27,7 @@ def generate(opts \\ []) do
|
||||||
|
|
||||||
make_friends(main_user, opts[:friends])
|
make_friends(main_user, opts[:friends])
|
||||||
|
|
||||||
Repo.get(User, main_user.id)
|
User.get_by_id(main_user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_users(max) do
|
def generate_users(max) do
|
||||||
|
@ -166,4 +166,24 @@ defp run_stream(users, main_user) do
|
||||||
)
|
)
|
||||||
|> Stream.run()
|
|> Stream.run()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec prepare_users(User.t(), keyword()) :: map()
|
||||||
|
def prepare_users(user, opts) do
|
||||||
|
friends_limit = opts[:friends_used]
|
||||||
|
non_friends_limit = opts[:non_friends_used]
|
||||||
|
|
||||||
|
%{
|
||||||
|
user: user,
|
||||||
|
friends_local: fetch_users(user, friends_limit, :local, true),
|
||||||
|
friends_remote: fetch_users(user, friends_limit, :external, true),
|
||||||
|
non_friends_local: fetch_users(user, non_friends_limit, :local, false),
|
||||||
|
non_friends_remote: fetch_users(user, non_friends_limit, :external, false)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_users(user, limit, local, friends?) do
|
||||||
|
user
|
||||||
|
|> get_users(limit: limit, local: local, friends?: friends?)
|
||||||
|
|> Enum.shuffle()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,6 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Tags do
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.MastodonAPI.TimelineController
|
|
||||||
|
|
||||||
def run(_args) do
|
def run(_args) do
|
||||||
Mix.Pleroma.start_pleroma()
|
Mix.Pleroma.start_pleroma()
|
||||||
|
@ -37,7 +36,7 @@ def run(_args) do
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
%{
|
%{
|
||||||
"Hashtag fetching, any" => fn tags ->
|
"Hashtag fetching, any" => fn tags ->
|
||||||
TimelineController.hashtag_fetching(
|
hashtag_fetching(
|
||||||
%{
|
%{
|
||||||
"any" => tags
|
"any" => tags
|
||||||
},
|
},
|
||||||
|
@ -47,7 +46,7 @@ def run(_args) do
|
||||||
end,
|
end,
|
||||||
# Will always return zero results because no overlapping hashtags are generated.
|
# Will always return zero results because no overlapping hashtags are generated.
|
||||||
"Hashtag fetching, all" => fn tags ->
|
"Hashtag fetching, all" => fn tags ->
|
||||||
TimelineController.hashtag_fetching(
|
hashtag_fetching(
|
||||||
%{
|
%{
|
||||||
"all" => tags
|
"all" => tags
|
||||||
},
|
},
|
||||||
|
@ -67,7 +66,7 @@ def run(_args) do
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
%{
|
%{
|
||||||
"Hashtag fetching" => fn tag ->
|
"Hashtag fetching" => fn tag ->
|
||||||
TimelineController.hashtag_fetching(
|
hashtag_fetching(
|
||||||
%{
|
%{
|
||||||
"tag" => tag
|
"tag" => tag
|
||||||
},
|
},
|
||||||
|
@ -80,4 +79,35 @@ def run(_args) do
|
||||||
time: 5
|
time: 5
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp hashtag_fetching(params, user, local_only) do
|
||||||
|
tags =
|
||||||
|
[params["tag"], params["any"]]
|
||||||
|
|> List.flatten()
|
||||||
|
|> Enum.uniq()
|
||||||
|
|> Enum.filter(& &1)
|
||||||
|
|> Enum.map(&String.downcase(&1))
|
||||||
|
|
||||||
|
tag_all =
|
||||||
|
params
|
||||||
|
|> Map.get("all", [])
|
||||||
|
|> Enum.map(&String.downcase(&1))
|
||||||
|
|
||||||
|
tag_reject =
|
||||||
|
params
|
||||||
|
|> Map.get("none", [])
|
||||||
|
|> Enum.map(&String.downcase(&1))
|
||||||
|
|
||||||
|
_activities =
|
||||||
|
params
|
||||||
|
|> Map.put(:type, "Create")
|
||||||
|
|> Map.put(:local_only, local_only)
|
||||||
|
|> Map.put(:blocking_user, user)
|
||||||
|
|> Map.put(:muting_user, user)
|
||||||
|
|> Map.put(:user, user)
|
||||||
|
|> Map.put(:tag, tags)
|
||||||
|
|> Map.put(:tag_all, tag_all)
|
||||||
|
|> Map.put(:tag_reject, tag_reject)
|
||||||
|
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -171,7 +171,8 @@
|
||||||
"application/ld+json" => ["activity+json"]
|
"application/ld+json" => ["activity+json"]
|
||||||
}
|
}
|
||||||
|
|
||||||
config :tesla, adapter: Tesla.Adapter.Gun
|
config :tesla, adapter: Tesla.Adapter.Hackney
|
||||||
|
|
||||||
# Configures http settings, upstream proxy etc.
|
# Configures http settings, upstream proxy etc.
|
||||||
config :pleroma, :http,
|
config :pleroma, :http,
|
||||||
proxy_url: nil,
|
proxy_url: nil,
|
||||||
|
@ -183,7 +184,7 @@
|
||||||
name: "Pleroma",
|
name: "Pleroma",
|
||||||
email: "example@example.com",
|
email: "example@example.com",
|
||||||
notify_email: "noreply@example.com",
|
notify_email: "noreply@example.com",
|
||||||
description: "A Pleroma instance, an alternative fediverse server",
|
description: "Pleroma: An efficient and flexible fediverse server",
|
||||||
background_image: "/images/city.jpg",
|
background_image: "/images/city.jpg",
|
||||||
limit: 5_000,
|
limit: 5_000,
|
||||||
chat_limit: 5_000,
|
chat_limit: 5_000,
|
||||||
|
|
|
@ -547,7 +547,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"totalReports" : 1,
|
"total" : 1,
|
||||||
"reports": [
|
"reports": [
|
||||||
{
|
{
|
||||||
"account": {
|
"account": {
|
||||||
|
@ -768,7 +768,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- 400 Bad Request `"Invalid parameters"` when `status` is missing
|
- 400 Bad Request `"Invalid parameters"` when `status` is missing
|
||||||
- On success: `204`, empty response
|
- On success: `204`, empty response
|
||||||
|
|
||||||
## `POST /api/pleroma/admin/reports/:report_id/notes/:id`
|
## `DELETE /api/pleroma/admin/reports/:report_id/notes/:id`
|
||||||
|
|
||||||
### Delete report note
|
### Delete report note
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,12 @@ Feel free to contact us to be added to this list!
|
||||||
- Platforms: SailfishOS
|
- Platforms: SailfishOS
|
||||||
- Features: No Streaming
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### Husky
|
||||||
|
- Source code: <https://git.mentality.rip/FWGS/Husky>
|
||||||
|
- Contact: [@Husky@enigmatic.observer](https://enigmatic.observer/users/Husky)
|
||||||
|
- Platforms: Android
|
||||||
|
- Features: No Streaming, Emoji Reactions, Text Formatting, FE Stickers
|
||||||
|
|
||||||
### Nekonium
|
### Nekonium
|
||||||
- Homepage: [F-Droid Repository](https://repo.gdgd.jp.net/), [Google Play](https://play.google.com/store/apps/details?id=com.apps.nekonium), [Amazon](https://www.amazon.co.jp/dp/B076FXPRBC/)
|
- Homepage: [F-Droid Repository](https://repo.gdgd.jp.net/), [Google Play](https://play.google.com/store/apps/details?id=com.apps.nekonium), [Amazon](https://www.amazon.co.jp/dp/B076FXPRBC/)
|
||||||
- Source: <https://gogs.gdgd.jp.net/lin/nekonium>
|
- Source: <https://gogs.gdgd.jp.net/lin/nekonium>
|
||||||
|
|
|
@ -92,10 +92,10 @@ def handle_command(state, "home") do
|
||||||
|
|
||||||
params =
|
params =
|
||||||
%{}
|
%{}
|
||||||
|> Map.put("type", ["Create"])
|
|> Map.put(:type, ["Create"])
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("user", user)
|
|> Map.put(:user, user)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
[user.ap_id | Pleroma.User.following(user)]
|
[user.ap_id | Pleroma.User.following(user)]
|
||||||
|
|
|
@ -24,6 +24,6 @@ defmodule Pleroma.Constants do
|
||||||
|
|
||||||
const(static_only_files,
|
const(static_only_files,
|
||||||
do:
|
do:
|
||||||
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc)
|
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -163,8 +163,8 @@ def for_user_with_last_activity_id(user, params \\ %{}) do
|
||||||
|> Enum.map(fn participation ->
|
|> Enum.map(fn participation ->
|
||||||
activity_id =
|
activity_id =
|
||||||
ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{
|
ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{
|
||||||
"user" => user,
|
user: user,
|
||||||
"blocking_user" => user
|
blocking_user: user
|
||||||
})
|
})
|
||||||
|
|
||||||
%{
|
%{
|
||||||
|
|
|
@ -141,6 +141,12 @@ def following_query(%User{} = user) do
|
||||||
|> where([r], r.state == ^:follow_accept)
|
|> where([r], r.state == ^:follow_accept)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def outgoing_pending_follow_requests_query(%User{} = follower) do
|
||||||
|
__MODULE__
|
||||||
|
|> where([r], r.follower_id == ^follower.id)
|
||||||
|
|> where([r], r.state == ^:follow_pending)
|
||||||
|
end
|
||||||
|
|
||||||
def following(%User{} = user) do
|
def following(%User{} = user) do
|
||||||
following =
|
following =
|
||||||
following_query(user)
|
following_query(user)
|
||||||
|
|
|
@ -17,14 +17,6 @@ def append_uri_params(uri, appended_params) do
|
||||||
|> URI.to_string()
|
|> URI.to_string()
|
||||||
end
|
end
|
||||||
|
|
||||||
def append_param_if_present(%{} = params, param_name, param_value) do
|
|
||||||
if param_value do
|
|
||||||
Map.put(params, param_name, param_value)
|
|
||||||
else
|
|
||||||
params
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def maybe_add_base("/" <> uri, base), do: Path.join([base, uri])
|
def maybe_add_base("/" <> uri, base), do: Path.join([base, uri])
|
||||||
def maybe_add_base(uri, _base), do: uri
|
def maybe_add_base(uri, _base), do: uri
|
||||||
end
|
end
|
||||||
|
|
15
lib/pleroma/maps.ex
Normal file
15
lib/pleroma/maps.ex
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Maps do
|
||||||
|
def put_if_present(map, key, value, value_function \\ &{:ok, &1}) when is_map(map) do
|
||||||
|
with false <- is_nil(key),
|
||||||
|
false <- is_nil(value),
|
||||||
|
{:ok, new_value} <- value_function.(value) do
|
||||||
|
Map.put(map, key, new_value)
|
||||||
|
else
|
||||||
|
_ -> map
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -23,12 +23,12 @@ def page_keys, do: @page_keys
|
||||||
@spec fetch_paginated(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()]
|
@spec fetch_paginated(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()]
|
||||||
def fetch_paginated(query, params, type \\ :keyset, table_binding \\ nil)
|
def fetch_paginated(query, params, type \\ :keyset, table_binding \\ nil)
|
||||||
|
|
||||||
def fetch_paginated(query, %{"total" => true} = params, :keyset, table_binding) do
|
def fetch_paginated(query, %{total: true} = params, :keyset, table_binding) do
|
||||||
total = Repo.aggregate(query, :count, :id)
|
total = Repo.aggregate(query, :count, :id)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
total: total,
|
total: total,
|
||||||
items: fetch_paginated(query, Map.drop(params, ["total"]), :keyset, table_binding)
|
items: fetch_paginated(query, Map.drop(params, [:total]), :keyset, table_binding)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ def fetch_paginated(query, params, :keyset, table_binding) do
|
||||||
|> enforce_order(options)
|
|> enforce_order(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_paginated(query, %{"total" => true} = params, :offset, table_binding) do
|
def fetch_paginated(query, %{total: true} = params, :offset, table_binding) do
|
||||||
total =
|
total =
|
||||||
query
|
query
|
||||||
|> Ecto.Query.exclude(:left_join)
|
|> Ecto.Query.exclude(:left_join)
|
||||||
|
@ -49,7 +49,7 @@ def fetch_paginated(query, %{"total" => true} = params, :offset, table_binding)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
total: total,
|
total: total,
|
||||||
items: fetch_paginated(query, Map.drop(params, ["total"]), :offset, table_binding)
|
items: fetch_paginated(query, Map.drop(params, [:total]), :offset, table_binding)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -90,12 +90,6 @@ defp cast_params(params) do
|
||||||
skip_order: :boolean
|
skip_order: :boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
params =
|
|
||||||
Enum.reduce(params, %{}, fn
|
|
||||||
{key, _value}, acc when is_atom(key) -> Map.drop(acc, [key])
|
|
||||||
{key, value}, acc -> Map.put(acc, key, value)
|
|
||||||
end)
|
|
||||||
|
|
||||||
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
||||||
changeset.changes
|
changeset.changes
|
||||||
end
|
end
|
||||||
|
|
|
@ -1489,6 +1489,8 @@ def perform(:delete, %User{} = user) do
|
||||||
|
|
||||||
delete_user_activities(user)
|
delete_user_activities(user)
|
||||||
|
|
||||||
|
delete_outgoing_pending_follow_requests(user)
|
||||||
|
|
||||||
delete_or_deactivate(user)
|
delete_or_deactivate(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1611,6 +1613,12 @@ defp delete_activity(%{data: %{"type" => type}} = activity, user)
|
||||||
|
|
||||||
defp delete_activity(_activity, _user), do: "Doing nothing"
|
defp delete_activity(_activity, _user), do: "Doing nothing"
|
||||||
|
|
||||||
|
defp delete_outgoing_pending_follow_requests(user) do
|
||||||
|
user
|
||||||
|
|> FollowingRelationship.outgoing_pending_follow_requests_query()
|
||||||
|
|> Repo.delete_all()
|
||||||
|
end
|
||||||
|
|
||||||
def html_filter_policy(%User{no_rich_text: true}) do
|
def html_filter_policy(%User{no_rich_text: true}) do
|
||||||
Pleroma.HTML.Scrubber.TwitterText
|
Pleroma.HTML.Scrubber.TwitterText
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,7 +45,7 @@ defmodule Pleroma.User.Query do
|
||||||
is_admin: boolean(),
|
is_admin: boolean(),
|
||||||
is_moderator: boolean(),
|
is_moderator: boolean(),
|
||||||
super_users: boolean(),
|
super_users: boolean(),
|
||||||
exclude_service_users: boolean(),
|
invisible: boolean(),
|
||||||
followers: User.t(),
|
followers: User.t(),
|
||||||
friends: User.t(),
|
friends: User.t(),
|
||||||
recipients_from_activity: [String.t()],
|
recipients_from_activity: [String.t()],
|
||||||
|
@ -89,8 +89,8 @@ defp compose_query({key, value}, query)
|
||||||
where(query, [u], ilike(field(u, ^key), ^"%#{value}%"))
|
where(query, [u], ilike(field(u, ^key), ^"%#{value}%"))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp compose_query({:exclude_service_users, _}, query) do
|
defp compose_query({:invisible, bool}, query) when is_boolean(bool) do
|
||||||
where(query, [u], not like(u.ap_id, "%/relay") and not like(u.ap_id, "%/internal/fetch"))
|
where(query, [u], u.invisible == ^bool)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp compose_query({key, value}, query)
|
defp compose_query({key, value}, query)
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
alias Pleroma.Constants
|
alias Pleroma.Constants
|
||||||
alias Pleroma.Conversation
|
alias Pleroma.Conversation
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Object.Containment
|
alias Pleroma.Object.Containment
|
||||||
|
@ -20,7 +21,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.MRF
|
alias Pleroma.Web.ActivityPub.MRF
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
|
||||||
alias Pleroma.Web.Streamer
|
alias Pleroma.Web.Streamer
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
alias Pleroma.Workers.BackgroundWorker
|
alias Pleroma.Workers.BackgroundWorker
|
||||||
|
@ -68,16 +68,12 @@ defp get_recipients(data) do
|
||||||
{recipients, to, cc}
|
{recipients, to, cc}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_actor_is_active(actor) do
|
defp check_actor_is_active(nil), do: true
|
||||||
if not is_nil(actor) do
|
|
||||||
with user <- User.get_cached_by_ap_id(actor),
|
defp check_actor_is_active(actor) when is_binary(actor) do
|
||||||
false <- user.deactivated do
|
case User.get_cached_by_ap_id(actor) do
|
||||||
true
|
%User{deactivated: deactivated} -> not deactivated
|
||||||
else
|
_ -> false
|
||||||
_e -> false
|
|
||||||
end
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -88,7 +84,7 @@ defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(
|
||||||
|
|
||||||
defp check_remote_limit(_), do: true
|
defp check_remote_limit(_), do: true
|
||||||
|
|
||||||
def increase_note_count_if_public(actor, object) do
|
defp increase_note_count_if_public(actor, object) do
|
||||||
if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
|
if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -96,7 +92,7 @@ def decrease_note_count_if_public(actor, object) do
|
||||||
if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
|
if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
|
||||||
end
|
end
|
||||||
|
|
||||||
def increase_replies_count_if_reply(%{
|
defp increase_replies_count_if_reply(%{
|
||||||
"object" => %{"inReplyTo" => reply_ap_id} = object,
|
"object" => %{"inReplyTo" => reply_ap_id} = object,
|
||||||
"type" => "Create"
|
"type" => "Create"
|
||||||
}) do
|
}) do
|
||||||
|
@ -105,19 +101,9 @@ def increase_replies_count_if_reply(%{
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def increase_replies_count_if_reply(_create_data), do: :noop
|
defp increase_replies_count_if_reply(_create_data), do: :noop
|
||||||
|
|
||||||
def decrease_replies_count_if_reply(%Object{
|
defp increase_poll_votes_if_vote(%{
|
||||||
data: %{"inReplyTo" => reply_ap_id} = object
|
|
||||||
}) do
|
|
||||||
if is_public?(object) do
|
|
||||||
Object.decrease_replies_count(reply_ap_id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def decrease_replies_count_if_reply(_object), do: :noop
|
|
||||||
|
|
||||||
def increase_poll_votes_if_vote(%{
|
|
||||||
"object" => %{"inReplyTo" => reply_ap_id, "name" => name},
|
"object" => %{"inReplyTo" => reply_ap_id, "name" => name},
|
||||||
"type" => "Create",
|
"type" => "Create",
|
||||||
"actor" => actor
|
"actor" => actor
|
||||||
|
@ -125,7 +111,7 @@ def increase_poll_votes_if_vote(%{
|
||||||
Object.increase_vote_count(reply_ap_id, name, actor)
|
Object.increase_vote_count(reply_ap_id, name, actor)
|
||||||
end
|
end
|
||||||
|
|
||||||
def increase_poll_votes_if_vote(_create_data), do: :noop
|
defp increase_poll_votes_if_vote(_create_data), do: :noop
|
||||||
|
|
||||||
@spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
|
@spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
|
||||||
def persist(object, meta) do
|
def persist(object, meta) do
|
||||||
|
@ -164,12 +150,7 @@ def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when
|
||||||
|> maybe_create_activity_expiration()
|
|> maybe_create_activity_expiration()
|
||||||
|
|
||||||
# Splice in the child object if we have one.
|
# Splice in the child object if we have one.
|
||||||
activity =
|
activity = Maps.put_if_present(activity, :object, object)
|
||||||
if not is_nil(object) do
|
|
||||||
Map.put(activity, :object, object)
|
|
||||||
else
|
|
||||||
activity
|
|
||||||
end
|
|
||||||
|
|
||||||
BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
|
BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
|
||||||
|
|
||||||
|
@ -214,8 +195,8 @@ defp maybe_create_activity_expiration(result), do: result
|
||||||
|
|
||||||
defp create_or_bump_conversation(activity, actor) do
|
defp create_or_bump_conversation(activity, actor) do
|
||||||
with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
|
with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
|
||||||
%User{} = user <- User.get_cached_by_ap_id(actor),
|
%User{} = user <- User.get_cached_by_ap_id(actor) do
|
||||||
Participation.mark_as_read(user, conversation) do
|
Participation.mark_as_read(user, conversation)
|
||||||
{:ok, conversation}
|
{:ok, conversation}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -237,13 +218,15 @@ def stream_out_participations(participations) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def stream_out_participations(%Object{data: %{"context" => context}}, user) do
|
def stream_out_participations(%Object{data: %{"context" => context}}, user) do
|
||||||
with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
|
with %Conversation{} = conversation <- Conversation.get_for_ap_id(context) do
|
||||||
conversation = Repo.preload(conversation, :participations),
|
conversation = Repo.preload(conversation, :participations)
|
||||||
|
|
||||||
last_activity_id =
|
last_activity_id =
|
||||||
fetch_latest_activity_id_for_context(conversation.ap_id, %{
|
fetch_latest_activity_id_for_context(conversation.ap_id, %{
|
||||||
"user" => user,
|
user: user,
|
||||||
"blocking_user" => user
|
blocking_user: user
|
||||||
}) do
|
})
|
||||||
|
|
||||||
if last_activity_id do
|
if last_activity_id do
|
||||||
stream_out_participations(conversation.participations)
|
stream_out_participations(conversation.participations)
|
||||||
end
|
end
|
||||||
|
@ -277,12 +260,13 @@ defp do_create(%{to: to, actor: actor, context: context, object: object} = param
|
||||||
published = params[:published]
|
published = params[:published]
|
||||||
quick_insert? = Config.get([:env]) == :benchmark
|
quick_insert? = Config.get([:env]) == :benchmark
|
||||||
|
|
||||||
with create_data <-
|
create_data =
|
||||||
make_create_data(
|
make_create_data(
|
||||||
%{to: to, actor: actor, published: published, context: context, object: object},
|
%{to: to, actor: actor, published: published, context: context, object: object},
|
||||||
additional
|
additional
|
||||||
),
|
)
|
||||||
{:ok, activity} <- insert(create_data, local, fake),
|
|
||||||
|
with {:ok, activity} <- insert(create_data, local, fake),
|
||||||
{:fake, false, activity} <- {:fake, fake, activity},
|
{:fake, false, activity} <- {:fake, fake, activity},
|
||||||
_ <- increase_replies_count_if_reply(create_data),
|
_ <- increase_replies_count_if_reply(create_data),
|
||||||
_ <- increase_poll_votes_if_vote(create_data),
|
_ <- increase_poll_votes_if_vote(create_data),
|
||||||
|
@ -310,12 +294,13 @@ def listen(%{to: to, actor: actor, context: context, object: object} = params) d
|
||||||
local = !(params[:local] == false)
|
local = !(params[:local] == false)
|
||||||
published = params[:published]
|
published = params[:published]
|
||||||
|
|
||||||
with listen_data <-
|
listen_data =
|
||||||
make_listen_data(
|
make_listen_data(
|
||||||
%{to: to, actor: actor, published: published, context: context, object: object},
|
%{to: to, actor: actor, published: published, context: context, object: object},
|
||||||
additional
|
additional
|
||||||
),
|
)
|
||||||
{:ok, activity} <- insert(listen_data, local),
|
|
||||||
|
with {:ok, activity} <- insert(listen_data, local),
|
||||||
_ <- notify_and_stream(activity),
|
_ <- notify_and_stream(activity),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
@ -333,14 +318,15 @@ def reject(params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec accept_or_reject(String.t(), map()) :: {:ok, Activity.t()} | {:error, any()}
|
@spec accept_or_reject(String.t(), map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||||
def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
|
defp accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
|
||||||
local = Map.get(params, :local, true)
|
local = Map.get(params, :local, true)
|
||||||
activity_id = Map.get(params, :activity_id, nil)
|
activity_id = Map.get(params, :activity_id, nil)
|
||||||
|
|
||||||
with data <-
|
data =
|
||||||
%{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
|
%{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
|
||||||
|> Utils.maybe_put("id", activity_id),
|
|> Maps.put_if_present("id", activity_id)
|
||||||
{:ok, activity} <- insert(data, local),
|
|
||||||
|
with {:ok, activity} <- insert(data, local),
|
||||||
_ <- notify_and_stream(activity),
|
_ <- notify_and_stream(activity),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
@ -352,15 +338,17 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
||||||
local = !(params[:local] == false)
|
local = !(params[:local] == false)
|
||||||
activity_id = params[:activity_id]
|
activity_id = params[:activity_id]
|
||||||
|
|
||||||
with data <- %{
|
data =
|
||||||
|
%{
|
||||||
"to" => to,
|
"to" => to,
|
||||||
"cc" => cc,
|
"cc" => cc,
|
||||||
"type" => "Update",
|
"type" => "Update",
|
||||||
"actor" => actor,
|
"actor" => actor,
|
||||||
"object" => object
|
"object" => object
|
||||||
},
|
}
|
||||||
data <- Utils.maybe_put(data, "id", activity_id),
|
|> Maps.put_if_present("id", activity_id)
|
||||||
{:ok, activity} <- insert(data, local),
|
|
||||||
|
with {:ok, activity} <- insert(data, local),
|
||||||
_ <- notify_and_stream(activity),
|
_ <- notify_and_stream(activity),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
@ -377,8 +365,9 @@ def follow(follower, followed, activity_id \\ nil, local \\ true) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_follow(follower, followed, activity_id, local) do
|
defp do_follow(follower, followed, activity_id, local) do
|
||||||
with data <- make_follow_data(follower, followed, activity_id),
|
data = make_follow_data(follower, followed, activity_id)
|
||||||
{:ok, activity} <- insert(data, local),
|
|
||||||
|
with {:ok, activity} <- insert(data, local),
|
||||||
_ <- notify_and_stream(activity),
|
_ <- notify_and_stream(activity),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
@ -422,13 +411,13 @@ def block(blocker, blocked, activity_id \\ nil, local \\ true) do
|
||||||
defp do_block(blocker, blocked, activity_id, local) do
|
defp do_block(blocker, blocked, activity_id, local) do
|
||||||
unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
|
unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
|
||||||
|
|
||||||
if unfollow_blocked do
|
if unfollow_blocked and fetch_latest_follow(blocker, blocked) do
|
||||||
follow_activity = fetch_latest_follow(blocker, blocked)
|
unfollow(blocker, blocked, nil, local)
|
||||||
if follow_activity, do: unfollow(blocker, blocked, nil, local)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
with block_data <- make_block_data(blocker, blocked, activity_id),
|
block_data = make_block_data(blocker, blocked, activity_id)
|
||||||
{:ok, activity} <- insert(block_data, local),
|
|
||||||
|
with {:ok, activity} <- insert(block_data, local),
|
||||||
_ <- notify_and_stream(activity),
|
_ <- notify_and_stream(activity),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
@ -507,8 +496,8 @@ def fetch_activities_for_context_query(context, opts) do
|
||||||
public = [Constants.as_public()]
|
public = [Constants.as_public()]
|
||||||
|
|
||||||
recipients =
|
recipients =
|
||||||
if opts["user"],
|
if opts[:user],
|
||||||
do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
|
do: [opts[:user].ap_id | User.following(opts[:user])] ++ public,
|
||||||
else: public
|
else: public
|
||||||
|
|
||||||
from(activity in Activity)
|
from(activity in Activity)
|
||||||
|
@ -516,7 +505,7 @@ def fetch_activities_for_context_query(context, opts) do
|
||||||
|> maybe_preload_bookmarks(opts)
|
|> maybe_preload_bookmarks(opts)
|
||||||
|> maybe_set_thread_muted_field(opts)
|
|> maybe_set_thread_muted_field(opts)
|
||||||
|> restrict_blocked(opts)
|
|> restrict_blocked(opts)
|
||||||
|> restrict_recipients(recipients, opts["user"])
|
|> restrict_recipients(recipients, opts[:user])
|
||||||
|> where(
|
|> where(
|
||||||
[activity],
|
[activity],
|
||||||
fragment(
|
fragment(
|
||||||
|
@ -543,7 +532,7 @@ def fetch_activities_for_context(context, opts \\ %{}) do
|
||||||
FlakeId.Ecto.CompatType.t() | nil
|
FlakeId.Ecto.CompatType.t() | nil
|
||||||
def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
|
def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
|
||||||
context
|
context
|
||||||
|> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
|
|> fetch_activities_for_context_query(Map.merge(%{skip_preload: true}, opts))
|
||||||
|> limit(1)
|
|> limit(1)
|
||||||
|> select([a], a.id)
|
|> select([a], a.id)
|
||||||
|> Repo.one()
|
|> Repo.one()
|
||||||
|
@ -551,24 +540,18 @@ def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
|
||||||
|
|
||||||
@spec fetch_public_or_unlisted_activities(map(), Pagination.type()) :: [Activity.t()]
|
@spec fetch_public_or_unlisted_activities(map(), Pagination.type()) :: [Activity.t()]
|
||||||
def fetch_public_or_unlisted_activities(opts \\ %{}, pagination \\ :keyset) do
|
def fetch_public_or_unlisted_activities(opts \\ %{}, pagination \\ :keyset) do
|
||||||
opts = Map.drop(opts, ["user"])
|
opts = Map.delete(opts, :user)
|
||||||
|
|
||||||
query = fetch_activities_query([Constants.as_public()], opts)
|
[Constants.as_public()]
|
||||||
|
|> fetch_activities_query(opts)
|
||||||
query =
|
|> restrict_unlisted(opts)
|
||||||
if opts["restrict_unlisted"] do
|
|> Pagination.fetch_paginated(opts, pagination)
|
||||||
restrict_unlisted(query)
|
|
||||||
else
|
|
||||||
query
|
|
||||||
end
|
|
||||||
|
|
||||||
Pagination.fetch_paginated(query, opts, pagination)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
|
@spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
|
||||||
def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
|
def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
|
||||||
opts
|
opts
|
||||||
|> Map.put("restrict_unlisted", true)
|
|> Map.put(:restrict_unlisted, true)
|
||||||
|> fetch_public_or_unlisted_activities(pagination)
|
|> fetch_public_or_unlisted_activities(pagination)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -577,7 +560,6 @@ def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
|
||||||
defp restrict_visibility(query, %{visibility: visibility})
|
defp restrict_visibility(query, %{visibility: visibility})
|
||||||
when is_list(visibility) do
|
when is_list(visibility) do
|
||||||
if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
|
if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
|
||||||
query =
|
|
||||||
from(
|
from(
|
||||||
a in query,
|
a in query,
|
||||||
where:
|
where:
|
||||||
|
@ -589,8 +571,6 @@ defp restrict_visibility(query, %{visibility: visibility})
|
||||||
^visibility
|
^visibility
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
query
|
|
||||||
else
|
else
|
||||||
Logger.error("Could not restrict visibility to #{visibility}")
|
Logger.error("Could not restrict visibility to #{visibility}")
|
||||||
end
|
end
|
||||||
|
@ -612,7 +592,7 @@ defp restrict_visibility(_query, %{visibility: visibility})
|
||||||
|
|
||||||
defp restrict_visibility(query, _visibility), do: query
|
defp restrict_visibility(query, _visibility), do: query
|
||||||
|
|
||||||
defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
|
defp exclude_visibility(query, %{exclude_visibilities: visibility})
|
||||||
when is_list(visibility) do
|
when is_list(visibility) do
|
||||||
if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
|
if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
|
||||||
from(
|
from(
|
||||||
|
@ -632,7 +612,7 @@ defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
|
defp exclude_visibility(query, %{exclude_visibilities: visibility})
|
||||||
when visibility in @valid_visibilities do
|
when visibility in @valid_visibilities do
|
||||||
from(
|
from(
|
||||||
a in query,
|
a in query,
|
||||||
|
@ -647,7 +627,7 @@ defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
|
defp exclude_visibility(query, %{exclude_visibilities: visibility})
|
||||||
when visibility not in [nil | @valid_visibilities] do
|
when visibility not in [nil | @valid_visibilities] do
|
||||||
Logger.error("Could not exclude visibility to #{visibility}")
|
Logger.error("Could not exclude visibility to #{visibility}")
|
||||||
query
|
query
|
||||||
|
@ -658,14 +638,10 @@ defp exclude_visibility(query, _visibility), do: query
|
||||||
defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
|
defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
|
||||||
do: query
|
do: query
|
||||||
|
|
||||||
defp restrict_thread_visibility(
|
defp restrict_thread_visibility(query, %{user: %User{skip_thread_containment: true}}, _),
|
||||||
query,
|
|
||||||
%{"user" => %User{skip_thread_containment: true}},
|
|
||||||
_
|
|
||||||
),
|
|
||||||
do: query
|
do: query
|
||||||
|
|
||||||
defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
|
defp restrict_thread_visibility(query, %{user: %User{ap_id: ap_id}}, _) do
|
||||||
from(
|
from(
|
||||||
a in query,
|
a in query,
|
||||||
where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
|
where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
|
||||||
|
@ -677,87 +653,79 @@ defp restrict_thread_visibility(query, _, _), do: query
|
||||||
def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
|
def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.put("user", reading_user)
|
|> Map.put(:user, reading_user)
|
||||||
|> Map.put("actor_id", user.ap_id)
|
|> Map.put(:actor_id, user.ap_id)
|
||||||
|
|
||||||
recipients =
|
%{
|
||||||
user_activities_recipients(%{
|
godmode: params[:godmode],
|
||||||
"godmode" => params["godmode"],
|
reading_user: reading_user
|
||||||
"reading_user" => reading_user
|
}
|
||||||
})
|
|> user_activities_recipients()
|
||||||
|
|> fetch_activities(params)
|
||||||
fetch_activities(recipients, params)
|
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_user_activities(user, reading_user, params \\ %{}) do
|
def fetch_user_activities(user, reading_user, params \\ %{}) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("user", reading_user)
|
|> Map.put(:user, reading_user)
|
||||||
|> Map.put("actor_id", user.ap_id)
|
|> Map.put(:actor_id, user.ap_id)
|
||||||
|> Map.put("pinned_activity_ids", user.pinned_activities)
|
|> Map.put(:pinned_activity_ids, user.pinned_activities)
|
||||||
|
|
||||||
params =
|
params =
|
||||||
if User.blocks?(reading_user, user) do
|
if User.blocks?(reading_user, user) do
|
||||||
params
|
params
|
||||||
else
|
else
|
||||||
params
|
params
|
||||||
|> Map.put("blocking_user", reading_user)
|
|> Map.put(:blocking_user, reading_user)
|
||||||
|> Map.put("muting_user", reading_user)
|
|> Map.put(:muting_user, reading_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
recipients =
|
%{
|
||||||
user_activities_recipients(%{
|
godmode: params[:godmode],
|
||||||
"godmode" => params["godmode"],
|
reading_user: reading_user
|
||||||
"reading_user" => reading_user
|
}
|
||||||
})
|
|> user_activities_recipients()
|
||||||
|
|> fetch_activities(params)
|
||||||
fetch_activities(recipients, params)
|
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_statuses(reading_user, params) do
|
def fetch_statuses(reading_user, params) do
|
||||||
params =
|
params = Map.put(params, :type, ["Create", "Announce"])
|
||||||
params
|
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|
||||||
|
|
||||||
recipients =
|
%{
|
||||||
user_activities_recipients(%{
|
godmode: params[:godmode],
|
||||||
"godmode" => params["godmode"],
|
reading_user: reading_user
|
||||||
"reading_user" => reading_user
|
}
|
||||||
})
|
|> user_activities_recipients()
|
||||||
|
|> fetch_activities(params, :offset)
|
||||||
fetch_activities(recipients, params, :offset)
|
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp user_activities_recipients(%{"godmode" => true}) do
|
defp user_activities_recipients(%{godmode: true}), do: []
|
||||||
[]
|
|
||||||
end
|
|
||||||
|
|
||||||
defp user_activities_recipients(%{"reading_user" => reading_user}) do
|
defp user_activities_recipients(%{reading_user: reading_user}) do
|
||||||
if reading_user do
|
if reading_user do
|
||||||
[Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
|
[Constants.as_public(), reading_user.ap_id | User.following(reading_user)]
|
||||||
else
|
else
|
||||||
[Constants.as_public()]
|
[Constants.as_public()]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_since(query, %{"since_id" => ""}), do: query
|
defp restrict_since(query, %{since_id: ""}), do: query
|
||||||
|
|
||||||
defp restrict_since(query, %{"since_id" => since_id}) do
|
defp restrict_since(query, %{since_id: since_id}) do
|
||||||
from(activity in query, where: activity.id > ^since_id)
|
from(activity in query, where: activity.id > ^since_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_since(query, _), do: query
|
defp restrict_since(query, _), do: query
|
||||||
|
|
||||||
defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
|
defp restrict_tag_reject(_query, %{tag_reject: _tag_reject, skip_preload: true}) do
|
||||||
raise "Can't use the child object without preloading!"
|
raise "Can't use the child object without preloading!"
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
|
defp restrict_tag_reject(query, %{tag_reject: [_ | _] = tag_reject}) do
|
||||||
when is_list(tag_reject) and tag_reject != [] do
|
|
||||||
from(
|
from(
|
||||||
[_activity, object] in query,
|
[_activity, object] in query,
|
||||||
where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
|
where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
|
||||||
|
@ -766,12 +734,11 @@ defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
|
||||||
|
|
||||||
defp restrict_tag_reject(query, _), do: query
|
defp restrict_tag_reject(query, _), do: query
|
||||||
|
|
||||||
defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
|
defp restrict_tag_all(_query, %{tag_all: _tag_all, skip_preload: true}) do
|
||||||
raise "Can't use the child object without preloading!"
|
raise "Can't use the child object without preloading!"
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_tag_all(query, %{"tag_all" => tag_all})
|
defp restrict_tag_all(query, %{tag_all: [_ | _] = tag_all}) do
|
||||||
when is_list(tag_all) and tag_all != [] do
|
|
||||||
from(
|
from(
|
||||||
[_activity, object] in query,
|
[_activity, object] in query,
|
||||||
where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
|
where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
|
||||||
|
@ -780,18 +747,18 @@ defp restrict_tag_all(query, %{"tag_all" => tag_all})
|
||||||
|
|
||||||
defp restrict_tag_all(query, _), do: query
|
defp restrict_tag_all(query, _), do: query
|
||||||
|
|
||||||
defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
|
defp restrict_tag(_query, %{tag: _tag, skip_preload: true}) do
|
||||||
raise "Can't use the child object without preloading!"
|
raise "Can't use the child object without preloading!"
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
|
defp restrict_tag(query, %{tag: tag}) when is_list(tag) do
|
||||||
from(
|
from(
|
||||||
[_activity, object] in query,
|
[_activity, object] in query,
|
||||||
where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
|
where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
|
defp restrict_tag(query, %{tag: tag}) when is_binary(tag) do
|
||||||
from(
|
from(
|
||||||
[_activity, object] in query,
|
[_activity, object] in query,
|
||||||
where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
|
where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
|
||||||
|
@ -814,35 +781,35 @@ defp restrict_recipients(query, recipients, user) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_local(query, %{"local_only" => true}) do
|
defp restrict_local(query, %{local_only: true}) do
|
||||||
from(activity in query, where: activity.local == true)
|
from(activity in query, where: activity.local == true)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_local(query, _), do: query
|
defp restrict_local(query, _), do: query
|
||||||
|
|
||||||
defp restrict_actor(query, %{"actor_id" => actor_id}) do
|
defp restrict_actor(query, %{actor_id: actor_id}) do
|
||||||
from(activity in query, where: activity.actor == ^actor_id)
|
from(activity in query, where: activity.actor == ^actor_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_actor(query, _), do: query
|
defp restrict_actor(query, _), do: query
|
||||||
|
|
||||||
defp restrict_type(query, %{"type" => type}) when is_binary(type) do
|
defp restrict_type(query, %{type: type}) when is_binary(type) do
|
||||||
from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
|
from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_type(query, %{"type" => type}) do
|
defp restrict_type(query, %{type: type}) do
|
||||||
from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
|
from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_type(query, _), do: query
|
defp restrict_type(query, _), do: query
|
||||||
|
|
||||||
defp restrict_state(query, %{"state" => state}) do
|
defp restrict_state(query, %{state: state}) do
|
||||||
from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
|
from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_state(query, _), do: query
|
defp restrict_state(query, _), do: query
|
||||||
|
|
||||||
defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
|
defp restrict_favorited_by(query, %{favorited_by: ap_id}) do
|
||||||
from(
|
from(
|
||||||
[_activity, object] in query,
|
[_activity, object] in query,
|
||||||
where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
|
where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
|
||||||
|
@ -851,11 +818,11 @@ defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
|
||||||
|
|
||||||
defp restrict_favorited_by(query, _), do: query
|
defp restrict_favorited_by(query, _), do: query
|
||||||
|
|
||||||
defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
|
defp restrict_media(_query, %{only_media: _val, skip_preload: true}) do
|
||||||
raise "Can't use the child object without preloading!"
|
raise "Can't use the child object without preloading!"
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_media(query, %{"only_media" => val}) when val in [true, "true", "1"] do
|
defp restrict_media(query, %{only_media: true}) do
|
||||||
from(
|
from(
|
||||||
[_activity, object] in query,
|
[_activity, object] in query,
|
||||||
where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
|
where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
|
||||||
|
@ -864,7 +831,7 @@ defp restrict_media(query, %{"only_media" => val}) when val in [true, "true", "1
|
||||||
|
|
||||||
defp restrict_media(query, _), do: query
|
defp restrict_media(query, _), do: query
|
||||||
|
|
||||||
defp restrict_replies(query, %{"exclude_replies" => val}) when val in [true, "true", "1"] do
|
defp restrict_replies(query, %{exclude_replies: true}) do
|
||||||
from(
|
from(
|
||||||
[_activity, object] in query,
|
[_activity, object] in query,
|
||||||
where: fragment("?->>'inReplyTo' is null", object.data)
|
where: fragment("?->>'inReplyTo' is null", object.data)
|
||||||
|
@ -872,8 +839,8 @@ defp restrict_replies(query, %{"exclude_replies" => val}) when val in [true, "tr
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_replies(query, %{
|
defp restrict_replies(query, %{
|
||||||
"reply_filtering_user" => user,
|
reply_filtering_user: user,
|
||||||
"reply_visibility" => "self"
|
reply_visibility: "self"
|
||||||
}) do
|
}) do
|
||||||
from(
|
from(
|
||||||
[activity, object] in query,
|
[activity, object] in query,
|
||||||
|
@ -888,8 +855,8 @@ defp restrict_replies(query, %{
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_replies(query, %{
|
defp restrict_replies(query, %{
|
||||||
"reply_filtering_user" => user,
|
reply_filtering_user: user,
|
||||||
"reply_visibility" => "following"
|
reply_visibility: "following"
|
||||||
}) do
|
}) do
|
||||||
from(
|
from(
|
||||||
[activity, object] in query,
|
[activity, object] in query,
|
||||||
|
@ -908,16 +875,16 @@ defp restrict_replies(query, %{
|
||||||
|
|
||||||
defp restrict_replies(query, _), do: query
|
defp restrict_replies(query, _), do: query
|
||||||
|
|
||||||
defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val in [true, "true", "1"] do
|
defp restrict_reblogs(query, %{exclude_reblogs: true}) do
|
||||||
from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
|
from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_reblogs(query, _), do: query
|
defp restrict_reblogs(query, _), do: query
|
||||||
|
|
||||||
defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
|
defp restrict_muted(query, %{with_muted: true}), do: query
|
||||||
|
|
||||||
defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
|
defp restrict_muted(query, %{muting_user: %User{} = user} = opts) do
|
||||||
mutes = opts["muted_users_ap_ids"] || User.muted_users_ap_ids(user)
|
mutes = opts[:muted_users_ap_ids] || User.muted_users_ap_ids(user)
|
||||||
|
|
||||||
query =
|
query =
|
||||||
from([activity] in query,
|
from([activity] in query,
|
||||||
|
@ -925,7 +892,7 @@ defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
|
||||||
where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
|
where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
|
||||||
)
|
)
|
||||||
|
|
||||||
unless opts["skip_preload"] do
|
unless opts[:skip_preload] do
|
||||||
from([thread_mute: tm] in query, where: is_nil(tm.user_id))
|
from([thread_mute: tm] in query, where: is_nil(tm.user_id))
|
||||||
else
|
else
|
||||||
query
|
query
|
||||||
|
@ -934,8 +901,8 @@ defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
|
||||||
|
|
||||||
defp restrict_muted(query, _), do: query
|
defp restrict_muted(query, _), do: query
|
||||||
|
|
||||||
defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
|
defp restrict_blocked(query, %{blocking_user: %User{} = user} = opts) do
|
||||||
blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
|
blocked_ap_ids = opts[:blocked_users_ap_ids] || User.blocked_users_ap_ids(user)
|
||||||
domain_blocks = user.domain_blocks || []
|
domain_blocks = user.domain_blocks || []
|
||||||
|
|
||||||
following_ap_ids = User.get_friends_ap_ids(user)
|
following_ap_ids = User.get_friends_ap_ids(user)
|
||||||
|
@ -947,6 +914,12 @@ defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
|
||||||
[activity, object: o] in query,
|
[activity, object: o] in query,
|
||||||
where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
|
where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
|
||||||
where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
|
where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"recipients_contain_blocked_domains(?, ?) = false",
|
||||||
|
activity.recipients,
|
||||||
|
^domain_blocks
|
||||||
|
),
|
||||||
where:
|
where:
|
||||||
fragment(
|
fragment(
|
||||||
"not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
|
"not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
|
||||||
|
@ -975,7 +948,7 @@ defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
|
||||||
|
|
||||||
defp restrict_blocked(query, _), do: query
|
defp restrict_blocked(query, _), do: query
|
||||||
|
|
||||||
defp restrict_unlisted(query) do
|
defp restrict_unlisted(query, %{restrict_unlisted: true}) do
|
||||||
from(
|
from(
|
||||||
activity in query,
|
activity in query,
|
||||||
where:
|
where:
|
||||||
|
@ -987,19 +960,16 @@ defp restrict_unlisted(query) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: when all endpoints migrated to OpenAPI compare `pinned` with `true` (boolean) only,
|
defp restrict_unlisted(query, _), do: query
|
||||||
# the same for `restrict_media/2`, `restrict_replies/2`, 'restrict_reblogs/2'
|
|
||||||
# and `restrict_muted/2`
|
|
||||||
|
|
||||||
defp restrict_pinned(query, %{"pinned" => pinned, "pinned_activity_ids" => ids})
|
defp restrict_pinned(query, %{pinned: true, pinned_activity_ids: ids}) do
|
||||||
when pinned in [true, "true", "1"] do
|
|
||||||
from(activity in query, where: activity.id in ^ids)
|
from(activity in query, where: activity.id in ^ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_pinned(query, _), do: query
|
defp restrict_pinned(query, _), do: query
|
||||||
|
|
||||||
defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user} = opts) do
|
defp restrict_muted_reblogs(query, %{muting_user: %User{} = user} = opts) do
|
||||||
muted_reblogs = opts["reblog_muted_users_ap_ids"] || User.reblog_muted_users_ap_ids(user)
|
muted_reblogs = opts[:reblog_muted_users_ap_ids] || User.reblog_muted_users_ap_ids(user)
|
||||||
|
|
||||||
from(
|
from(
|
||||||
activity in query,
|
activity in query,
|
||||||
|
@ -1015,7 +985,7 @@ defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user} = opts) do
|
||||||
|
|
||||||
defp restrict_muted_reblogs(query, _), do: query
|
defp restrict_muted_reblogs(query, _), do: query
|
||||||
|
|
||||||
defp restrict_instance(query, %{"instance" => instance}) do
|
defp restrict_instance(query, %{instance: instance}) do
|
||||||
users =
|
users =
|
||||||
from(
|
from(
|
||||||
u in User,
|
u in User,
|
||||||
|
@ -1029,7 +999,7 @@ defp restrict_instance(query, %{"instance" => instance}) do
|
||||||
|
|
||||||
defp restrict_instance(query, _), do: query
|
defp restrict_instance(query, _), do: query
|
||||||
|
|
||||||
defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
|
defp exclude_poll_votes(query, %{include_poll_votes: true}), do: query
|
||||||
|
|
||||||
defp exclude_poll_votes(query, _) do
|
defp exclude_poll_votes(query, _) do
|
||||||
if has_named_binding?(query, :object) do
|
if has_named_binding?(query, :object) do
|
||||||
|
@ -1041,38 +1011,49 @@ defp exclude_poll_votes(query, _) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
|
defp exclude_invisible_actors(query, %{invisible_actors: true}), do: query
|
||||||
|
|
||||||
|
defp exclude_invisible_actors(query, _opts) do
|
||||||
|
invisible_ap_ids =
|
||||||
|
User.Query.build(%{invisible: true, select: [:ap_id]})
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.map(fn %{ap_id: ap_id} -> ap_id end)
|
||||||
|
|
||||||
|
from([activity] in query, where: activity.actor not in ^invisible_ap_ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_id(query, %{exclude_id: id}) when is_binary(id) do
|
||||||
from(activity in query, where: activity.id != ^id)
|
from(activity in query, where: activity.id != ^id)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp exclude_id(query, _), do: query
|
defp exclude_id(query, _), do: query
|
||||||
|
|
||||||
defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
|
defp maybe_preload_objects(query, %{skip_preload: true}), do: query
|
||||||
|
|
||||||
defp maybe_preload_objects(query, _) do
|
defp maybe_preload_objects(query, _) do
|
||||||
query
|
query
|
||||||
|> Activity.with_preloaded_object()
|
|> Activity.with_preloaded_object()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
|
defp maybe_preload_bookmarks(query, %{skip_preload: true}), do: query
|
||||||
|
|
||||||
defp maybe_preload_bookmarks(query, opts) do
|
defp maybe_preload_bookmarks(query, opts) do
|
||||||
query
|
query
|
||||||
|> Activity.with_preloaded_bookmark(opts["user"])
|
|> Activity.with_preloaded_bookmark(opts[:user])
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_preload_report_notes(query, %{"preload_report_notes" => true}) do
|
defp maybe_preload_report_notes(query, %{preload_report_notes: true}) do
|
||||||
query
|
query
|
||||||
|> Activity.with_preloaded_report_notes()
|
|> Activity.with_preloaded_report_notes()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_preload_report_notes(query, _), do: query
|
defp maybe_preload_report_notes(query, _), do: query
|
||||||
|
|
||||||
defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
|
defp maybe_set_thread_muted_field(query, %{skip_preload: true}), do: query
|
||||||
|
|
||||||
defp maybe_set_thread_muted_field(query, opts) do
|
defp maybe_set_thread_muted_field(query, opts) do
|
||||||
query
|
query
|
||||||
|> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
|
|> Activity.with_set_thread_muted_field(opts[:muting_user] || opts[:user])
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_order(query, %{order: :desc}) do
|
defp maybe_order(query, %{order: :desc}) do
|
||||||
|
@ -1088,24 +1069,23 @@ defp maybe_order(query, %{order: :asc}) do
|
||||||
defp maybe_order(query, _), do: query
|
defp maybe_order(query, _), do: query
|
||||||
|
|
||||||
defp fetch_activities_query_ap_ids_ops(opts) do
|
defp fetch_activities_query_ap_ids_ops(opts) do
|
||||||
source_user = opts["muting_user"]
|
source_user = opts[:muting_user]
|
||||||
ap_id_relationships = if source_user, do: [:mute, :reblog_mute], else: []
|
ap_id_relationships = if source_user, do: [:mute, :reblog_mute], else: []
|
||||||
|
|
||||||
ap_id_relationships =
|
ap_id_relationships =
|
||||||
ap_id_relationships ++
|
if opts[:blocking_user] && opts[:blocking_user] == source_user do
|
||||||
if opts["blocking_user"] && opts["blocking_user"] == source_user do
|
[:block | ap_id_relationships]
|
||||||
[:block]
|
|
||||||
else
|
else
|
||||||
[]
|
ap_id_relationships
|
||||||
end
|
end
|
||||||
|
|
||||||
preloaded_ap_ids = User.outgoing_relationships_ap_ids(source_user, ap_id_relationships)
|
preloaded_ap_ids = User.outgoing_relationships_ap_ids(source_user, ap_id_relationships)
|
||||||
|
|
||||||
restrict_blocked_opts = Map.merge(%{"blocked_users_ap_ids" => preloaded_ap_ids[:block]}, opts)
|
restrict_blocked_opts = Map.merge(%{blocked_users_ap_ids: preloaded_ap_ids[:block]}, opts)
|
||||||
restrict_muted_opts = Map.merge(%{"muted_users_ap_ids" => preloaded_ap_ids[:mute]}, opts)
|
restrict_muted_opts = Map.merge(%{muted_users_ap_ids: preloaded_ap_ids[:mute]}, opts)
|
||||||
|
|
||||||
restrict_muted_reblogs_opts =
|
restrict_muted_reblogs_opts =
|
||||||
Map.merge(%{"reblog_muted_users_ap_ids" => preloaded_ap_ids[:reblog_mute]}, opts)
|
Map.merge(%{reblog_muted_users_ap_ids: preloaded_ap_ids[:reblog_mute]}, opts)
|
||||||
|
|
||||||
{restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts}
|
{restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts}
|
||||||
end
|
end
|
||||||
|
@ -1124,7 +1104,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> maybe_preload_report_notes(opts)
|
|> maybe_preload_report_notes(opts)
|
||||||
|> maybe_set_thread_muted_field(opts)
|
|> maybe_set_thread_muted_field(opts)
|
||||||
|> maybe_order(opts)
|
|> maybe_order(opts)
|
||||||
|> restrict_recipients(recipients, opts["user"])
|
|> restrict_recipients(recipients, opts[:user])
|
||||||
|> restrict_replies(opts)
|
|> restrict_replies(opts)
|
||||||
|> restrict_tag(opts)
|
|> restrict_tag(opts)
|
||||||
|> restrict_tag_reject(opts)
|
|> restrict_tag_reject(opts)
|
||||||
|
@ -1146,16 +1126,17 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> restrict_instance(opts)
|
|> restrict_instance(opts)
|
||||||
|> Activity.restrict_deactivated_users()
|
|> Activity.restrict_deactivated_users()
|
||||||
|> exclude_poll_votes(opts)
|
|> exclude_poll_votes(opts)
|
||||||
|
|> exclude_invisible_actors(opts)
|
||||||
|> exclude_visibility(opts)
|
|> exclude_visibility(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
|
def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
|
||||||
list_memberships = Pleroma.List.memberships(opts["user"])
|
list_memberships = Pleroma.List.memberships(opts[:user])
|
||||||
|
|
||||||
fetch_activities_query(recipients ++ list_memberships, opts)
|
fetch_activities_query(recipients ++ list_memberships, opts)
|
||||||
|> Pagination.fetch_paginated(opts, pagination)
|
|> Pagination.fetch_paginated(opts, pagination)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|> maybe_update_cc(list_memberships, opts["user"])
|
|> maybe_update_cc(list_memberships, opts[:user])
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -1171,16 +1152,15 @@ def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
|
||||||
|> select([_like, object, activity], %{activity | object: object})
|
|> select([_like, object, activity], %{activity | object: object})
|
||||||
|> order_by([like, _, _], desc_nulls_last: like.id)
|
|> order_by([like, _, _], desc_nulls_last: like.id)
|
||||||
|> Pagination.fetch_paginated(
|
|> Pagination.fetch_paginated(
|
||||||
Map.merge(params, %{"skip_order" => true}),
|
Map.merge(params, %{skip_order: true}),
|
||||||
pagination,
|
pagination,
|
||||||
:object_activity
|
:object_activity
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
|
defp maybe_update_cc(activities, [_ | _] = list_memberships, %User{ap_id: user_ap_id}) do
|
||||||
when is_list(list_memberships) and length(list_memberships) > 0 do
|
|
||||||
Enum.map(activities, fn
|
Enum.map(activities, fn
|
||||||
%{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
|
%{data: %{"bcc" => [_ | _] = bcc}} = activity ->
|
||||||
if Enum.any?(bcc, &(&1 in list_memberships)) do
|
if Enum.any?(bcc, &(&1 in list_memberships)) do
|
||||||
update_in(activity.data["cc"], &[user_ap_id | &1])
|
update_in(activity.data["cc"], &[user_ap_id | &1])
|
||||||
else
|
else
|
||||||
|
@ -1194,7 +1174,7 @@ defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
|
||||||
|
|
||||||
defp maybe_update_cc(activities, _, _), do: activities
|
defp maybe_update_cc(activities, _, _), do: activities
|
||||||
|
|
||||||
def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
|
defp fetch_activities_bounded_query(query, recipients, recipients_with_public) do
|
||||||
from(activity in query,
|
from(activity in query,
|
||||||
where:
|
where:
|
||||||
fragment("? && ?", activity.recipients, ^recipients) or
|
fragment("? && ?", activity.recipients, ^recipients) or
|
||||||
|
@ -1218,12 +1198,7 @@ def fetch_activities_bounded(
|
||||||
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
|
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
|
||||||
def upload(file, opts \\ []) do
|
def upload(file, opts \\ []) do
|
||||||
with {:ok, data} <- Upload.store(file, opts) do
|
with {:ok, data} <- Upload.store(file, opts) do
|
||||||
obj_data =
|
obj_data = Maps.put_if_present(data, "actor", opts[:actor])
|
||||||
if opts[:actor] do
|
|
||||||
Map.put(data, "actor", opts[:actor])
|
|
||||||
else
|
|
||||||
data
|
|
||||||
end
|
|
||||||
|
|
||||||
Repo.insert(%Object{data: obj_data})
|
Repo.insert(%Object{data: obj_data})
|
||||||
end
|
end
|
||||||
|
@ -1269,8 +1244,8 @@ defp object_to_user_data(data) do
|
||||||
%{"type" => "Emoji"} -> true
|
%{"type" => "Emoji"} -> true
|
||||||
_ -> false
|
_ -> false
|
||||||
end)
|
end)
|
||||||
|> Enum.reduce(%{}, fn %{"icon" => %{"url" => url}, "name" => name}, acc ->
|
|> Map.new(fn %{"icon" => %{"url" => url}, "name" => name} ->
|
||||||
Map.put(acc, String.trim(name, ":"), url)
|
{String.trim(name, ":"), url}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
locked = data["manuallyApprovesFollowers"] || false
|
locked = data["manuallyApprovesFollowers"] || false
|
||||||
|
@ -1316,7 +1291,6 @@ defp object_to_user_data(data) do
|
||||||
}
|
}
|
||||||
|
|
||||||
# nickname can be nil because of virtual actors
|
# nickname can be nil because of virtual actors
|
||||||
user_data =
|
|
||||||
if data["preferredUsername"] do
|
if data["preferredUsername"] do
|
||||||
Map.put(
|
Map.put(
|
||||||
user_data,
|
user_data,
|
||||||
|
@ -1326,8 +1300,6 @@ defp object_to_user_data(data) do
|
||||||
else
|
else
|
||||||
Map.put(user_data, :nickname, nil)
|
Map.put(user_data, :nickname, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok, user_data}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_follow_information_for_user(user) do
|
def fetch_follow_information_for_user(user) do
|
||||||
|
@ -1402,9 +1374,8 @@ defp collection_private(%{"first" => first}) do
|
||||||
defp collection_private(_data), do: {:ok, true}
|
defp collection_private(_data), do: {:ok, true}
|
||||||
|
|
||||||
def user_data_from_user_object(data) do
|
def user_data_from_user_object(data) do
|
||||||
with {:ok, data} <- MRF.filter(data),
|
with {:ok, data} <- MRF.filter(data) do
|
||||||
{:ok, data} <- object_to_user_data(data) do
|
{:ok, object_to_user_data(data)}
|
||||||
{:ok, data}
|
|
||||||
else
|
else
|
||||||
e -> {:error, e}
|
e -> {:error, e}
|
||||||
end
|
end
|
||||||
|
@ -1412,15 +1383,14 @@ def user_data_from_user_object(data) do
|
||||||
|
|
||||||
def fetch_and_prepare_user_from_ap_id(ap_id) do
|
def fetch_and_prepare_user_from_ap_id(ap_id) do
|
||||||
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
|
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
|
||||||
{:ok, data} <- user_data_from_user_object(data),
|
{:ok, data} <- user_data_from_user_object(data) do
|
||||||
data <- maybe_update_follow_information(data) do
|
{:ok, maybe_update_follow_information(data)}
|
||||||
{:ok, data}
|
|
||||||
else
|
else
|
||||||
{:error, "Object has been deleted"} = e ->
|
{:error, "Object has been deleted" = e} ->
|
||||||
Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
|
Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
|
||||||
{:error, e}
|
{:error, e}
|
||||||
|
|
||||||
e ->
|
{:error, e} ->
|
||||||
Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
|
Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
|
||||||
{:error, e}
|
{:error, e}
|
||||||
end
|
end
|
||||||
|
@ -1443,8 +1413,6 @@ def make_user_from_ap_id(ap_id) do
|
||||||
|> Repo.insert()
|
|> Repo.insert()
|
||||||
|> User.set_cache()
|
|> User.set_cache()
|
||||||
end
|
end
|
||||||
else
|
|
||||||
e -> {:error, e}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1458,7 +1426,7 @@ def make_user_from_nickname(nickname) do
|
||||||
end
|
end
|
||||||
|
|
||||||
# filter out broken threads
|
# filter out broken threads
|
||||||
def contain_broken_threads(%Activity{} = activity, %User{} = user) do
|
defp contain_broken_threads(%Activity{} = activity, %User{} = user) do
|
||||||
entire_thread_visible_for_user?(activity, user)
|
entire_thread_visible_for_user?(activity, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1469,7 +1437,7 @@ def contain_activity(%Activity{} = activity, %User{} = user) do
|
||||||
|
|
||||||
def fetch_direct_messages_query do
|
def fetch_direct_messages_query do
|
||||||
Activity
|
Activity
|
||||||
|> restrict_type(%{"type" => "Create"})
|
|> restrict_type(%{type: "Create"})
|
||||||
|> restrict_visibility(%{visibility: "direct"})
|
|> restrict_visibility(%{visibility: "direct"})
|
||||||
|> order_by([activity], asc: activity.id)
|
|> order_by([activity], asc: activity.id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,6 +21,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
alias Pleroma.Web.ActivityPub.UserView
|
alias Pleroma.Web.ActivityPub.UserView
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
alias Pleroma.Web.ControllerHelper
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.FederatingPlug
|
alias Pleroma.Web.FederatingPlug
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
|
@ -230,27 +231,23 @@ def outbox(
|
||||||
when page? in [true, "true"] do
|
when page? in [true, "true"] do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||||
{:ok, user} <- User.ensure_keys_present(user) do
|
{:ok, user} <- User.ensure_keys_present(user) do
|
||||||
activities =
|
# "include_poll_votes" is a hack because postgres generates inefficient
|
||||||
if params["max_id"] do
|
# queries when filtering by 'Answer', poll votes will be hidden by the
|
||||||
ActivityPub.fetch_user_activities(user, for_user, %{
|
# visibility filter in this case anyway
|
||||||
"max_id" => params["max_id"],
|
params =
|
||||||
# This is a hack because postgres generates inefficient queries when filtering by
|
params
|
||||||
# 'Answer', poll votes will be hidden by the visibility filter in this case anyway
|
|> Map.drop(["nickname", "page"])
|
||||||
"include_poll_votes" => true,
|
|> Map.put("include_poll_votes", true)
|
||||||
"limit" => 10
|
|> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end)
|
||||||
})
|
|
||||||
else
|
activities = ActivityPub.fetch_user_activities(user, for_user, params)
|
||||||
ActivityPub.fetch_user_activities(user, for_user, %{
|
|
||||||
"limit" => 10,
|
|
||||||
"include_poll_votes" => true
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_resp_content_type("application/activity+json")
|
|> put_resp_content_type("application/activity+json")
|
||||||
|> put_view(UserView)
|
|> put_view(UserView)
|
||||||
|> render("activity_collection_page.json", %{
|
|> render("activity_collection_page.json", %{
|
||||||
activities: activities,
|
activities: activities,
|
||||||
|
pagination: ControllerHelper.get_pagination_fields(conn, activities),
|
||||||
iri: "#{user.ap_id}/outbox"
|
iri: "#{user.ap_id}/outbox"
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
@ -353,21 +350,24 @@ def read_inbox(
|
||||||
%{"nickname" => nickname, "page" => page?} = params
|
%{"nickname" => nickname, "page" => page?} = params
|
||||||
)
|
)
|
||||||
when page? in [true, "true"] do
|
when page? in [true, "true"] do
|
||||||
|
params =
|
||||||
|
params
|
||||||
|
|> Map.drop(["nickname", "page"])
|
||||||
|
|> Map.put("blocking_user", user)
|
||||||
|
|> Map.put("user", user)
|
||||||
|
|> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
if params["max_id"] do
|
[user.ap_id | User.following(user)]
|
||||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], %{
|
|> ActivityPub.fetch_activities(params)
|
||||||
"max_id" => params["max_id"],
|
|> Enum.reverse()
|
||||||
"limit" => 10
|
|
||||||
})
|
|
||||||
else
|
|
||||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], %{"limit" => 10})
|
|
||||||
end
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_resp_content_type("application/activity+json")
|
|> put_resp_content_type("application/activity+json")
|
||||||
|> put_view(UserView)
|
|> put_view(UserView)
|
||||||
|> render("activity_collection_page.json", %{
|
|> render("activity_collection_page.json", %{
|
||||||
activities: activities,
|
activities: activities,
|
||||||
|
pagination: ControllerHelper.get_pagination_fields(conn, activities),
|
||||||
iri: "#{user.ap_id}/inbox"
|
iri: "#{user.ap_id}/inbox"
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.EarmarkRenderer
|
alias Pleroma.EarmarkRenderer
|
||||||
alias Pleroma.FollowingRelationship
|
alias Pleroma.FollowingRelationship
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Object.Containment
|
alias Pleroma.Object.Containment
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -208,12 +209,6 @@ def fix_context(object) do
|
||||||
|> Map.put("conversation", context)
|
|> Map.put("conversation", context)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp add_if_present(map, _key, nil), do: map
|
|
||||||
|
|
||||||
defp add_if_present(map, key, value) do
|
|
||||||
Map.put(map, key, value)
|
|
||||||
end
|
|
||||||
|
|
||||||
def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachment) do
|
def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachment) do
|
||||||
attachments =
|
attachments =
|
||||||
Enum.map(attachment, fn data ->
|
Enum.map(attachment, fn data ->
|
||||||
|
@ -241,13 +236,13 @@ def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachm
|
||||||
|
|
||||||
attachment_url =
|
attachment_url =
|
||||||
%{"href" => href}
|
%{"href" => href}
|
||||||
|> add_if_present("mediaType", media_type)
|
|> Maps.put_if_present("mediaType", media_type)
|
||||||
|> add_if_present("type", Map.get(url || %{}, "type"))
|
|> Maps.put_if_present("type", Map.get(url || %{}, "type"))
|
||||||
|
|
||||||
%{"url" => [attachment_url]}
|
%{"url" => [attachment_url]}
|
||||||
|> add_if_present("mediaType", media_type)
|
|> Maps.put_if_present("mediaType", media_type)
|
||||||
|> add_if_present("type", data["type"])
|
|> Maps.put_if_present("type", data["type"])
|
||||||
|> add_if_present("name", data["name"])
|
|> Maps.put_if_present("name", data["name"])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Map.put(object, "attachment", attachments)
|
Map.put(object, "attachment", attachments)
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
alias Ecto.UUID
|
alias Ecto.UUID
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -244,7 +245,7 @@ defp lazy_put_object_defaults(activity, _), do: activity
|
||||||
Inserts a full object if it is contained in an activity.
|
Inserts a full object if it is contained in an activity.
|
||||||
"""
|
"""
|
||||||
def insert_full_object(%{"object" => %{"type" => type} = object_data} = map)
|
def insert_full_object(%{"object" => %{"type" => type} = object_data} = map)
|
||||||
when is_map(object_data) and type in @supported_object_types do
|
when type in @supported_object_types do
|
||||||
with {:ok, object} <- Object.create(object_data) do
|
with {:ok, object} <- Object.create(object_data) do
|
||||||
map = Map.put(map, "object", object.data["id"])
|
map = Map.put(map, "object", object.data["id"])
|
||||||
|
|
||||||
|
@ -307,7 +308,7 @@ def make_like_data(
|
||||||
"cc" => cc,
|
"cc" => cc,
|
||||||
"context" => object.data["context"]
|
"context" => object.data["context"]
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_emoji_reaction_data(user, object, emoji, activity_id) do
|
def make_emoji_reaction_data(user, object, emoji, activity_id) do
|
||||||
|
@ -477,7 +478,7 @@ def make_follow_data(
|
||||||
"object" => followed_id,
|
"object" => followed_id,
|
||||||
"state" => "pending"
|
"state" => "pending"
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
|
def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
|
||||||
|
@ -546,7 +547,7 @@ def make_announce_data(
|
||||||
"cc" => [],
|
"cc" => [],
|
||||||
"context" => object.data["context"]
|
"context" => object.data["context"]
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_announce_data(
|
def make_announce_data(
|
||||||
|
@ -563,7 +564,7 @@ def make_announce_data(
|
||||||
"cc" => [Pleroma.Constants.as_public()],
|
"cc" => [Pleroma.Constants.as_public()],
|
||||||
"context" => object.data["context"]
|
"context" => object.data["context"]
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_undo_data(
|
def make_undo_data(
|
||||||
|
@ -582,7 +583,7 @@ def make_undo_data(
|
||||||
"cc" => [Pleroma.Constants.as_public()],
|
"cc" => [Pleroma.Constants.as_public()],
|
||||||
"context" => context
|
"context" => context
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec add_announce_to_object(Activity.t(), Object.t()) ::
|
@spec add_announce_to_object(Activity.t(), Object.t()) ::
|
||||||
|
@ -627,7 +628,7 @@ def make_unfollow_data(follower, followed, follow_activity, activity_id) do
|
||||||
"to" => [followed.ap_id],
|
"to" => [followed.ap_id],
|
||||||
"object" => follow_activity.data
|
"object" => follow_activity.data
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
#### Block-related helpers
|
#### Block-related helpers
|
||||||
|
@ -650,7 +651,7 @@ def make_block_data(blocker, blocked, activity_id) do
|
||||||
"to" => [blocked.ap_id],
|
"to" => [blocked.ap_id],
|
||||||
"object" => blocked.ap_id
|
"object" => blocked.ap_id
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
#### Create-related helpers
|
#### Create-related helpers
|
||||||
|
@ -740,12 +741,12 @@ defp build_flag_object(_), do: []
|
||||||
def get_reports(params, page, page_size) do
|
def get_reports(params, page, page_size) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.put("type", "Flag")
|
|> Map.put(:type, "Flag")
|
||||||
|> Map.put("skip_preload", true)
|
|> Map.put(:skip_preload, true)
|
||||||
|> Map.put("preload_report_notes", true)
|
|> Map.put(:preload_report_notes, true)
|
||||||
|> Map.put("total", true)
|
|> Map.put(:total, true)
|
||||||
|> Map.put("limit", page_size)
|
|> Map.put(:limit, page_size)
|
||||||
|> Map.put("offset", (page - 1) * page_size)
|
|> Map.put(:offset, (page - 1) * page_size)
|
||||||
|
|
||||||
ActivityPub.fetch_activities([], params, :offset)
|
ActivityPub.fetch_activities([], params, :offset)
|
||||||
end
|
end
|
||||||
|
@ -870,7 +871,4 @@ def get_existing_votes(actor, %{data: %{"id" => id}}) do
|
||||||
|> where([a, object: o], fragment("(?)->>'type' = 'Answer'", o.data))
|
|> where([a, object: o], fragment("(?)->>'type' = 'Answer'", o.data))
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
def maybe_put(map, _key, nil), do: map
|
|
||||||
def maybe_put(map, key, value), do: Map.put(map, key, value)
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -213,34 +213,24 @@ def render("activity_collection.json", %{iri: iri}) do
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("activity_collection_page.json", %{activities: activities, iri: iri}) do
|
def render("activity_collection_page.json", %{
|
||||||
# this is sorted chronologically, so first activity is the newest (max)
|
activities: activities,
|
||||||
{max_id, min_id, collection} =
|
iri: iri,
|
||||||
if length(activities) > 0 do
|
pagination: pagination
|
||||||
{
|
}) do
|
||||||
Enum.at(activities, 0).id,
|
collection =
|
||||||
Enum.at(Enum.reverse(activities), 0).id,
|
Enum.map(activities, fn activity ->
|
||||||
Enum.map(activities, fn act ->
|
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
||||||
{:ok, data} = Transmogrifier.prepare_outgoing(act.data)
|
|
||||||
data
|
data
|
||||||
end)
|
end)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
[]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"id" => "#{iri}?max_id=#{max_id}&page=true",
|
|
||||||
"type" => "OrderedCollectionPage",
|
"type" => "OrderedCollectionPage",
|
||||||
"partOf" => iri,
|
"partOf" => iri,
|
||||||
"orderedItems" => collection,
|
"orderedItems" => collection
|
||||||
"next" => "#{iri}?max_id=#{min_id}&page=true"
|
|
||||||
}
|
}
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
|
|> Map.merge(pagination)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_put_total_items(map, false, _total), do: map
|
defp maybe_put_total_items(map, false, _total), do: map
|
||||||
|
|
|
@ -7,38 +7,24 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
alias Pleroma.Activity
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.ConfigDB
|
|
||||||
alias Pleroma.MFA
|
alias Pleroma.MFA
|
||||||
alias Pleroma.ModerationLog
|
alias Pleroma.ModerationLog
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.ReportNote
|
|
||||||
alias Pleroma.Stats
|
alias Pleroma.Stats
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserInviteToken
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Builder
|
alias Pleroma.Web.ActivityPub.Builder
|
||||||
alias Pleroma.Web.ActivityPub.Pipeline
|
alias Pleroma.Web.ActivityPub.Pipeline
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
|
||||||
alias Pleroma.Web.AdminAPI
|
alias Pleroma.Web.AdminAPI
|
||||||
alias Pleroma.Web.AdminAPI.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
alias Pleroma.Web.AdminAPI.ConfigView
|
|
||||||
alias Pleroma.Web.AdminAPI.ModerationLogView
|
alias Pleroma.Web.AdminAPI.ModerationLogView
|
||||||
alias Pleroma.Web.AdminAPI.Report
|
|
||||||
alias Pleroma.Web.AdminAPI.ReportView
|
|
||||||
alias Pleroma.Web.AdminAPI.Search
|
alias Pleroma.Web.AdminAPI.Search
|
||||||
alias Pleroma.Web.CommonAPI
|
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.MastodonAPI
|
|
||||||
alias Pleroma.Web.MastodonAPI.AppView
|
|
||||||
alias Pleroma.Web.OAuth.App
|
|
||||||
alias Pleroma.Web.Router
|
alias Pleroma.Web.Router
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@descriptions Pleroma.Docs.JSON.compile()
|
|
||||||
@users_page_size 50
|
@users_page_size 50
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -69,30 +55,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :invites)
|
|
||||||
|
|
||||||
plug(
|
|
||||||
OAuthScopesPlug,
|
|
||||||
%{scopes: ["write:invites"], admin: true}
|
|
||||||
when action in [:create_invite_token, :revoke_invite, :email_invite]
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:follows"], admin: true}
|
%{scopes: ["write:follows"], admin: true}
|
||||||
when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow]
|
when action in [:user_follow, :user_unfollow]
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
|
||||||
OAuthScopesPlug,
|
|
||||||
%{scopes: ["read:reports"], admin: true}
|
|
||||||
when action in [:list_reports, :report_show]
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
|
||||||
OAuthScopesPlug,
|
|
||||||
%{scopes: ["write:reports"], admin: true}
|
|
||||||
when action in [:reports_update, :report_notes_create, :report_notes_delete]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -105,11 +71,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read"], admin: true}
|
%{scopes: ["read"], admin: true}
|
||||||
when action in [
|
when action in [
|
||||||
:config_show,
|
|
||||||
:list_log,
|
:list_log,
|
||||||
:stats,
|
:stats,
|
||||||
:relay_list,
|
|
||||||
:config_descriptions,
|
|
||||||
:need_reboot
|
:need_reboot
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -119,13 +82,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
%{scopes: ["write"], admin: true}
|
%{scopes: ["write"], admin: true}
|
||||||
when action in [
|
when action in [
|
||||||
:restart,
|
:restart,
|
||||||
:config_update,
|
|
||||||
:resend_confirmation_email,
|
:resend_confirmation_email,
|
||||||
:confirm_email,
|
:confirm_email,
|
||||||
:oauth_app_create,
|
|
||||||
:oauth_app_list,
|
|
||||||
:oauth_app_update,
|
|
||||||
:oauth_app_delete,
|
|
||||||
:reload_emoji
|
:reload_emoji
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -268,10 +226,10 @@ def list_instance_statuses(conn, %{"instance" => instance} = params) do
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_statuses(nil, %{
|
ActivityPub.fetch_statuses(nil, %{
|
||||||
"instance" => instance,
|
instance: instance,
|
||||||
"limit" => page_size,
|
limit: page_size,
|
||||||
"offset" => (page - 1) * page_size,
|
offset: (page - 1) * page_size,
|
||||||
"exclude_reblogs" => !with_reblogs && "true"
|
exclude_reblogs: not with_reblogs
|
||||||
})
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -288,13 +246,13 @@ def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_user_activities(user, nil, %{
|
ActivityPub.fetch_user_activities(user, nil, %{
|
||||||
"limit" => page_size,
|
limit: page_size,
|
||||||
"godmode" => godmode,
|
godmode: godmode,
|
||||||
"exclude_reblogs" => !with_reblogs && "true"
|
exclude_reblogs: not with_reblogs
|
||||||
})
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(MastodonAPI.StatusView)
|
|> put_view(AdminAPI.StatusView)
|
||||||
|> render("index.json", %{activities: activities, as: :activity})
|
|> render("index.json", %{activities: activities, as: :activity})
|
||||||
else
|
else
|
||||||
_ -> {:error, :not_found}
|
_ -> {:error, :not_found}
|
||||||
|
@ -531,113 +489,6 @@ def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname"
|
||||||
render_error(conn, :forbidden, "You can't revoke your own admin status.")
|
render_error(conn, :forbidden, "You can't revoke your own admin status.")
|
||||||
end
|
end
|
||||||
|
|
||||||
def relay_list(conn, _params) do
|
|
||||||
with {:ok, list} <- Relay.list() do
|
|
||||||
json(conn, %{relays: list})
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
conn
|
|
||||||
|> put_status(500)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
|
|
||||||
with {:ok, _message} <- Relay.follow(target) do
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
action: "relay_follow",
|
|
||||||
actor: admin,
|
|
||||||
target: target
|
|
||||||
})
|
|
||||||
|
|
||||||
json(conn, target)
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
conn
|
|
||||||
|> put_status(500)
|
|
||||||
|> json(target)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
|
|
||||||
with {:ok, _message} <- Relay.unfollow(target) do
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
action: "relay_unfollow",
|
|
||||||
actor: admin,
|
|
||||||
target: target
|
|
||||||
})
|
|
||||||
|
|
||||||
json(conn, target)
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
conn
|
|
||||||
|> put_status(500)
|
|
||||||
|> json(target)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Sends registration invite via email"
|
|
||||||
def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
|
|
||||||
with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
|
|
||||||
{_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
|
|
||||||
{:ok, invite_token} <- UserInviteToken.create_invite(),
|
|
||||||
email <-
|
|
||||||
Pleroma.Emails.UserEmail.user_invitation_email(
|
|
||||||
user,
|
|
||||||
invite_token,
|
|
||||||
email,
|
|
||||||
params["name"]
|
|
||||||
),
|
|
||||||
{:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
|
|
||||||
json_response(conn, :no_content, "")
|
|
||||||
else
|
|
||||||
{:registrations_open, _} ->
|
|
||||||
{:error, "To send invites you need to set the `registrations_open` option to false."}
|
|
||||||
|
|
||||||
{:invites_enabled, _} ->
|
|
||||||
{:error, "To send invites you need to set the `invites_enabled` option to true."}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Create an account registration invite token"
|
|
||||||
def create_invite_token(conn, params) do
|
|
||||||
opts = %{}
|
|
||||||
|
|
||||||
opts =
|
|
||||||
if params["max_use"],
|
|
||||||
do: Map.put(opts, :max_use, params["max_use"]),
|
|
||||||
else: opts
|
|
||||||
|
|
||||||
opts =
|
|
||||||
if params["expires_at"],
|
|
||||||
do: Map.put(opts, :expires_at, params["expires_at"]),
|
|
||||||
else: opts
|
|
||||||
|
|
||||||
{:ok, invite} = UserInviteToken.create_invite(opts)
|
|
||||||
|
|
||||||
json(conn, AccountView.render("invite.json", %{invite: invite}))
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Get list of created invites"
|
|
||||||
def invites(conn, _params) do
|
|
||||||
invites = UserInviteToken.list_invites()
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(AccountView)
|
|
||||||
|> render("invites.json", %{invites: invites})
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Revokes invite by token"
|
|
||||||
def revoke_invite(conn, %{"token" => token}) do
|
|
||||||
with {:ok, invite} <- UserInviteToken.find_by_token(token),
|
|
||||||
{:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
|
|
||||||
conn
|
|
||||||
|> put_view(AccountView)
|
|
||||||
|> render("invite.json", %{invite: updated_invite})
|
|
||||||
else
|
|
||||||
nil -> {:error, :not_found}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Get a password reset token (base64 string) for given nickname"
|
@doc "Get a password reset token (base64 string) for given nickname"
|
||||||
def get_password_reset(conn, %{"nickname" => nickname}) do
|
def get_password_reset(conn, %{"nickname" => nickname}) do
|
||||||
(%User{local: true} = user) = User.get_cached_by_nickname(nickname)
|
(%User{local: true} = user) = User.get_cached_by_nickname(nickname)
|
||||||
|
@ -724,85 +575,6 @@ def update_user_credentials(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_reports(conn, params) do
|
|
||||||
{page, page_size} = page_params(params)
|
|
||||||
|
|
||||||
reports = Utils.get_reports(params, page, page_size)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(ReportView)
|
|
||||||
|> render("index.json", %{reports: reports})
|
|
||||||
end
|
|
||||||
|
|
||||||
def report_show(conn, %{"id" => id}) do
|
|
||||||
with %Activity{} = report <- Activity.get_by_id(id) do
|
|
||||||
conn
|
|
||||||
|> put_view(ReportView)
|
|
||||||
|> render("show.json", Report.extract_report_info(report))
|
|
||||||
else
|
|
||||||
_ -> {:error, :not_found}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
|
|
||||||
result =
|
|
||||||
reports
|
|
||||||
|> Enum.map(fn report ->
|
|
||||||
with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
action: "report_update",
|
|
||||||
actor: admin,
|
|
||||||
subject: activity
|
|
||||||
})
|
|
||||||
|
|
||||||
activity
|
|
||||||
else
|
|
||||||
{:error, message} -> %{id: report["id"], error: message}
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
case Enum.any?(result, &Map.has_key?(&1, :error)) do
|
|
||||||
true -> json_response(conn, :bad_request, result)
|
|
||||||
false -> json_response(conn, :no_content, "")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def report_notes_create(%{assigns: %{user: user}} = conn, %{
|
|
||||||
"id" => report_id,
|
|
||||||
"content" => content
|
|
||||||
}) do
|
|
||||||
with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
action: "report_note",
|
|
||||||
actor: user,
|
|
||||||
subject: Activity.get_by_id(report_id),
|
|
||||||
text: content
|
|
||||||
})
|
|
||||||
|
|
||||||
json_response(conn, :no_content, "")
|
|
||||||
else
|
|
||||||
_ -> json_response(conn, :bad_request, "")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def report_notes_delete(%{assigns: %{user: user}} = conn, %{
|
|
||||||
"id" => note_id,
|
|
||||||
"report_id" => report_id
|
|
||||||
}) do
|
|
||||||
with {:ok, note} <- ReportNote.destroy(note_id) do
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
action: "report_note_delete",
|
|
||||||
actor: user,
|
|
||||||
subject: Activity.get_by_id(report_id),
|
|
||||||
text: note.content
|
|
||||||
})
|
|
||||||
|
|
||||||
json_response(conn, :no_content, "")
|
|
||||||
else
|
|
||||||
_ -> json_response(conn, :bad_request, "")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_log(conn, params) do
|
def list_log(conn, params) do
|
||||||
{page, page_size} = page_params(params)
|
{page, page_size} = page_params(params)
|
||||||
|
|
||||||
|
@ -821,105 +593,6 @@ def list_log(conn, params) do
|
||||||
|> render("index.json", %{log: log})
|
|> render("index.json", %{log: log})
|
||||||
end
|
end
|
||||||
|
|
||||||
def config_descriptions(conn, _params) do
|
|
||||||
descriptions = Enum.filter(@descriptions, &whitelisted_config?/1)
|
|
||||||
|
|
||||||
json(conn, descriptions)
|
|
||||||
end
|
|
||||||
|
|
||||||
def config_show(conn, %{"only_db" => true}) do
|
|
||||||
with :ok <- configurable_from_database() do
|
|
||||||
configs = Pleroma.Repo.all(ConfigDB)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(ConfigView)
|
|
||||||
|> render("index.json", %{configs: configs})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def config_show(conn, _params) do
|
|
||||||
with :ok <- configurable_from_database() do
|
|
||||||
configs = ConfigDB.get_all_as_keyword()
|
|
||||||
|
|
||||||
merged =
|
|
||||||
Config.Holder.default_config()
|
|
||||||
|> ConfigDB.merge(configs)
|
|
||||||
|> Enum.map(fn {group, values} ->
|
|
||||||
Enum.map(values, fn {key, value} ->
|
|
||||||
db =
|
|
||||||
if configs[group][key] do
|
|
||||||
ConfigDB.get_db_keys(configs[group][key], key)
|
|
||||||
end
|
|
||||||
|
|
||||||
db_value = configs[group][key]
|
|
||||||
|
|
||||||
merged_value =
|
|
||||||
if !is_nil(db_value) and Keyword.keyword?(db_value) and
|
|
||||||
ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
|
|
||||||
ConfigDB.merge_group(group, key, value, db_value)
|
|
||||||
else
|
|
||||||
value
|
|
||||||
end
|
|
||||||
|
|
||||||
setting = %{
|
|
||||||
group: ConfigDB.convert(group),
|
|
||||||
key: ConfigDB.convert(key),
|
|
||||||
value: ConfigDB.convert(merged_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if db, do: Map.put(setting, :db, db), else: setting
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|> List.flatten()
|
|
||||||
|
|
||||||
json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def config_update(conn, %{"configs" => configs}) do
|
|
||||||
with :ok <- configurable_from_database() do
|
|
||||||
{_errors, results} =
|
|
||||||
configs
|
|
||||||
|> Enum.filter(&whitelisted_config?/1)
|
|
||||||
|> Enum.map(fn
|
|
||||||
%{"group" => group, "key" => key, "delete" => true} = params ->
|
|
||||||
ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
|
|
||||||
|
|
||||||
%{"group" => group, "key" => key, "value" => value} ->
|
|
||||||
ConfigDB.update_or_create(%{group: group, key: key, value: value})
|
|
||||||
end)
|
|
||||||
|> Enum.split_with(fn result -> elem(result, 0) == :error end)
|
|
||||||
|
|
||||||
{deleted, updated} =
|
|
||||||
results
|
|
||||||
|> Enum.map(fn {:ok, config} ->
|
|
||||||
Map.put(config, :db, ConfigDB.get_db_keys(config))
|
|
||||||
end)
|
|
||||||
|> Enum.split_with(fn config ->
|
|
||||||
Ecto.get_meta(config, :state) == :deleted
|
|
||||||
end)
|
|
||||||
|
|
||||||
Config.TransferTask.load_and_update_env(deleted, false)
|
|
||||||
|
|
||||||
if !Restarter.Pleroma.need_reboot?() do
|
|
||||||
changed_reboot_settings? =
|
|
||||||
(updated ++ deleted)
|
|
||||||
|> Enum.any?(fn config ->
|
|
||||||
group = ConfigDB.from_string(config.group)
|
|
||||||
key = ConfigDB.from_string(config.key)
|
|
||||||
value = ConfigDB.from_binary(config.value)
|
|
||||||
Config.TransferTask.pleroma_need_restart?(group, key, value)
|
|
||||||
end)
|
|
||||||
|
|
||||||
if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
|
|
||||||
end
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(ConfigView)
|
|
||||||
|> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def restart(conn, _params) do
|
def restart(conn, _params) do
|
||||||
with :ok <- configurable_from_database() do
|
with :ok <- configurable_from_database() do
|
||||||
Restarter.Pleroma.restart(Config.get(:env), 50)
|
Restarter.Pleroma.restart(Config.get(:env), 50)
|
||||||
|
@ -940,28 +613,6 @@ defp configurable_from_database do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp whitelisted_config?(group, key) do
|
|
||||||
if whitelisted_configs = Config.get(:database_config_whitelist) do
|
|
||||||
Enum.any?(whitelisted_configs, fn
|
|
||||||
{whitelisted_group} ->
|
|
||||||
group == inspect(whitelisted_group)
|
|
||||||
|
|
||||||
{whitelisted_group, whitelisted_key} ->
|
|
||||||
group == inspect(whitelisted_group) && key == inspect(whitelisted_key)
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp whitelisted_config?(%{"group" => group, "key" => key}) do
|
|
||||||
whitelisted_config?(group, key)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp whitelisted_config?(%{:group => group} = config) do
|
|
||||||
whitelisted_config?(group, config[:key])
|
|
||||||
end
|
|
||||||
|
|
||||||
def reload_emoji(conn, _params) do
|
def reload_emoji(conn, _params) do
|
||||||
Pleroma.Emoji.reload()
|
Pleroma.Emoji.reload()
|
||||||
|
|
||||||
|
@ -996,83 +647,6 @@ def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" =
|
||||||
conn |> json("")
|
conn |> json("")
|
||||||
end
|
end
|
||||||
|
|
||||||
def oauth_app_create(conn, params) do
|
|
||||||
params =
|
|
||||||
if params["name"] do
|
|
||||||
Map.put(params, "client_name", params["name"])
|
|
||||||
else
|
|
||||||
params
|
|
||||||
end
|
|
||||||
|
|
||||||
result =
|
|
||||||
case App.create(params) do
|
|
||||||
{:ok, app} ->
|
|
||||||
AppView.render("show.json", %{app: app, admin: true})
|
|
||||||
|
|
||||||
{:error, changeset} ->
|
|
||||||
App.errors(changeset)
|
|
||||||
end
|
|
||||||
|
|
||||||
json(conn, result)
|
|
||||||
end
|
|
||||||
|
|
||||||
def oauth_app_update(conn, params) do
|
|
||||||
params =
|
|
||||||
if params["name"] do
|
|
||||||
Map.put(params, "client_name", params["name"])
|
|
||||||
else
|
|
||||||
params
|
|
||||||
end
|
|
||||||
|
|
||||||
with {:ok, app} <- App.update(params) do
|
|
||||||
json(conn, AppView.render("show.json", %{app: app, admin: true}))
|
|
||||||
else
|
|
||||||
{:error, changeset} ->
|
|
||||||
json(conn, App.errors(changeset))
|
|
||||||
|
|
||||||
nil ->
|
|
||||||
json_response(conn, :bad_request, "")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def oauth_app_list(conn, params) do
|
|
||||||
{page, page_size} = page_params(params)
|
|
||||||
|
|
||||||
search_params = %{
|
|
||||||
client_name: params["name"],
|
|
||||||
client_id: params["client_id"],
|
|
||||||
page: page,
|
|
||||||
page_size: page_size
|
|
||||||
}
|
|
||||||
|
|
||||||
search_params =
|
|
||||||
if Map.has_key?(params, "trusted") do
|
|
||||||
Map.put(search_params, :trusted, params["trusted"])
|
|
||||||
else
|
|
||||||
search_params
|
|
||||||
end
|
|
||||||
|
|
||||||
with {:ok, apps, count} <- App.search(search_params) do
|
|
||||||
json(
|
|
||||||
conn,
|
|
||||||
AppView.render("index.json",
|
|
||||||
apps: apps,
|
|
||||||
count: count,
|
|
||||||
page_size: page_size,
|
|
||||||
admin: true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def oauth_app_delete(conn, params) do
|
|
||||||
with {:ok, _app} <- App.destroy(params["id"]) do
|
|
||||||
json_response(conn, :no_content, "")
|
|
||||||
else
|
|
||||||
_ -> json_response(conn, :bad_request, "")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def stats(conn, _) do
|
def stats(conn, _) do
|
||||||
count = Stats.get_status_visibility_count()
|
count = Stats.get_status_visibility_count()
|
||||||
|
|
||||||
|
|
152
lib/pleroma/web/admin_api/controllers/config_controller.ex
Normal file
152
lib/pleroma/web/admin_api/controllers/config_controller.ex
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.ConfigController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.ConfigDB
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
@descriptions Pleroma.Docs.JSON.compile()
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action == :update)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["read"], admin: true}
|
||||||
|
when action in [:show, :descriptions]
|
||||||
|
)
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ConfigOperation
|
||||||
|
|
||||||
|
def descriptions(conn, _params) do
|
||||||
|
descriptions = Enum.filter(@descriptions, &whitelisted_config?/1)
|
||||||
|
|
||||||
|
json(conn, descriptions)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(conn, %{only_db: true}) do
|
||||||
|
with :ok <- configurable_from_database() do
|
||||||
|
configs = Pleroma.Repo.all(ConfigDB)
|
||||||
|
render(conn, "index.json", %{configs: configs})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(conn, _params) do
|
||||||
|
with :ok <- configurable_from_database() do
|
||||||
|
configs = ConfigDB.get_all_as_keyword()
|
||||||
|
|
||||||
|
merged =
|
||||||
|
Config.Holder.default_config()
|
||||||
|
|> ConfigDB.merge(configs)
|
||||||
|
|> Enum.map(fn {group, values} ->
|
||||||
|
Enum.map(values, fn {key, value} ->
|
||||||
|
db =
|
||||||
|
if configs[group][key] do
|
||||||
|
ConfigDB.get_db_keys(configs[group][key], key)
|
||||||
|
end
|
||||||
|
|
||||||
|
db_value = configs[group][key]
|
||||||
|
|
||||||
|
merged_value =
|
||||||
|
if not is_nil(db_value) and Keyword.keyword?(db_value) and
|
||||||
|
ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
|
||||||
|
ConfigDB.merge_group(group, key, value, db_value)
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
%{
|
||||||
|
group: ConfigDB.convert(group),
|
||||||
|
key: ConfigDB.convert(key),
|
||||||
|
value: ConfigDB.convert(merged_value)
|
||||||
|
}
|
||||||
|
|> Pleroma.Maps.put_if_present(:db, db)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|> List.flatten()
|
||||||
|
|
||||||
|
json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(%{body_params: %{configs: configs}} = conn, _) do
|
||||||
|
with :ok <- configurable_from_database() do
|
||||||
|
results =
|
||||||
|
configs
|
||||||
|
|> Enum.filter(&whitelisted_config?/1)
|
||||||
|
|> Enum.map(fn
|
||||||
|
%{group: group, key: key, delete: true} = params ->
|
||||||
|
ConfigDB.delete(%{group: group, key: key, subkeys: params[:subkeys]})
|
||||||
|
|
||||||
|
%{group: group, key: key, value: value} ->
|
||||||
|
ConfigDB.update_or_create(%{group: group, key: key, value: value})
|
||||||
|
end)
|
||||||
|
|> Enum.reject(fn {result, _} -> result == :error end)
|
||||||
|
|
||||||
|
{deleted, updated} =
|
||||||
|
results
|
||||||
|
|> Enum.map(fn {:ok, config} ->
|
||||||
|
Map.put(config, :db, ConfigDB.get_db_keys(config))
|
||||||
|
end)
|
||||||
|
|> Enum.split_with(fn config ->
|
||||||
|
Ecto.get_meta(config, :state) == :deleted
|
||||||
|
end)
|
||||||
|
|
||||||
|
Config.TransferTask.load_and_update_env(deleted, false)
|
||||||
|
|
||||||
|
if not Restarter.Pleroma.need_reboot?() do
|
||||||
|
changed_reboot_settings? =
|
||||||
|
(updated ++ deleted)
|
||||||
|
|> Enum.any?(fn config ->
|
||||||
|
group = ConfigDB.from_string(config.group)
|
||||||
|
key = ConfigDB.from_string(config.key)
|
||||||
|
value = ConfigDB.from_binary(config.value)
|
||||||
|
Config.TransferTask.pleroma_need_restart?(group, key, value)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
|
||||||
|
end
|
||||||
|
|
||||||
|
render(conn, "index.json", %{
|
||||||
|
configs: updated,
|
||||||
|
need_reboot: Restarter.Pleroma.need_reboot?()
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp configurable_from_database do
|
||||||
|
if Config.get(:configurable_from_database) do
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
{:error, "To use this endpoint you need to enable configuration from database."}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp whitelisted_config?(group, key) do
|
||||||
|
if whitelisted_configs = Config.get(:database_config_whitelist) do
|
||||||
|
Enum.any?(whitelisted_configs, fn
|
||||||
|
{whitelisted_group} ->
|
||||||
|
group == inspect(whitelisted_group)
|
||||||
|
|
||||||
|
{whitelisted_group, whitelisted_key} ->
|
||||||
|
group == inspect(whitelisted_group) && key == inspect(whitelisted_key)
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp whitelisted_config?(%{group: group, key: key}) do
|
||||||
|
whitelisted_config?(group, key)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp whitelisted_config?(%{group: group} = config) do
|
||||||
|
whitelisted_config?(group, config[:key])
|
||||||
|
end
|
||||||
|
end
|
78
lib/pleroma/web/admin_api/controllers/invite_controller.ex
Normal file
78
lib/pleroma/web/admin_api/controllers/invite_controller.ex
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.InviteController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.UserInviteToken
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :index)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["write:invites"], admin: true} when action in [:create, :revoke, :email]
|
||||||
|
)
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.InviteOperation
|
||||||
|
|
||||||
|
@doc "Get list of created invites"
|
||||||
|
def index(conn, _params) do
|
||||||
|
invites = UserInviteToken.list_invites()
|
||||||
|
|
||||||
|
render(conn, "index.json", invites: invites)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Create an account registration invite token"
|
||||||
|
def create(%{body_params: params} = conn, _) do
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite(params)
|
||||||
|
|
||||||
|
render(conn, "show.json", invite: invite)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Revokes invite by token"
|
||||||
|
def revoke(%{body_params: %{token: token}} = conn, _) do
|
||||||
|
with {:ok, invite} <- UserInviteToken.find_by_token(token),
|
||||||
|
{:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
|
||||||
|
render(conn, "show.json", invite: updated_invite)
|
||||||
|
else
|
||||||
|
nil -> {:error, :not_found}
|
||||||
|
error -> error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Sends registration invite via email"
|
||||||
|
def email(%{assigns: %{user: user}, body_params: %{email: email} = params} = conn, _) do
|
||||||
|
with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
|
||||||
|
{_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
|
||||||
|
{:ok, invite_token} <- UserInviteToken.create_invite(),
|
||||||
|
{:ok, _} <-
|
||||||
|
user
|
||||||
|
|> Pleroma.Emails.UserEmail.user_invitation_email(
|
||||||
|
invite_token,
|
||||||
|
email,
|
||||||
|
params[:name]
|
||||||
|
)
|
||||||
|
|> Pleroma.Emails.Mailer.deliver() do
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
else
|
||||||
|
{:registrations_open, _} ->
|
||||||
|
{:error, "To send invites you need to set the `registrations_open` option to false."}
|
||||||
|
|
||||||
|
{:invites_enabled, _} ->
|
||||||
|
{:error, "To send invites you need to set the `invites_enabled` option to true."}
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
{:error, error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,77 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.OAuthAppController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.Web.OAuth.App
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
plug(:put_view, Pleroma.Web.MastodonAPI.AppView)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["write"], admin: true}
|
||||||
|
when action in [:create, :index, :update, :delete]
|
||||||
|
)
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.OAuthAppOperation
|
||||||
|
|
||||||
|
def index(conn, params) do
|
||||||
|
search_params =
|
||||||
|
params
|
||||||
|
|> Map.take([:client_id, :page, :page_size, :trusted])
|
||||||
|
|> Map.put(:client_name, params[:name])
|
||||||
|
|
||||||
|
with {:ok, apps, count} <- App.search(search_params) do
|
||||||
|
render(conn, "index.json",
|
||||||
|
apps: apps,
|
||||||
|
count: count,
|
||||||
|
page_size: params.page_size,
|
||||||
|
admin: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(%{body_params: params} = conn, _) do
|
||||||
|
params = Pleroma.Maps.put_if_present(params, :client_name, params[:name])
|
||||||
|
|
||||||
|
case App.create(params) do
|
||||||
|
{:ok, app} ->
|
||||||
|
render(conn, "show.json", app: app, admin: true)
|
||||||
|
|
||||||
|
{:error, changeset} ->
|
||||||
|
json(conn, App.errors(changeset))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(%{body_params: params} = conn, %{id: id}) do
|
||||||
|
params = Pleroma.Maps.put_if_present(params, :client_name, params[:name])
|
||||||
|
|
||||||
|
with {:ok, app} <- App.update(id, params) do
|
||||||
|
render(conn, "show.json", app: app, admin: true)
|
||||||
|
else
|
||||||
|
{:error, changeset} ->
|
||||||
|
json(conn, App.errors(changeset))
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
json_response(conn, :bad_request, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(conn, params) do
|
||||||
|
with {:ok, _app} <- App.destroy(params.id) do
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
else
|
||||||
|
_ -> json_response(conn, :bad_request, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
67
lib/pleroma/web/admin_api/controllers/relay_controller.ex
Normal file
67
lib/pleroma/web/admin_api/controllers/relay_controller.ex
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.RelayController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["write:follows"], admin: true}
|
||||||
|
when action in [:follow, :unfollow]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["read"], admin: true} when action == :index)
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.RelayOperation
|
||||||
|
|
||||||
|
def index(conn, _params) do
|
||||||
|
with {:ok, list} <- Relay.list() do
|
||||||
|
json(conn, %{relays: list})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def follow(%{assigns: %{user: admin}, body_params: %{relay_url: target}} = conn, _) do
|
||||||
|
with {:ok, _message} <- Relay.follow(target) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "relay_follow",
|
||||||
|
actor: admin,
|
||||||
|
target: target
|
||||||
|
})
|
||||||
|
|
||||||
|
json(conn, target)
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
conn
|
||||||
|
|> put_status(500)
|
||||||
|
|> json(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unfollow(%{assigns: %{user: admin}, body_params: %{relay_url: target}} = conn, _) do
|
||||||
|
with {:ok, _message} <- Relay.unfollow(target) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "relay_unfollow",
|
||||||
|
actor: admin,
|
||||||
|
target: target
|
||||||
|
})
|
||||||
|
|
||||||
|
json(conn, target)
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
conn
|
||||||
|
|> put_status(500)
|
||||||
|
|> json(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
107
lib/pleroma/web/admin_api/controllers/report_controller.ex
Normal file
107
lib/pleroma/web/admin_api/controllers/report_controller.ex
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.ReportController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.ReportNote
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.Web.AdminAPI
|
||||||
|
alias Pleroma.Web.AdminAPI.Report
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["read:reports"], admin: true} when action in [:index, :show])
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["write:reports"], admin: true}
|
||||||
|
when action in [:update, :notes_create, :notes_delete]
|
||||||
|
)
|
||||||
|
|
||||||
|
action_fallback(AdminAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ReportOperation
|
||||||
|
|
||||||
|
def index(conn, params) do
|
||||||
|
reports = Utils.get_reports(params, params.page, params.page_size)
|
||||||
|
|
||||||
|
render(conn, "index.json", reports: reports)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(conn, %{id: id}) do
|
||||||
|
with %Activity{} = report <- Activity.get_by_id(id) do
|
||||||
|
render(conn, "show.json", Report.extract_report_info(report))
|
||||||
|
else
|
||||||
|
_ -> {:error, :not_found}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(%{assigns: %{user: admin}, body_params: %{reports: reports}} = conn, _) do
|
||||||
|
result =
|
||||||
|
Enum.map(reports, fn report ->
|
||||||
|
case CommonAPI.update_report_state(report.id, report.state) do
|
||||||
|
{:ok, activity} ->
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "report_update",
|
||||||
|
actor: admin,
|
||||||
|
subject: activity
|
||||||
|
})
|
||||||
|
|
||||||
|
activity
|
||||||
|
|
||||||
|
{:error, message} ->
|
||||||
|
%{id: report.id, error: message}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
if Enum.any?(result, &Map.has_key?(&1, :error)) do
|
||||||
|
json_response(conn, :bad_request, result)
|
||||||
|
else
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def notes_create(%{assigns: %{user: user}, body_params: %{content: content}} = conn, %{
|
||||||
|
id: report_id
|
||||||
|
}) do
|
||||||
|
with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "report_note",
|
||||||
|
actor: user,
|
||||||
|
subject: Activity.get_by_id(report_id),
|
||||||
|
text: content
|
||||||
|
})
|
||||||
|
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
else
|
||||||
|
_ -> json_response(conn, :bad_request, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def notes_delete(%{assigns: %{user: user}} = conn, %{
|
||||||
|
id: note_id,
|
||||||
|
report_id: report_id
|
||||||
|
}) do
|
||||||
|
with {:ok, note} <- ReportNote.destroy(note_id) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "report_note_delete",
|
||||||
|
actor: user,
|
||||||
|
subject: Activity.get_by_id(report_id),
|
||||||
|
text: note.content
|
||||||
|
})
|
||||||
|
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
else
|
||||||
|
_ -> json_response(conn, :bad_request, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -29,11 +29,11 @@ defmodule Pleroma.Web.AdminAPI.StatusController do
|
||||||
def index(%{assigns: %{user: _admin}} = conn, params) do
|
def index(%{assigns: %{user: _admin}} = conn, params) do
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_statuses(nil, %{
|
ActivityPub.fetch_statuses(nil, %{
|
||||||
"godmode" => params.godmode,
|
godmode: params.godmode,
|
||||||
"local_only" => params.local_only,
|
local_only: params.local_only,
|
||||||
"limit" => params.page_size,
|
limit: params.page_size,
|
||||||
"offset" => (params.page - 1) * params.page_size,
|
offset: (params.page - 1) * params.page_size,
|
||||||
"exclude_reblogs" => not params.with_reblogs
|
exclude_reblogs: not params.with_reblogs
|
||||||
})
|
})
|
||||||
|
|
||||||
render(conn, "index.json", activities: activities, as: :activity)
|
render(conn, "index.json", activities: activities, as: :activity)
|
||||||
|
@ -41,9 +41,7 @@ def index(%{assigns: %{user: _admin}} = conn, params) do
|
||||||
|
|
||||||
def show(conn, %{id: id}) do
|
def show(conn, %{id: id}) do
|
||||||
with %Activity{} = activity <- Activity.get_by_id(id) do
|
with %Activity{} = activity <- Activity.get_by_id(id) do
|
||||||
conn
|
render(conn, "show.json", %{activity: activity})
|
||||||
|> put_view(MastodonAPI.StatusView)
|
|
||||||
|> render("show.json", %{activity: activity})
|
|
||||||
else
|
else
|
||||||
nil -> {:error, :not_found}
|
nil -> {:error, :not_found}
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,7 +21,7 @@ def user(params \\ %{}) do
|
||||||
query =
|
query =
|
||||||
params
|
params
|
||||||
|> Map.drop([:page, :page_size])
|
|> Map.drop([:page, :page_size])
|
||||||
|> Map.put(:exclude_service_users, true)
|
|> Map.put(:invisible, false)
|
||||||
|> User.Query.build()
|
|> User.Query.build()
|
||||||
|> order_by([u], u.nickname)
|
|> order_by([u], u.nickname)
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@ def user(params \\ %{}) do
|
||||||
count = Repo.aggregate(query, :count, :id)
|
count = Repo.aggregate(query, :count, :id)
|
||||||
|
|
||||||
results = Repo.all(paginated_query)
|
results = Repo.all(paginated_query)
|
||||||
|
|
||||||
{:ok, results, count}
|
{:ok, results, count}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -80,24 +80,6 @@ def render("show.json", %{user: user}) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("invite.json", %{invite: invite}) do
|
|
||||||
%{
|
|
||||||
"id" => invite.id,
|
|
||||||
"token" => invite.token,
|
|
||||||
"used" => invite.used,
|
|
||||||
"expires_at" => invite.expires_at,
|
|
||||||
"uses" => invite.uses,
|
|
||||||
"max_use" => invite.max_use,
|
|
||||||
"invite_type" => invite.invite_type
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def render("invites.json", %{invites: invites}) do
|
|
||||||
%{
|
|
||||||
invites: render_many(invites, AccountView, "invite.json", as: :invite)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def render("created.json", %{user: user}) do
|
def render("created.json", %{user: user}) do
|
||||||
%{
|
%{
|
||||||
type: "success",
|
type: "success",
|
||||||
|
|
25
lib/pleroma/web/admin_api/views/invite_view.ex
Normal file
25
lib/pleroma/web/admin_api/views/invite_view.ex
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.InviteView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
def render("index.json", %{invites: invites}) do
|
||||||
|
%{
|
||||||
|
invites: render_many(invites, __MODULE__, "show.json", as: :invite)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("show.json", %{invite: invite}) do
|
||||||
|
%{
|
||||||
|
"id" => invite.id,
|
||||||
|
"token" => invite.token,
|
||||||
|
"used" => invite.used,
|
||||||
|
"expires_at" => invite.expires_at,
|
||||||
|
"uses" => invite.uses,
|
||||||
|
"max_use" => invite.max_use,
|
||||||
|
"invite_type" => invite.invite_type
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -18,7 +18,7 @@ def render("index.json", %{reports: reports}) do
|
||||||
%{
|
%{
|
||||||
reports:
|
reports:
|
||||||
reports[:items]
|
reports[:items]
|
||||||
|> Enum.map(&Report.extract_report_info(&1))
|
|> Enum.map(&Report.extract_report_info/1)
|
||||||
|> Enum.map(&render(__MODULE__, "show.json", &1))
|
|> Enum.map(&render(__MODULE__, "show.json", &1))
|
||||||
|> Enum.reverse(),
|
|> Enum.reverse(),
|
||||||
total: reports[:total]
|
total: reports[:total]
|
||||||
|
|
142
lib/pleroma/web/api_spec/operations/admin/config_operation.ex
Normal file
142
lib/pleroma/web/api_spec/operations/admin/config_operation.ex
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.Admin.ConfigOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Config"],
|
||||||
|
summary: "Get list of merged default settings with saved in database",
|
||||||
|
operationId: "AdminAPI.ConfigController.show",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(
|
||||||
|
:only_db,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :boolean, default: false},
|
||||||
|
"Get only saved in database settings"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
security: [%{"oAuth" => ["read"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Config", "application/json", config_response()),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Config"],
|
||||||
|
summary: "Update config settings",
|
||||||
|
operationId: "AdminAPI.ConfigController.update",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
requestBody:
|
||||||
|
request_body("Parameters", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
configs: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
group: %Schema{type: :string},
|
||||||
|
key: %Schema{type: :string},
|
||||||
|
value: any(),
|
||||||
|
delete: %Schema{type: :boolean},
|
||||||
|
subkeys: %Schema{type: :array, items: %Schema{type: :string}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Config", "application/json", config_response()),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def descriptions_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Config"],
|
||||||
|
summary: "Get JSON with config descriptions.",
|
||||||
|
operationId: "AdminAPI.ConfigController.descriptions",
|
||||||
|
security: [%{"oAuth" => ["read"]}],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Config Descriptions", "application/json", %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
group: %Schema{type: :string},
|
||||||
|
key: %Schema{type: :string},
|
||||||
|
type: %Schema{oneOf: [%Schema{type: :string}, %Schema{type: :array}]},
|
||||||
|
description: %Schema{type: :string},
|
||||||
|
children: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
key: %Schema{type: :string},
|
||||||
|
type: %Schema{oneOf: [%Schema{type: :string}, %Schema{type: :array}]},
|
||||||
|
description: %Schema{type: :string},
|
||||||
|
suggestions: %Schema{type: :array}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp any do
|
||||||
|
%Schema{
|
||||||
|
oneOf: [
|
||||||
|
%Schema{type: :array},
|
||||||
|
%Schema{type: :object},
|
||||||
|
%Schema{type: :string},
|
||||||
|
%Schema{type: :integer},
|
||||||
|
%Schema{type: :boolean}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp config_response do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
configs: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
group: %Schema{type: :string},
|
||||||
|
key: %Schema{type: :string},
|
||||||
|
value: any()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
need_reboot: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description:
|
||||||
|
"If `need_reboot` is `true`, instance must be restarted, so reboot time settings can take effect"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
148
lib/pleroma/web/api_spec/operations/admin/invite_operation.ex
Normal file
148
lib/pleroma/web/api_spec/operations/admin/invite_operation.ex
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.Admin.InviteOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Invites"],
|
||||||
|
summary: "Get a list of generated invites",
|
||||||
|
operationId: "AdminAPI.InviteController.index",
|
||||||
|
security: [%{"oAuth" => ["read:invites"]}],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Invites", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
invites: %Schema{type: :array, items: invite()}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"invites" => [
|
||||||
|
%{
|
||||||
|
"id" => 123,
|
||||||
|
"token" => "kSQtDj_GNy2NZsL9AQDFIsHN5qdbguB6qRg3WHw6K1U=",
|
||||||
|
"used" => true,
|
||||||
|
"expires_at" => nil,
|
||||||
|
"uses" => 0,
|
||||||
|
"max_use" => nil,
|
||||||
|
"invite_type" => "one_time"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Invites"],
|
||||||
|
summary: "Create an account registration invite token",
|
||||||
|
operationId: "AdminAPI.InviteController.create",
|
||||||
|
security: [%{"oAuth" => ["write:invites"]}],
|
||||||
|
requestBody:
|
||||||
|
request_body("Parameters", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
max_use: %Schema{type: :integer},
|
||||||
|
expires_at: %Schema{type: :string, format: :date, example: "2020-04-20"}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Invite", "application/json", invite())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def revoke_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Invites"],
|
||||||
|
summary: "Revoke invite by token",
|
||||||
|
operationId: "AdminAPI.InviteController.revoke",
|
||||||
|
security: [%{"oAuth" => ["write:invites"]}],
|
||||||
|
requestBody:
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:token],
|
||||||
|
properties: %{
|
||||||
|
token: %Schema{type: :string}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Invite", "application/json", invite()),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def email_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Invites"],
|
||||||
|
summary: "Sends registration invite via email",
|
||||||
|
operationId: "AdminAPI.InviteController.email",
|
||||||
|
security: [%{"oAuth" => ["write:invites"]}],
|
||||||
|
requestBody:
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:email],
|
||||||
|
properties: %{
|
||||||
|
email: %Schema{type: :string, format: :email},
|
||||||
|
name: %Schema{type: :string}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
),
|
||||||
|
responses: %{
|
||||||
|
204 => no_content_response(),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp invite do
|
||||||
|
%Schema{
|
||||||
|
title: "Invite",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :integer},
|
||||||
|
token: %Schema{type: :string},
|
||||||
|
used: %Schema{type: :boolean},
|
||||||
|
expires_at: %Schema{type: :string, format: :date, nullable: true},
|
||||||
|
uses: %Schema{type: :integer},
|
||||||
|
max_use: %Schema{type: :integer, nullable: true},
|
||||||
|
invite_type: %Schema{
|
||||||
|
type: :string,
|
||||||
|
enum: ["one_time", "reusable", "date_limited", "reusable_date_limited"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"id" => 123,
|
||||||
|
"token" => "kSQtDj_GNy2NZsL9AQDFIsHN5qdbguB6qRg3WHw6K1U=",
|
||||||
|
"used" => true,
|
||||||
|
"expires_at" => nil,
|
||||||
|
"uses" => 0,
|
||||||
|
"max_use" => nil,
|
||||||
|
"invite_type" => "one_time"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
215
lib/pleroma/web/api_spec/operations/admin/oauth_app_operation.ex
Normal file
215
lib/pleroma/web/api_spec/operations/admin/oauth_app_operation.ex
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
summary: "List OAuth apps",
|
||||||
|
tags: ["Admin", "oAuth Apps"],
|
||||||
|
operationId: "AdminAPI.OAuthAppController.index",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:name, :query, %Schema{type: :string}, "App name"),
|
||||||
|
Operation.parameter(:client_id, :query, %Schema{type: :string}, "Client ID"),
|
||||||
|
Operation.parameter(:page, :query, %Schema{type: :integer, default: 1}, "Page"),
|
||||||
|
Operation.parameter(
|
||||||
|
:trusted,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :boolean, default: false},
|
||||||
|
"Trusted apps"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:page_size,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 50},
|
||||||
|
"Number of apps to return"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("List of apps", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
apps: %Schema{type: :array, items: oauth_app()},
|
||||||
|
count: %Schema{type: :integer},
|
||||||
|
page_size: %Schema{type: :integer}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"apps" => [
|
||||||
|
%{
|
||||||
|
"id" => 1,
|
||||||
|
"name" => "App name",
|
||||||
|
"client_id" => "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
|
||||||
|
"client_secret" => "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
|
||||||
|
"redirect_uri" => "https://example.com/oauth-callback",
|
||||||
|
"website" => "https://example.com",
|
||||||
|
"trusted" => true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"count" => 1,
|
||||||
|
"page_size" => 50
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "oAuth Apps"],
|
||||||
|
summary: "Create OAuth App",
|
||||||
|
operationId: "AdminAPI.OAuthAppController.create",
|
||||||
|
requestBody: request_body("Parameters", create_request()),
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("App", "application/json", oauth_app()),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "oAuth Apps"],
|
||||||
|
summary: "Update OAuth App",
|
||||||
|
operationId: "AdminAPI.OAuthAppController.update",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
requestBody: request_body("Parameters", update_request()),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("App", "application/json", oauth_app()),
|
||||||
|
400 =>
|
||||||
|
Operation.response("Bad Request", "application/json", %Schema{
|
||||||
|
oneOf: [ApiError, %Schema{type: :string}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "oAuth Apps"],
|
||||||
|
summary: "Delete OAuth App",
|
||||||
|
operationId: "AdminAPI.OAuthAppController.delete",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
responses: %{
|
||||||
|
204 => no_content_response(),
|
||||||
|
400 => no_content_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp create_request do
|
||||||
|
%Schema{
|
||||||
|
title: "oAuthAppCreateRequest",
|
||||||
|
type: :object,
|
||||||
|
required: [:name, :redirect_uris],
|
||||||
|
properties: %{
|
||||||
|
name: %Schema{type: :string, description: "Application Name"},
|
||||||
|
scopes: %Schema{type: :array, items: %Schema{type: :string}, description: "oAuth scopes"},
|
||||||
|
redirect_uris: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description:
|
||||||
|
"Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter."
|
||||||
|
},
|
||||||
|
website: %Schema{
|
||||||
|
type: :string,
|
||||||
|
nullable: true,
|
||||||
|
description: "A URL to the homepage of the app"
|
||||||
|
},
|
||||||
|
trusted: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
nullable: true,
|
||||||
|
default: false,
|
||||||
|
description: "Is the app trusted?"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"name" => "My App",
|
||||||
|
"redirect_uris" => "https://myapp.com/auth/callback",
|
||||||
|
"website" => "https://myapp.com/",
|
||||||
|
"scopes" => ["read", "write"],
|
||||||
|
"trusted" => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_request do
|
||||||
|
%Schema{
|
||||||
|
title: "oAuthAppUpdateRequest",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
name: %Schema{type: :string, description: "Application Name"},
|
||||||
|
scopes: %Schema{type: :array, items: %Schema{type: :string}, description: "oAuth scopes"},
|
||||||
|
redirect_uris: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description:
|
||||||
|
"Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter."
|
||||||
|
},
|
||||||
|
website: %Schema{
|
||||||
|
type: :string,
|
||||||
|
nullable: true,
|
||||||
|
description: "A URL to the homepage of the app"
|
||||||
|
},
|
||||||
|
trusted: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
nullable: true,
|
||||||
|
default: false,
|
||||||
|
description: "Is the app trusted?"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"name" => "My App",
|
||||||
|
"redirect_uris" => "https://myapp.com/auth/callback",
|
||||||
|
"website" => "https://myapp.com/",
|
||||||
|
"scopes" => ["read", "write"],
|
||||||
|
"trusted" => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp oauth_app do
|
||||||
|
%Schema{
|
||||||
|
title: "oAuthApp",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :integer},
|
||||||
|
name: %Schema{type: :string},
|
||||||
|
client_id: %Schema{type: :string},
|
||||||
|
client_secret: %Schema{type: :string},
|
||||||
|
redirect_uri: %Schema{type: :string},
|
||||||
|
website: %Schema{type: :string, nullable: true},
|
||||||
|
trusted: %Schema{type: :boolean}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"id" => 123,
|
||||||
|
"name" => "My App",
|
||||||
|
"client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
|
||||||
|
"client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
|
||||||
|
"redirect_uri" => "https://myapp.com/oauth-callback",
|
||||||
|
"website" => "https://myapp.com/",
|
||||||
|
"trusted" => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def id_param do
|
||||||
|
Operation.parameter(:id, :path, :integer, "App ID",
|
||||||
|
example: 1337,
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
83
lib/pleroma/web/api_spec/operations/admin/relay_operation.ex
Normal file
83
lib/pleroma/web/api_spec/operations/admin/relay_operation.ex
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.Admin.RelayOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Relays"],
|
||||||
|
summary: "List Relays",
|
||||||
|
operationId: "AdminAPI.RelayController.index",
|
||||||
|
security: [%{"oAuth" => ["read"]}],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Response", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
relays: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{type: :string},
|
||||||
|
example: ["lain.com", "mstdn.io"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def follow_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Relays"],
|
||||||
|
summary: "Follow a Relay",
|
||||||
|
operationId: "AdminAPI.RelayController.follow",
|
||||||
|
security: [%{"oAuth" => ["write:follows"]}],
|
||||||
|
requestBody:
|
||||||
|
request_body("Parameters", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
relay_url: %Schema{type: :string, format: :uri}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Status", "application/json", %Schema{
|
||||||
|
type: :string,
|
||||||
|
example: "http://mastodon.example.org/users/admin"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def unfollow_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Relays"],
|
||||||
|
summary: "Unfollow a Relay",
|
||||||
|
operationId: "AdminAPI.RelayController.unfollow",
|
||||||
|
security: [%{"oAuth" => ["write:follows"]}],
|
||||||
|
requestBody:
|
||||||
|
request_body("Parameters", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
relay_url: %Schema{type: :string, format: :uri}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Status", "application/json", %Schema{
|
||||||
|
type: :string,
|
||||||
|
example: "http://mastodon.example.org/users/admin"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
237
lib/pleroma/web/api_spec/operations/admin/report_operation.ex
Normal file
237
lib/pleroma/web/api_spec/operations/admin/report_operation.ex
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Reports"],
|
||||||
|
summary: "Get a list of reports",
|
||||||
|
operationId: "AdminAPI.ReportController.index",
|
||||||
|
security: [%{"oAuth" => ["read:reports"]}],
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(
|
||||||
|
:state,
|
||||||
|
:query,
|
||||||
|
report_state(),
|
||||||
|
"Filter by report state"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:limit,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer},
|
||||||
|
"The number of records to retrieve"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:page,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 1},
|
||||||
|
"Page number"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:page_size,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 50},
|
||||||
|
"Number number of log entries per page"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Response", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
total: %Schema{type: :integer},
|
||||||
|
reports: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: report()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Reports"],
|
||||||
|
summary: "Get an individual report",
|
||||||
|
operationId: "AdminAPI.ReportController.show",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["read:reports"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Report", "application/json", report()),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Reports"],
|
||||||
|
summary: "Change the state of one or multiple reports",
|
||||||
|
operationId: "AdminAPI.ReportController.update",
|
||||||
|
security: [%{"oAuth" => ["write:reports"]}],
|
||||||
|
requestBody: request_body("Parameters", update_request(), required: true),
|
||||||
|
responses: %{
|
||||||
|
204 => no_content_response(),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", update_400_response()),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def notes_create_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Reports"],
|
||||||
|
summary: "Create report note",
|
||||||
|
operationId: "AdminAPI.ReportController.notes_create",
|
||||||
|
parameters: [id_param()],
|
||||||
|
requestBody:
|
||||||
|
request_body("Parameters", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
content: %Schema{type: :string, description: "The message"}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
security: [%{"oAuth" => ["write:reports"]}],
|
||||||
|
responses: %{
|
||||||
|
204 => no_content_response(),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def notes_delete_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Reports"],
|
||||||
|
summary: "Delete report note",
|
||||||
|
operationId: "AdminAPI.ReportController.notes_delete",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:report_id, :path, :string, "Report ID"),
|
||||||
|
Operation.parameter(:id, :path, :string, "Note ID")
|
||||||
|
],
|
||||||
|
security: [%{"oAuth" => ["write:reports"]}],
|
||||||
|
responses: %{
|
||||||
|
204 => no_content_response(),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp report_state do
|
||||||
|
%Schema{type: :string, enum: ["open", "closed", "resolved"]}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp id_param do
|
||||||
|
Operation.parameter(:id, :path, FlakeID, "Report ID",
|
||||||
|
example: "9umDrYheeY451cQnEe",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp report do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: FlakeID,
|
||||||
|
state: report_state(),
|
||||||
|
account: account_admin(),
|
||||||
|
actor: account_admin(),
|
||||||
|
content: %Schema{type: :string},
|
||||||
|
created_at: %Schema{type: :string, format: :"date-time"},
|
||||||
|
statuses: %Schema{type: :array, items: Status},
|
||||||
|
notes: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :integer},
|
||||||
|
user_id: FlakeID,
|
||||||
|
content: %Schema{type: :string},
|
||||||
|
inserted_at: %Schema{type: :string, format: :"date-time"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp account_admin do
|
||||||
|
%Schema{
|
||||||
|
title: "Account",
|
||||||
|
description: "Account view for admins",
|
||||||
|
type: :object,
|
||||||
|
properties:
|
||||||
|
Map.merge(Account.schema().properties, %{
|
||||||
|
nickname: %Schema{type: :string},
|
||||||
|
deactivated: %Schema{type: :boolean},
|
||||||
|
local: %Schema{type: :boolean},
|
||||||
|
roles: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
admin: %Schema{type: :boolean},
|
||||||
|
moderator: %Schema{type: :boolean}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmation_pending: %Schema{type: :boolean}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_request do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:reports],
|
||||||
|
properties: %{
|
||||||
|
reports: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{allOf: [FlakeID], description: "Required, report ID"},
|
||||||
|
state: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description:
|
||||||
|
"Required, the new state. Valid values are `open`, `closed` and `resolved`"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"reports" => [
|
||||||
|
%{"id" => "123", "state" => "closed"},
|
||||||
|
%{"id" => "1337", "state" => "resolved"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_400_response do
|
||||||
|
%Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{allOf: [FlakeID], description: "Report ID"},
|
||||||
|
error: %Schema{type: :string, description: "Error message"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -74,7 +74,7 @@ def show_operation do
|
||||||
parameters: [id_param()],
|
parameters: [id_param()],
|
||||||
security: [%{"oAuth" => ["read:statuses"]}],
|
security: [%{"oAuth" => ["read:statuses"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Status", "application/json", Status),
|
200 => Operation.response("Status", "application/json", status()),
|
||||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ defp status do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp admin_account do
|
def admin_account do
|
||||||
%Schema{
|
%Schema{
|
||||||
type: :object,
|
type: :object,
|
||||||
properties: %{
|
properties: %{
|
||||||
|
|
|
@ -137,7 +137,7 @@ defp instance do
|
||||||
"background_upload_limit" => 4_000_000,
|
"background_upload_limit" => 4_000_000,
|
||||||
"background_image" => "/static/image.png",
|
"background_image" => "/static/image.png",
|
||||||
"banner_upload_limit" => 4_000_000,
|
"banner_upload_limit" => 4_000_000,
|
||||||
"description" => "A Pleroma instance, an alternative fediverse server",
|
"description" => "Pleroma: An efficient and flexible fediverse server",
|
||||||
"email" => "lain@lain.com",
|
"email" => "lain@lain.com",
|
||||||
"languages" => ["en"],
|
"languages" => ["en"],
|
||||||
"max_toot_chars" => 5000,
|
"max_toot_chars" => 5000,
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
defmodule Pleroma.Web.ControllerHelper do
|
defmodule Pleroma.Web.ControllerHelper do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Pagination
|
||||||
|
|
||||||
# As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
|
# As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
|
||||||
@falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]
|
@falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]
|
||||||
|
|
||||||
|
@ -46,33 +48,8 @@ def add_link_headers(%{assigns: %{skip_link_headers: true}} = conn, _activities,
|
||||||
do: conn
|
do: conn
|
||||||
|
|
||||||
def add_link_headers(conn, activities, extra_params) do
|
def add_link_headers(conn, activities, extra_params) do
|
||||||
case List.last(activities) do
|
case get_pagination_fields(conn, activities, extra_params) do
|
||||||
%{id: max_id} ->
|
%{"next" => next_url, "prev" => prev_url} ->
|
||||||
params =
|
|
||||||
conn.params
|
|
||||||
|> Map.drop(Map.keys(conn.path_params))
|
|
||||||
|> Map.drop(["since_id", "max_id", "min_id"])
|
|
||||||
|> Map.merge(extra_params)
|
|
||||||
|
|
||||||
limit =
|
|
||||||
params
|
|
||||||
|> Map.get("limit", "20")
|
|
||||||
|> String.to_integer()
|
|
||||||
|
|
||||||
min_id =
|
|
||||||
if length(activities) <= limit do
|
|
||||||
activities
|
|
||||||
|> List.first()
|
|
||||||
|> Map.get(:id)
|
|
||||||
else
|
|
||||||
activities
|
|
||||||
|> Enum.at(limit * -1)
|
|
||||||
|> Map.get(:id)
|
|
||||||
end
|
|
||||||
|
|
||||||
next_url = current_url(conn, Map.merge(params, %{max_id: max_id}))
|
|
||||||
prev_url = current_url(conn, Map.merge(params, %{min_id: min_id}))
|
|
||||||
|
|
||||||
put_resp_header(conn, "link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"")
|
put_resp_header(conn, "link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"")
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -80,9 +57,43 @@ def add_link_headers(conn, activities, extra_params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_pagination_fields(conn, activities, extra_params \\ %{}) do
|
||||||
|
case List.last(activities) do
|
||||||
|
%{id: max_id} ->
|
||||||
|
params =
|
||||||
|
conn.params
|
||||||
|
|> Map.drop(Map.keys(conn.path_params))
|
||||||
|
|> Map.merge(extra_params)
|
||||||
|
|> Map.drop(Pagination.page_keys() -- ["limit", "order"])
|
||||||
|
|
||||||
|
min_id =
|
||||||
|
activities
|
||||||
|
|> List.first()
|
||||||
|
|> Map.get(:id)
|
||||||
|
|
||||||
|
fields = %{
|
||||||
|
"next" => current_url(conn, Map.put(params, :max_id, max_id)),
|
||||||
|
"prev" => current_url(conn, Map.put(params, :min_id, min_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generating an `id` without already present pagination keys would
|
||||||
|
# need a query-restriction with an `q.id >= ^id` or `q.id <= ^id`
|
||||||
|
# instead of the `q.id > ^min_id` and `q.id < ^max_id`.
|
||||||
|
# This is because we only have ids present inside of the page, while
|
||||||
|
# `min_id`, `since_id` and `max_id` requires to know one outside of it.
|
||||||
|
if Map.take(conn.params, Pagination.page_keys() -- ["limit", "order"]) != [] do
|
||||||
|
Map.put(fields, "id", current_url(conn, conn.params))
|
||||||
|
else
|
||||||
|
fields
|
||||||
|
end
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
%{}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def assign_account_by_id(conn, _) do
|
def assign_account_by_id(conn, _) do
|
||||||
# TODO: use `conn.params[:id]` only after moving to OpenAPI
|
case Pleroma.User.get_cached_by_id(conn.params.id) do
|
||||||
case Pleroma.User.get_cached_by_id(conn.params[:id] || conn.params["id"]) do
|
|
||||||
%Pleroma.User{} = account -> assign(conn, :account, account)
|
%Pleroma.User{} = account -> assign(conn, :account, account)
|
||||||
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
||||||
end
|
end
|
||||||
|
@ -99,11 +110,6 @@ def try_render(conn, _, _) do
|
||||||
render_error(conn, :not_implemented, "Can't display this activity")
|
render_error(conn, :not_implemented, "Can't display this activity")
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec put_if_exist(map(), atom() | String.t(), any) :: map()
|
|
||||||
def put_if_exist(map, _key, nil), do: map
|
|
||||||
|
|
||||||
def put_if_exist(map, key, value), do: Map.put(map, key, value)
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns true if request specifies to include embedded relationships in account objects.
|
Returns true if request specifies to include embedded relationships in account objects.
|
||||||
May only be used in selected account-related endpoints; has no effect for status- or
|
May only be used in selected account-related endpoints; has no effect for status- or
|
||||||
|
|
42
lib/pleroma/web/embed_controller.ex
Normal file
42
lib/pleroma/web/embed_controller.ex
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.EmbedController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
|
||||||
|
plug(:put_layout, :embed)
|
||||||
|
|
||||||
|
def show(conn, %{"id" => id}) do
|
||||||
|
with %Activity{local: true} = activity <-
|
||||||
|
Activity.get_by_id_with_object(id),
|
||||||
|
true <- Visibility.is_public?(activity.object) do
|
||||||
|
{:ok, author} = User.get_or_fetch(activity.object.data["actor"])
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> delete_resp_header("x-frame-options")
|
||||||
|
|> delete_resp_header("content-security-policy")
|
||||||
|
|> render("show.html",
|
||||||
|
activity: activity,
|
||||||
|
author: User.sanitize_html(author),
|
||||||
|
counts: get_counts(activity)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_counts(%Activity{} = activity) do
|
||||||
|
%Object{data: data} = Object.normalize(activity)
|
||||||
|
|
||||||
|
%{
|
||||||
|
likes: Map.get(data, "like_count", 0),
|
||||||
|
replies: Map.get(data, "repliesCount", 0),
|
||||||
|
announces: Map.get(data, "announcement_count", 0)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,14 +9,12 @@ defmodule Pleroma.Web.Feed.TagController do
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.Feed.FeedView
|
alias Pleroma.Web.Feed.FeedView
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [put_if_exist: 3]
|
|
||||||
|
|
||||||
def feed(conn, %{"tag" => raw_tag} = params) do
|
def feed(conn, %{"tag" => raw_tag} = params) do
|
||||||
{format, tag} = parse_tag(raw_tag)
|
{format, tag} = parse_tag(raw_tag)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
%{"type" => ["Create"], "tag" => tag}
|
%{type: ["Create"], tag: tag}
|
||||||
|> put_if_exist("max_id", params["max_id"])
|
|> Pleroma.Maps.put_if_present(:max_id, params["max_id"])
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -11,8 +11,6 @@ defmodule Pleroma.Web.Feed.UserController do
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPubController
|
alias Pleroma.Web.ActivityPub.ActivityPubController
|
||||||
alias Pleroma.Web.Feed.FeedView
|
alias Pleroma.Web.Feed.FeedView
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [put_if_exist: 3]
|
|
||||||
|
|
||||||
plug(Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect])
|
plug(Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect])
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
@ -52,10 +50,10 @@ def feed(conn, %{"nickname" => nickname} = params) do
|
||||||
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
|
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
|
||||||
activities =
|
activities =
|
||||||
%{
|
%{
|
||||||
"type" => ["Create"],
|
type: ["Create"],
|
||||||
"actor_id" => user.ap_id
|
actor_id: user.ap_id
|
||||||
}
|
}
|
||||||
|> put_if_exist("max_id", params["max_id"])
|
|> Pleroma.Maps.put_if_present(:max_id, params["max_id"])
|
||||||
|> ActivityPub.fetch_public_or_unlisted_activities()
|
|> ActivityPub.fetch_public_or_unlisted_activities()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
json_response: 3
|
json_response: 3
|
||||||
]
|
]
|
||||||
|
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
|
@ -139,9 +140,7 @@ def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "PATCH /api/v1/accounts/update_credentials"
|
@doc "PATCH /api/v1/accounts/update_credentials"
|
||||||
def update_credentials(%{assigns: %{user: original_user}, body_params: params} = conn, _params) do
|
def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _params) do
|
||||||
user = original_user
|
|
||||||
|
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Enum.filter(fn {_, value} -> not is_nil(value) end)
|
|> Enum.filter(fn {_, value} -> not is_nil(value) end)
|
||||||
|
@ -162,41 +161,49 @@ def update_credentials(%{assigns: %{user: original_user}, body_params: params} =
|
||||||
:discoverable
|
:discoverable
|
||||||
]
|
]
|
||||||
|> Enum.reduce(%{}, fn key, acc ->
|
|> Enum.reduce(%{}, fn key, acc ->
|
||||||
add_if_present(acc, params, key, key, &{:ok, truthy_param?(&1)})
|
Maps.put_if_present(acc, key, params[key], &{:ok, truthy_param?(&1)})
|
||||||
end)
|
end)
|
||||||
|> add_if_present(params, :display_name, :name)
|
|> Maps.put_if_present(:name, params[:display_name])
|
||||||
|> add_if_present(params, :note, :bio)
|
|> Maps.put_if_present(:bio, params[:note])
|
||||||
|> add_if_present(params, :avatar, :avatar)
|
|> Maps.put_if_present(:avatar, params[:avatar])
|
||||||
|> add_if_present(params, :header, :banner)
|
|> Maps.put_if_present(:banner, params[:header])
|
||||||
|> add_if_present(params, :pleroma_background_image, :background)
|
|> Maps.put_if_present(:background, params[:pleroma_background_image])
|
||||||
|> add_if_present(
|
|> Maps.put_if_present(
|
||||||
params,
|
|
||||||
:fields_attributes,
|
|
||||||
:raw_fields,
|
:raw_fields,
|
||||||
|
params[:fields_attributes],
|
||||||
&{:ok, normalize_fields_attributes(&1)}
|
&{:ok, normalize_fields_attributes(&1)}
|
||||||
)
|
)
|
||||||
|> add_if_present(params, :pleroma_settings_store, :pleroma_settings_store)
|
|> Maps.put_if_present(:pleroma_settings_store, params[:pleroma_settings_store])
|
||||||
|> add_if_present(params, :default_scope, :default_scope)
|
|> Maps.put_if_present(:default_scope, params[:default_scope])
|
||||||
|> add_if_present(params["source"], "privacy", :default_scope)
|
|> Maps.put_if_present(:default_scope, params["source"]["privacy"])
|
||||||
|> add_if_present(params, :actor_type, :actor_type)
|
|> Maps.put_if_present(:actor_type, params[:actor_type])
|
||||||
|
|
||||||
changeset = User.update_changeset(user, user_params)
|
changeset = User.update_changeset(user, user_params)
|
||||||
|
|
||||||
with {:ok, user} <- User.update_and_set_cache(changeset) do
|
with {:ok, user} <- User.update_and_set_cache(changeset) do
|
||||||
|
user
|
||||||
|
|> build_update_activity_params()
|
||||||
|
|> ActivityPub.update()
|
||||||
|
|
||||||
render(conn, "show.json", user: user, for: user, with_pleroma_settings: true)
|
render(conn, "show.json", user: user, for: user, with_pleroma_settings: true)
|
||||||
else
|
else
|
||||||
_e -> render_error(conn, :forbidden, "Invalid request")
|
_e -> render_error(conn, :forbidden, "Invalid request")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp add_if_present(map, params, params_field, map_field, value_function \\ &{:ok, &1}) do
|
# Hotfix, handling will be redone with the pipeline
|
||||||
with true <- is_map(params),
|
defp build_update_activity_params(user) do
|
||||||
true <- Map.has_key?(params, params_field),
|
object =
|
||||||
{:ok, new_value} <- value_function.(Map.get(params, params_field)) do
|
Pleroma.Web.ActivityPub.UserView.render("user.json", user: user)
|
||||||
Map.put(map, map_field, new_value)
|
|> Map.delete("@context")
|
||||||
else
|
|
||||||
_ -> map
|
%{
|
||||||
end
|
local: true,
|
||||||
|
to: [user.follower_address],
|
||||||
|
cc: [],
|
||||||
|
object: object,
|
||||||
|
actor: user.ap_id
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp normalize_fields_attributes(fields) do
|
defp normalize_fields_attributes(fields) do
|
||||||
|
@ -237,9 +244,7 @@ def statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.delete(:tagged)
|
|> Map.delete(:tagged)
|
||||||
|> Enum.filter(&(not is_nil(&1)))
|
|> Map.put(:tag, params[:tagged])
|
||||||
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|
|
||||||
|> Map.put("tag", params[:tagged])
|
|
||||||
|
|
||||||
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
|
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.ConversationController do
|
||||||
|
|
||||||
@doc "GET /api/v1/conversations"
|
@doc "GET /api/v1/conversations"
|
||||||
def index(%{assigns: %{user: user}} = conn, params) do
|
def index(%{assigns: %{user: user}} = conn, params) do
|
||||||
params = stringify_pagination_params(params)
|
|
||||||
participations = Participation.for_user_with_last_activity_id(user, params)
|
participations = Participation.for_user_with_last_activity_id(user, params)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -37,20 +36,4 @@ def mark_as_read(%{assigns: %{user: user}} = conn, %{id: participation_id}) do
|
||||||
render(conn, "participation.json", participation: participation, for: user)
|
render(conn, "participation.json", participation: participation, for: user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp stringify_pagination_params(params) do
|
|
||||||
atom_keys =
|
|
||||||
Pleroma.Pagination.page_keys()
|
|
||||||
|> Enum.map(&String.to_atom(&1))
|
|
||||||
|
|
||||||
str_keys =
|
|
||||||
params
|
|
||||||
|> Map.take(atom_keys)
|
|
||||||
|> Enum.map(fn {key, value} -> {to_string(key), value} end)
|
|
||||||
|> Enum.into(%{})
|
|
||||||
|
|
||||||
params
|
|
||||||
|> Map.delete(atom_keys)
|
|
||||||
|> Map.merge(str_keys)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -113,22 +113,44 @@ defp resource_search(:v2, "hashtags", query, _options) do
|
||||||
query
|
query
|
||||||
|> prepare_tags()
|
|> prepare_tags()
|
||||||
|> Enum.map(fn tag ->
|
|> Enum.map(fn tag ->
|
||||||
tag = String.trim_leading(tag, "#")
|
|
||||||
%{name: tag, url: tags_path <> tag}
|
%{name: tag, url: tags_path <> tag}
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp resource_search(:v1, "hashtags", query, _options) do
|
defp resource_search(:v1, "hashtags", query, _options) do
|
||||||
query
|
prepare_tags(query)
|
||||||
|> prepare_tags()
|
|
||||||
|> Enum.map(fn tag -> String.trim_leading(tag, "#") end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp prepare_tags(query) do
|
defp prepare_tags(query, add_joined_tag \\ true) do
|
||||||
|
tags =
|
||||||
query
|
query
|
||||||
|> String.split()
|
|> String.split(~r/[^#\w]+/u, trim: true)
|
||||||
|> Enum.uniq()
|
|> Enum.uniq_by(&String.downcase/1)
|
||||||
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
|
|
||||||
|
explicit_tags = Enum.filter(tags, fn tag -> String.starts_with?(tag, "#") end)
|
||||||
|
|
||||||
|
tags =
|
||||||
|
if Enum.any?(explicit_tags) do
|
||||||
|
explicit_tags
|
||||||
|
else
|
||||||
|
tags
|
||||||
|
end
|
||||||
|
|
||||||
|
tags = Enum.map(tags, fn tag -> String.trim_leading(tag, "#") end)
|
||||||
|
|
||||||
|
if Enum.empty?(explicit_tags) && add_joined_tag do
|
||||||
|
tags
|
||||||
|
|> Kernel.++([joined_tag(tags)])
|
||||||
|
|> Enum.uniq_by(&String.downcase/1)
|
||||||
|
else
|
||||||
|
tags
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp joined_tag(tags) do
|
||||||
|
tags
|
||||||
|
|> Enum.map(fn tag -> String.capitalize(tag) end)
|
||||||
|
|> Enum.join()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp with_fallback(f, fallback \\ []) do
|
defp with_fallback(f, fallback \\ []) do
|
||||||
|
|
|
@ -359,9 +359,9 @@ def context(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||||
with %Activity{} = activity <- Activity.get_by_id(id) do
|
with %Activity{} = activity <- Activity.get_by_id(id) do
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities_for_context(activity.data["context"], %{
|
ActivityPub.fetch_activities_for_context(activity.data["context"], %{
|
||||||
"blocking_user" => user,
|
blocking_user: user,
|
||||||
"user" => user,
|
user: user,
|
||||||
"exclude_id" => activity.id
|
exclude_id: activity.id
|
||||||
})
|
})
|
||||||
|
|
||||||
render(conn, "context.json", activity: activity, activities: activities, user: user)
|
render(conn, "context.json", activity: activity, activities: activities, user: user)
|
||||||
|
@ -370,11 +370,6 @@ def context(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||||
|
|
||||||
@doc "GET /api/v1/favourites"
|
@doc "GET /api/v1/favourites"
|
||||||
def favourites(%{assigns: %{user: %User{} = user}} = conn, params) do
|
def favourites(%{assigns: %{user: %User{} = user}} = conn, params) do
|
||||||
params =
|
|
||||||
params
|
|
||||||
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|
|
||||||
|> Map.take(Pleroma.Pagination.page_keys())
|
|
||||||
|
|
||||||
activities = ActivityPub.fetch_favourites(user, params)
|
activities = ActivityPub.fetch_favourites(user, params)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -44,17 +44,14 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||||
def home(%{assigns: %{user: user}} = conn, params) do
|
def home(%{assigns: %{user: user}} = conn, params) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:user, user)
|
||||||
|> Map.put("user", user)
|
|
||||||
|
|
||||||
recipients = [user.ap_id | User.following(user)]
|
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
recipients
|
[user.ap_id | User.following(user)]
|
||||||
|> ActivityPub.fetch_activities(params)
|
|> ActivityPub.fetch_activities(params)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
|
@ -71,10 +68,9 @@ def home(%{assigns: %{user: user}} = conn, params) do
|
||||||
def direct(%{assigns: %{user: user}} = conn, params) do
|
def direct(%{assigns: %{user: user}} = conn, params) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|
|> Map.put(:type, "Create")
|
||||||
|> Map.put("type", "Create")
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:user, user)
|
||||||
|> Map.put("user", user)
|
|
||||||
|> Map.put(:visibility, "direct")
|
|> Map.put(:visibility, "direct")
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
|
@ -93,9 +89,7 @@ def direct(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
|
||||||
# GET /api/v1/timelines/public
|
# GET /api/v1/timelines/public
|
||||||
def public(%{assigns: %{user: user}} = conn, params) do
|
def public(%{assigns: %{user: user}} = conn, params) do
|
||||||
params = Map.new(params, fn {key, value} -> {to_string(key), value} end)
|
local_only = params[:local]
|
||||||
|
|
||||||
local_only = params["local"]
|
|
||||||
|
|
||||||
cfg_key =
|
cfg_key =
|
||||||
if local_only do
|
if local_only do
|
||||||
|
@ -111,11 +105,11 @@ def public(%{assigns: %{user: user}} = conn, params) do
|
||||||
else
|
else
|
||||||
activities =
|
activities =
|
||||||
params
|
params
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create"])
|
||||||
|> Map.put("local_only", local_only)
|
|> Map.put(:local_only, local_only)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -130,39 +124,38 @@ def public(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
|
||||||
defp hashtag_fetching(params, user, local_only) do
|
defp hashtag_fetching(params, user, local_only) do
|
||||||
tags =
|
tags =
|
||||||
[params["tag"], params["any"]]
|
[params[:tag], params[:any]]
|
||||||
|> List.flatten()
|
|> List.flatten()
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|> Enum.filter(& &1)
|
|> Enum.reject(&is_nil/1)
|
||||||
|> Enum.map(&String.downcase(&1))
|
|> Enum.map(&String.downcase/1)
|
||||||
|
|
||||||
tag_all =
|
tag_all =
|
||||||
params
|
params
|
||||||
|> Map.get("all", [])
|
|> Map.get(:all, [])
|
||||||
|> Enum.map(&String.downcase(&1))
|
|> Enum.map(&String.downcase/1)
|
||||||
|
|
||||||
tag_reject =
|
tag_reject =
|
||||||
params
|
params
|
||||||
|> Map.get("none", [])
|
|> Map.get(:none, [])
|
||||||
|> Enum.map(&String.downcase(&1))
|
|> Enum.map(&String.downcase/1)
|
||||||
|
|
||||||
_activities =
|
_activities =
|
||||||
params
|
params
|
||||||
|> Map.put("type", "Create")
|
|> Map.put(:type, "Create")
|
||||||
|> Map.put("local_only", local_only)
|
|> Map.put(:local_only, local_only)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("user", user)
|
|> Map.put(:user, user)
|
||||||
|> Map.put("tag", tags)
|
|> Map.put(:tag, tags)
|
||||||
|> Map.put("tag_all", tag_all)
|
|> Map.put(:tag_all, tag_all)
|
||||||
|> Map.put("tag_reject", tag_reject)
|
|> Map.put(:tag_reject, tag_reject)
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /api/v1/timelines/tag/:tag
|
# GET /api/v1/timelines/tag/:tag
|
||||||
def hashtag(%{assigns: %{user: user}} = conn, params) do
|
def hashtag(%{assigns: %{user: user}} = conn, params) do
|
||||||
params = Map.new(params, fn {key, value} -> {to_string(key), value} end)
|
local_only = params[:local]
|
||||||
local_only = params["local"]
|
|
||||||
activities = hashtag_fetching(params, user, local_only)
|
activities = hashtag_fetching(params, user, local_only)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -45,10 +45,6 @@ def render("short.json", %{app: %App{website: webiste, client_name: name}}) do
|
||||||
defp with_vapid_key(data) do
|
defp with_vapid_key(data) do
|
||||||
vapid_key = Application.get_env(:web_push_encryption, :vapid_details, [])[:public_key]
|
vapid_key = Application.get_env(:web_push_encryption, :vapid_details, [])[:public_key]
|
||||||
|
|
||||||
if vapid_key do
|
Pleroma.Maps.put_if_present(data, "vapid_key", vapid_key)
|
||||||
Map.put(data, "vapid_key", vapid_key)
|
|
||||||
else
|
|
||||||
data
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,8 +24,8 @@ def render("participation.json", %{participation: participation, for: user}) do
|
||||||
last_activity_id =
|
last_activity_id =
|
||||||
with nil <- participation.last_activity_id do
|
with nil <- participation.last_activity_id do
|
||||||
ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{
|
ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{
|
||||||
"user" => user,
|
user: user,
|
||||||
"blocking_user" => user
|
blocking_user: user
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ defp with_media_attachments(data, %{params: %{"media_attachments" => media_attac
|
||||||
defp with_media_attachments(data, _), do: data
|
defp with_media_attachments(data, _), do: data
|
||||||
|
|
||||||
defp status_params(params) do
|
defp status_params(params) do
|
||||||
data = %{
|
%{
|
||||||
text: params["status"],
|
text: params["status"],
|
||||||
sensitive: params["sensitive"],
|
sensitive: params["sensitive"],
|
||||||
spoiler_text: params["spoiler_text"],
|
spoiler_text: params["spoiler_text"],
|
||||||
|
@ -39,10 +39,6 @@ defp status_params(params) do
|
||||||
poll: params["poll"],
|
poll: params["poll"],
|
||||||
in_reply_to_id: params["in_reply_to_id"]
|
in_reply_to_id: params["in_reply_to_id"]
|
||||||
}
|
}
|
||||||
|
|> Pleroma.Maps.put_if_present(:media_ids, params["media_ids"])
|
||||||
case params["media_ids"] do
|
|
||||||
nil -> data
|
|
||||||
media_ids -> Map.put(data, :media_ids, media_ids)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,12 +25,12 @@ defmodule Pleroma.Web.OAuth.App do
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec changeset(App.t(), map()) :: Ecto.Changeset.t()
|
@spec changeset(t(), map()) :: Ecto.Changeset.t()
|
||||||
def changeset(struct, params) do
|
def changeset(struct, params) do
|
||||||
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted])
|
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted])
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec register_changeset(App.t(), map()) :: Ecto.Changeset.t()
|
@spec register_changeset(t(), map()) :: Ecto.Changeset.t()
|
||||||
def register_changeset(struct, params \\ %{}) do
|
def register_changeset(struct, params \\ %{}) do
|
||||||
changeset =
|
changeset =
|
||||||
struct
|
struct
|
||||||
|
@ -52,18 +52,19 @@ def register_changeset(struct, params \\ %{}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec create(map()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
@spec create(map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
def create(params) do
|
def create(params) do
|
||||||
with changeset <- __MODULE__.register_changeset(%__MODULE__{}, params) do
|
%__MODULE__{}
|
||||||
Repo.insert(changeset)
|
|> register_changeset(params)
|
||||||
end
|
|> Repo.insert()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec update(map()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
@spec update(pos_integer(), map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
def update(params) do
|
def update(id, params) do
|
||||||
with %__MODULE__{} = app <- Repo.get(__MODULE__, params["id"]),
|
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
|
||||||
changeset <- changeset(app, params) do
|
app
|
||||||
Repo.update(changeset)
|
|> changeset(params)
|
||||||
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ def update(params) do
|
||||||
Gets app by attrs or create new with attrs.
|
Gets app by attrs or create new with attrs.
|
||||||
And updates the scopes if need.
|
And updates the scopes if need.
|
||||||
"""
|
"""
|
||||||
@spec get_or_make(map(), list(String.t())) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
@spec get_or_make(map(), list(String.t())) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
def get_or_make(attrs, scopes) do
|
def get_or_make(attrs, scopes) do
|
||||||
with %__MODULE__{} = app <- Repo.get_by(__MODULE__, attrs) do
|
with %__MODULE__{} = app <- Repo.get_by(__MODULE__, attrs) do
|
||||||
update_scopes(app, scopes)
|
update_scopes(app, scopes)
|
||||||
|
@ -92,7 +93,7 @@ defp update_scopes(%__MODULE__{} = app, scopes) do
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec search(map()) :: {:ok, [App.t()], non_neg_integer()}
|
@spec search(map()) :: {:ok, [t()], non_neg_integer()}
|
||||||
def search(params) do
|
def search(params) do
|
||||||
query = from(a in __MODULE__)
|
query = from(a in __MODULE__)
|
||||||
|
|
||||||
|
@ -128,7 +129,7 @@ def search(params) do
|
||||||
{:ok, Repo.all(query), count}
|
{:ok, Repo.all(query), count}
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec destroy(pos_integer()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
@spec destroy(pos_integer()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
def destroy(id) do
|
def destroy(id) do
|
||||||
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
|
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
|
||||||
Repo.delete(app)
|
Repo.delete(app)
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Helpers.UriHelper
|
alias Pleroma.Helpers.UriHelper
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.MFA
|
alias Pleroma.MFA
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
alias Pleroma.Registration
|
alias Pleroma.Registration
|
||||||
|
@ -108,7 +109,7 @@ defp handle_existing_authorization(
|
||||||
if redirect_uri in String.split(app.redirect_uris) do
|
if redirect_uri in String.split(app.redirect_uris) do
|
||||||
redirect_uri = redirect_uri(conn, redirect_uri)
|
redirect_uri = redirect_uri(conn, redirect_uri)
|
||||||
url_params = %{access_token: token.token}
|
url_params = %{access_token: token.token}
|
||||||
url_params = UriHelper.append_param_if_present(url_params, :state, params["state"])
|
url_params = Maps.put_if_present(url_params, :state, params["state"])
|
||||||
url = UriHelper.append_uri_params(redirect_uri, url_params)
|
url = UriHelper.append_uri_params(redirect_uri, url_params)
|
||||||
redirect(conn, external: url)
|
redirect(conn, external: url)
|
||||||
else
|
else
|
||||||
|
@ -147,7 +148,7 @@ def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{
|
||||||
if redirect_uri in String.split(app.redirect_uris) do
|
if redirect_uri in String.split(app.redirect_uris) do
|
||||||
redirect_uri = redirect_uri(conn, redirect_uri)
|
redirect_uri = redirect_uri(conn, redirect_uri)
|
||||||
url_params = %{code: auth.token}
|
url_params = %{code: auth.token}
|
||||||
url_params = UriHelper.append_param_if_present(url_params, :state, auth_attrs["state"])
|
url_params = Maps.put_if_present(url_params, :state, auth_attrs["state"])
|
||||||
url = UriHelper.append_uri_params(redirect_uri, url_params)
|
url = UriHelper.append_uri_params(redirect_uri, url_params)
|
||||||
redirect(conn, external: url)
|
redirect(conn, external: url)
|
||||||
else
|
else
|
||||||
|
|
|
@ -126,10 +126,9 @@ def favourites(%{assigns: %{account: %{hide_favorites: true}}} = conn, _params)
|
||||||
def favourites(%{assigns: %{user: for_user, account: user}} = conn, params) do
|
def favourites(%{assigns: %{user: for_user, account: user}} = conn, params) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|
|> Map.put(:type, "Create")
|
||||||
|> Map.put("type", "Create")
|
|> Map.put(:favorited_by, user.ap_id)
|
||||||
|> Map.put("favorited_by", user.ap_id)
|
|> Map.put(:blocking_user, for_user)
|
||||||
|> Map.put("blocking_user", for_user)
|
|
||||||
|
|
||||||
recipients =
|
recipients =
|
||||||
if for_user do
|
if for_user do
|
||||||
|
|
|
@ -42,15 +42,14 @@ def statuses(
|
||||||
Participation.get(participation_id, preload: [:conversation]) do
|
Participation.get(participation_id, preload: [:conversation]) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:user, user)
|
||||||
|> Map.put("user", user)
|
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
participation.conversation.ap_id
|
participation.conversation.ap_id
|
||||||
|> ActivityPub.fetch_activities_for_context_query(params)
|
|> ActivityPub.fetch_activities_for_context_query(params)
|
||||||
|> Pleroma.Pagination.fetch_paginated(Map.put(params, "total", false))
|
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :total, false))
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -36,10 +36,7 @@ def create(%{assigns: %{user: user}, body_params: params} = conn, _) do
|
||||||
|
|
||||||
def index(%{assigns: %{user: reading_user}} = conn, %{id: id} = params) do
|
def index(%{assigns: %{user: reading_user}} = conn, %{id: id} = params) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname_or_id(id, for: reading_user) do
|
with %User{} = user <- User.get_cached_by_nickname_or_id(id, for: reading_user) do
|
||||||
params =
|
params = Map.put(params, :type, ["Listen"])
|
||||||
params
|
|
||||||
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|
|
||||||
|> Map.put("type", ["Listen"])
|
|
||||||
|
|
||||||
activities = ActivityPub.fetch_user_abstract_activities(user, reading_user, params)
|
activities = ActivityPub.fetch_user_abstract_activities(user, reading_user, params)
|
||||||
|
|
||||||
|
|
|
@ -160,14 +160,14 @@ defmodule Pleroma.Web.Router do
|
||||||
:right_delete_multiple
|
:right_delete_multiple
|
||||||
)
|
)
|
||||||
|
|
||||||
get("/relay", AdminAPIController, :relay_list)
|
get("/relay", RelayController, :index)
|
||||||
post("/relay", AdminAPIController, :relay_follow)
|
post("/relay", RelayController, :follow)
|
||||||
delete("/relay", AdminAPIController, :relay_unfollow)
|
delete("/relay", RelayController, :unfollow)
|
||||||
|
|
||||||
post("/users/invite_token", AdminAPIController, :create_invite_token)
|
post("/users/invite_token", InviteController, :create)
|
||||||
get("/users/invites", AdminAPIController, :invites)
|
get("/users/invites", InviteController, :index)
|
||||||
post("/users/revoke_invite", AdminAPIController, :revoke_invite)
|
post("/users/revoke_invite", InviteController, :revoke)
|
||||||
post("/users/email_invite", AdminAPIController, :email_invite)
|
post("/users/email_invite", InviteController, :email)
|
||||||
|
|
||||||
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
|
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
|
||||||
patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
|
patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
|
||||||
|
@ -183,20 +183,20 @@ defmodule Pleroma.Web.Router do
|
||||||
patch("/users/confirm_email", AdminAPIController, :confirm_email)
|
patch("/users/confirm_email", AdminAPIController, :confirm_email)
|
||||||
patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
|
patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
|
||||||
|
|
||||||
get("/reports", AdminAPIController, :list_reports)
|
get("/reports", ReportController, :index)
|
||||||
get("/reports/:id", AdminAPIController, :report_show)
|
get("/reports/:id", ReportController, :show)
|
||||||
patch("/reports", AdminAPIController, :reports_update)
|
patch("/reports", ReportController, :update)
|
||||||
post("/reports/:id/notes", AdminAPIController, :report_notes_create)
|
post("/reports/:id/notes", ReportController, :notes_create)
|
||||||
delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete)
|
delete("/reports/:report_id/notes/:id", ReportController, :notes_delete)
|
||||||
|
|
||||||
get("/statuses/:id", StatusController, :show)
|
get("/statuses/:id", StatusController, :show)
|
||||||
put("/statuses/:id", StatusController, :update)
|
put("/statuses/:id", StatusController, :update)
|
||||||
delete("/statuses/:id", StatusController, :delete)
|
delete("/statuses/:id", StatusController, :delete)
|
||||||
get("/statuses", StatusController, :index)
|
get("/statuses", StatusController, :index)
|
||||||
|
|
||||||
get("/config", AdminAPIController, :config_show)
|
get("/config", ConfigController, :show)
|
||||||
post("/config", AdminAPIController, :config_update)
|
post("/config", ConfigController, :update)
|
||||||
get("/config/descriptions", AdminAPIController, :config_descriptions)
|
get("/config/descriptions", ConfigController, :descriptions)
|
||||||
get("/need_reboot", AdminAPIController, :need_reboot)
|
get("/need_reboot", AdminAPIController, :need_reboot)
|
||||||
get("/restart", AdminAPIController, :restart)
|
get("/restart", AdminAPIController, :restart)
|
||||||
|
|
||||||
|
@ -205,10 +205,10 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
||||||
get("/stats", AdminAPIController, :stats)
|
get("/stats", AdminAPIController, :stats)
|
||||||
|
|
||||||
get("/oauth_app", AdminAPIController, :oauth_app_list)
|
get("/oauth_app", OAuthAppController, :index)
|
||||||
post("/oauth_app", AdminAPIController, :oauth_app_create)
|
post("/oauth_app", OAuthAppController, :create)
|
||||||
patch("/oauth_app/:id", AdminAPIController, :oauth_app_update)
|
patch("/oauth_app/:id", OAuthAppController, :update)
|
||||||
delete("/oauth_app/:id", AdminAPIController, :oauth_app_delete)
|
delete("/oauth_app/:id", OAuthAppController, :delete)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||||
|
@ -571,13 +571,6 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web.ActivityPub do
|
|
||||||
# XXX: not really ostatus
|
|
||||||
pipe_through(:ostatus)
|
|
||||||
|
|
||||||
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
|
||||||
end
|
|
||||||
|
|
||||||
pipeline :ap_service_actor do
|
pipeline :ap_service_actor do
|
||||||
plug(:accepts, ["activity+json", "json"])
|
plug(:accepts, ["activity+json", "json"])
|
||||||
end
|
end
|
||||||
|
@ -602,6 +595,7 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/api/ap/whoami", ActivityPubController, :whoami)
|
get("/api/ap/whoami", ActivityPubController, :whoami)
|
||||||
get("/users/:nickname/inbox", ActivityPubController, :read_inbox)
|
get("/users/:nickname/inbox", ActivityPubController, :read_inbox)
|
||||||
|
|
||||||
|
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
||||||
post("/users/:nickname/outbox", ActivityPubController, :update_outbox)
|
post("/users/:nickname/outbox", ActivityPubController, :update_outbox)
|
||||||
post("/api/ap/upload_media", ActivityPubController, :upload_media)
|
post("/api/ap/upload_media", ActivityPubController, :upload_media)
|
||||||
|
|
||||||
|
@ -664,6 +658,8 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/auth/password", MastodonAPI.AuthController, :password_reset)
|
post("/auth/password", MastodonAPI.AuthController, :password_reset)
|
||||||
|
|
||||||
get("/web/*path", MastoFEController, :index)
|
get("/web/*path", MastoFEController, :index)
|
||||||
|
|
||||||
|
get("/embed/:id", EmbedController, :show)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/proxy/", Pleroma.Web.MediaProxy do
|
scope "/proxy/", Pleroma.Web.MediaProxy do
|
||||||
|
|
|
@ -111,8 +111,14 @@ def show(%{assigns: %{username_or_id: username_or_id}} = conn, params) do
|
||||||
%User{} = user ->
|
%User{} = user ->
|
||||||
meta = Metadata.build_tags(%{user: user})
|
meta = Metadata.build_tags(%{user: user})
|
||||||
|
|
||||||
|
params =
|
||||||
|
params
|
||||||
|
|> Map.take(@page_keys)
|
||||||
|
|> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end)
|
||||||
|
|
||||||
timeline =
|
timeline =
|
||||||
ActivityPub.fetch_user_activities(user, nil, Map.take(params, @page_keys))
|
user
|
||||||
|
|> ActivityPub.fetch_user_activities(nil, params)
|
||||||
|> Enum.map(&represent/1)
|
|> Enum.map(&represent/1)
|
||||||
|
|
||||||
prev_page_id =
|
prev_page_id =
|
||||||
|
|
8
lib/pleroma/web/templates/embed/_attachment.html.eex
Normal file
8
lib/pleroma/web/templates/embed/_attachment.html.eex
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<%= case @mediaType do %>
|
||||||
|
<% "audio" -> %>
|
||||||
|
<audio src="<%= @url %>" controls="controls"></audio>
|
||||||
|
<% "video" -> %>
|
||||||
|
<video src="<%= @url %>" controls="controls"></video>
|
||||||
|
<% _ -> %>
|
||||||
|
<img src="<%= @url %>" alt="<%= @name %>" title="<%= @name %>">
|
||||||
|
<% end %>
|
76
lib/pleroma/web/templates/embed/show.html.eex
Normal file
76
lib/pleroma/web/templates/embed/show.html.eex
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<div>
|
||||||
|
<div class="p-author h-card">
|
||||||
|
<a class="u-url" rel="author noopener" href="<%= @author.ap_id %>">
|
||||||
|
<div class="avatar">
|
||||||
|
<img src="<%= User.avatar_url(@author) |> MediaProxy.url %>" width="48" height="48" alt="">
|
||||||
|
</div>
|
||||||
|
<span class="display-name" style="padding-left: 0.5em;">
|
||||||
|
<bdi><%= raw (@author.name |> Formatter.emojify(@author.emoji)) %></bdi>
|
||||||
|
<span class="nickname"><%= full_nickname(@author) %></span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="activity-content" >
|
||||||
|
<%= if status_title(@activity) != "" do %>
|
||||||
|
<details <%= if open_content?() do %>open<% end %>>
|
||||||
|
<summary><%= raw status_title(@activity) %></summary>
|
||||||
|
<div><%= activity_content(@activity) %></div>
|
||||||
|
</details>
|
||||||
|
<% else %>
|
||||||
|
<div><%= activity_content(@activity) %></div>
|
||||||
|
<% end %>
|
||||||
|
<%= for %{"name" => name, "url" => [url | _]} <- attachments(@activity) do %>
|
||||||
|
<div class="attachment">
|
||||||
|
<%= if sensitive?(@activity) do %>
|
||||||
|
<details class="nsfw">
|
||||||
|
<summary onClick="updateHeight()"><%= Gettext.gettext("sensitive media") %></summary>
|
||||||
|
<div class="nsfw-content">
|
||||||
|
<%= render("_attachment.html", %{name: name, url: url["href"],
|
||||||
|
mediaType: fetch_media_type(url)}) %>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
<% else %>
|
||||||
|
<%= render("_attachment.html", %{name: name, url: url["href"],
|
||||||
|
mediaType: fetch_media_type(url)}) %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<dl class="counts pull-right">
|
||||||
|
<dt><%= Gettext.gettext("replies") %></dt><dd><%= @counts.replies %></dd>
|
||||||
|
<dt><%= Gettext.gettext("announces") %></dt><dd><%= @counts.announces %></dd>
|
||||||
|
<dt><%= Gettext.gettext("likes") %></dt><dd><%= @counts.likes %></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<p class="date pull-left">
|
||||||
|
<%= link published(@activity), to: activity_url(@author, @activity) %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function updateHeight() {
|
||||||
|
window.requestAnimationFrame(function(){
|
||||||
|
var height = document.getElementsByTagName('html')[0].scrollHeight;
|
||||||
|
|
||||||
|
window.parent.postMessage({
|
||||||
|
type: 'setHeightPleromaEmbed',
|
||||||
|
id: window.parentId,
|
||||||
|
height: height,
|
||||||
|
}, '*');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('message', function(e){
|
||||||
|
var data = e.data || {};
|
||||||
|
|
||||||
|
if (!window.parent || data.type !== 'setHeightPleromaEmbed') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.parentId = data.id
|
||||||
|
|
||||||
|
updateHeight()
|
||||||
|
});
|
||||||
|
</script>
|
15
lib/pleroma/web/templates/layout/embed.html.eex
Normal file
15
lib/pleroma/web/templates/layout/embed.html.eex
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui" />
|
||||||
|
<title><%= Pleroma.Config.get([:instance, :name]) %></title>
|
||||||
|
<meta content='noindex' name='robots'>
|
||||||
|
<%= Phoenix.HTML.raw(assigns[:meta] || "") %>
|
||||||
|
<link rel="stylesheet" href="/embed.css">
|
||||||
|
<base target="_parent">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<%= render @view_module, @view_template, assigns %>
|
||||||
|
</body>
|
||||||
|
</html>
|
74
lib/pleroma/web/views/embed_view.ex
Normal file
74
lib/pleroma/web/views/embed_view.ex
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.EmbedView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
alias Calendar.Strftime
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Emoji.Formatter
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.Gettext
|
||||||
|
alias Pleroma.Web.MediaProxy
|
||||||
|
alias Pleroma.Web.Metadata.Utils
|
||||||
|
alias Pleroma.Web.Router.Helpers
|
||||||
|
|
||||||
|
use Phoenix.HTML
|
||||||
|
|
||||||
|
@media_types ["image", "audio", "video"]
|
||||||
|
|
||||||
|
defp fetch_media_type(%{"mediaType" => mediaType}) do
|
||||||
|
Utils.fetch_media_type(@media_types, mediaType)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp open_content? do
|
||||||
|
Pleroma.Config.get(
|
||||||
|
[:frontend_configurations, :collapse_message_with_subjects],
|
||||||
|
true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp full_nickname(user) do
|
||||||
|
%{host: host} = URI.parse(user.ap_id)
|
||||||
|
"@" <> user.nickname <> "@" <> host
|
||||||
|
end
|
||||||
|
|
||||||
|
defp status_title(%Activity{object: %Object{data: %{"name" => name}}}) when is_binary(name),
|
||||||
|
do: name
|
||||||
|
|
||||||
|
defp status_title(%Activity{object: %Object{data: %{"summary" => summary}}})
|
||||||
|
when is_binary(summary),
|
||||||
|
do: summary
|
||||||
|
|
||||||
|
defp status_title(_), do: nil
|
||||||
|
|
||||||
|
defp activity_content(%Activity{object: %Object{data: %{"content" => content}}}) do
|
||||||
|
content |> Pleroma.HTML.filter_tags() |> raw()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp activity_content(_), do: nil
|
||||||
|
|
||||||
|
defp activity_url(%User{local: true}, activity) do
|
||||||
|
Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp activity_url(%User{local: false}, %Activity{object: %Object{data: data}}) do
|
||||||
|
data["url"] || data["external_url"] || data["id"]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp attachments(%Activity{object: %Object{data: %{"attachment" => attachments}}}) do
|
||||||
|
attachments
|
||||||
|
end
|
||||||
|
|
||||||
|
defp sensitive?(%Activity{object: %Object{data: %{"sensitive" => sensitive}}}) do
|
||||||
|
sensitive
|
||||||
|
end
|
||||||
|
|
||||||
|
defp published(%Activity{object: %Object{data: %{"published" => published}}}) do
|
||||||
|
published
|
||||||
|
|> NaiveDateTime.from_iso8601!()
|
||||||
|
|> Strftime.strftime!("%B %d, %Y, %l:%M %p")
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,14 +3,16 @@ msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-05-15 09:37+0000\n"
|
"POT-Creation-Date: 2020-05-15 09:37+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: 2020-06-02 07:36+0000\n"
|
||||||
"Last-Translator: Automatically generated\n"
|
"Last-Translator: Fristi <fristi@subcon.town>\n"
|
||||||
"Language-Team: none\n"
|
"Language-Team: Dutch <https://translate.pleroma.social/projects/pleroma/"
|
||||||
|
"pleroma/nl/>\n"
|
||||||
"Language: nl\n"
|
"Language: nl\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Generator: Translate Toolkit 2.5.1\n"
|
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||||
|
"X-Generator: Weblate 4.0.4\n"
|
||||||
|
|
||||||
## This file is a PO Template file.
|
## This file is a PO Template file.
|
||||||
##
|
##
|
||||||
|
@ -23,142 +25,142 @@ msgstr ""
|
||||||
## effect: edit them in PO (`.po`) files instead.
|
## effect: edit them in PO (`.po`) files instead.
|
||||||
## From Ecto.Changeset.cast/4
|
## From Ecto.Changeset.cast/4
|
||||||
msgid "can't be blank"
|
msgid "can't be blank"
|
||||||
msgstr ""
|
msgstr "kan niet leeg zijn"
|
||||||
|
|
||||||
## From Ecto.Changeset.unique_constraint/3
|
## From Ecto.Changeset.unique_constraint/3
|
||||||
msgid "has already been taken"
|
msgid "has already been taken"
|
||||||
msgstr ""
|
msgstr "is al bezet"
|
||||||
|
|
||||||
## From Ecto.Changeset.put_change/3
|
## From Ecto.Changeset.put_change/3
|
||||||
msgid "is invalid"
|
msgid "is invalid"
|
||||||
msgstr ""
|
msgstr "is ongeldig"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_format/3
|
## From Ecto.Changeset.validate_format/3
|
||||||
msgid "has invalid format"
|
msgid "has invalid format"
|
||||||
msgstr ""
|
msgstr "heeft een ongeldig formaat"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_subset/3
|
## From Ecto.Changeset.validate_subset/3
|
||||||
msgid "has an invalid entry"
|
msgid "has an invalid entry"
|
||||||
msgstr ""
|
msgstr "heeft een ongeldige entry"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_exclusion/3
|
## From Ecto.Changeset.validate_exclusion/3
|
||||||
msgid "is reserved"
|
msgid "is reserved"
|
||||||
msgstr ""
|
msgstr "is gereserveerd"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_confirmation/3
|
## From Ecto.Changeset.validate_confirmation/3
|
||||||
msgid "does not match confirmation"
|
msgid "does not match confirmation"
|
||||||
msgstr ""
|
msgstr "komt niet overeen met bevestiging"
|
||||||
|
|
||||||
## From Ecto.Changeset.no_assoc_constraint/3
|
## From Ecto.Changeset.no_assoc_constraint/3
|
||||||
msgid "is still associated with this entry"
|
msgid "is still associated with this entry"
|
||||||
msgstr ""
|
msgstr "is nog geassocieerd met deze entry"
|
||||||
|
|
||||||
msgid "are still associated with this entry"
|
msgid "are still associated with this entry"
|
||||||
msgstr ""
|
msgstr "zijn nog geassocieerd met deze entry"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_length/3
|
## From Ecto.Changeset.validate_length/3
|
||||||
msgid "should be %{count} character(s)"
|
msgid "should be %{count} character(s)"
|
||||||
msgid_plural "should be %{count} character(s)"
|
msgid_plural "should be %{count} character(s)"
|
||||||
msgstr[0] ""
|
msgstr[0] "dient %{count} karakter te bevatten"
|
||||||
msgstr[1] ""
|
msgstr[1] "dient %{count} karakters te bevatten"
|
||||||
|
|
||||||
msgid "should have %{count} item(s)"
|
msgid "should have %{count} item(s)"
|
||||||
msgid_plural "should have %{count} item(s)"
|
msgid_plural "should have %{count} item(s)"
|
||||||
msgstr[0] ""
|
msgstr[0] "dient %{count} item te bevatten"
|
||||||
msgstr[1] ""
|
msgstr[1] "dient %{count} items te bevatten"
|
||||||
|
|
||||||
msgid "should be at least %{count} character(s)"
|
msgid "should be at least %{count} character(s)"
|
||||||
msgid_plural "should be at least %{count} character(s)"
|
msgid_plural "should be at least %{count} character(s)"
|
||||||
msgstr[0] ""
|
msgstr[0] "dient ten minste %{count} karakter te bevatten"
|
||||||
msgstr[1] ""
|
msgstr[1] "dient ten minste %{count} karakters te bevatten"
|
||||||
|
|
||||||
msgid "should have at least %{count} item(s)"
|
msgid "should have at least %{count} item(s)"
|
||||||
msgid_plural "should have at least %{count} item(s)"
|
msgid_plural "should have at least %{count} item(s)"
|
||||||
msgstr[0] ""
|
msgstr[0] "dient ten minste %{count} item te bevatten"
|
||||||
msgstr[1] ""
|
msgstr[1] "dient ten minste %{count} items te bevatten"
|
||||||
|
|
||||||
msgid "should be at most %{count} character(s)"
|
msgid "should be at most %{count} character(s)"
|
||||||
msgid_plural "should be at most %{count} character(s)"
|
msgid_plural "should be at most %{count} character(s)"
|
||||||
msgstr[0] ""
|
msgstr[0] "dient niet meer dan %{count} karakter te bevatten"
|
||||||
msgstr[1] ""
|
msgstr[1] "dient niet meer dan %{count} karakters te bevatten"
|
||||||
|
|
||||||
msgid "should have at most %{count} item(s)"
|
msgid "should have at most %{count} item(s)"
|
||||||
msgid_plural "should have at most %{count} item(s)"
|
msgid_plural "should have at most %{count} item(s)"
|
||||||
msgstr[0] ""
|
msgstr[0] "dient niet meer dan %{count} item te bevatten"
|
||||||
msgstr[1] ""
|
msgstr[1] "dient niet meer dan %{count} items te bevatten"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_number/3
|
## From Ecto.Changeset.validate_number/3
|
||||||
msgid "must be less than %{number}"
|
msgid "must be less than %{number}"
|
||||||
msgstr ""
|
msgstr "dient kleiner te zijn dan %{number}"
|
||||||
|
|
||||||
msgid "must be greater than %{number}"
|
msgid "must be greater than %{number}"
|
||||||
msgstr ""
|
msgstr "dient groter te zijn dan %{number}"
|
||||||
|
|
||||||
msgid "must be less than or equal to %{number}"
|
msgid "must be less than or equal to %{number}"
|
||||||
msgstr ""
|
msgstr "dient kleiner dan of gelijk te zijn aan %{number}"
|
||||||
|
|
||||||
msgid "must be greater than or equal to %{number}"
|
msgid "must be greater than or equal to %{number}"
|
||||||
msgstr ""
|
msgstr "dient groter dan of gelijk te zijn aan %{number}"
|
||||||
|
|
||||||
msgid "must be equal to %{number}"
|
msgid "must be equal to %{number}"
|
||||||
msgstr ""
|
msgstr "dient gelijk te zijn aan %{number}"
|
||||||
|
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:421
|
#: lib/pleroma/web/common_api/common_api.ex:421
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Account not found"
|
msgid "Account not found"
|
||||||
msgstr ""
|
msgstr "Account niet gevonden"
|
||||||
|
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:249
|
#: lib/pleroma/web/common_api/common_api.ex:249
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Already voted"
|
msgid "Already voted"
|
||||||
msgstr ""
|
msgstr "Al gestemd"
|
||||||
|
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:360
|
#: lib/pleroma/web/oauth/oauth_controller.ex:360
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Bad request"
|
msgid "Bad request"
|
||||||
msgstr ""
|
msgstr "Bad request"
|
||||||
|
|
||||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:425
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:425
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Can't delete object"
|
msgid "Can't delete object"
|
||||||
msgstr ""
|
msgstr "Object kan niet verwijderd worden"
|
||||||
|
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:196
|
#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:196
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Can't delete this post"
|
msgid "Can't delete this post"
|
||||||
msgstr ""
|
msgstr "Bericht kan niet verwijderd worden"
|
||||||
|
|
||||||
#: lib/pleroma/web/controller_helper.ex:95
|
#: lib/pleroma/web/controller_helper.ex:95
|
||||||
#: lib/pleroma/web/controller_helper.ex:101
|
#: lib/pleroma/web/controller_helper.ex:101
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Can't display this activity"
|
msgid "Can't display this activity"
|
||||||
msgstr ""
|
msgstr "Activiteit kan niet worden getoond"
|
||||||
|
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:227
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:227
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:254
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:254
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Can't find user"
|
msgid "Can't find user"
|
||||||
msgstr ""
|
msgstr "Gebruiker kan niet gevonden worden"
|
||||||
|
|
||||||
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:114
|
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:114
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Can't get favorites"
|
msgid "Can't get favorites"
|
||||||
msgstr ""
|
msgstr "Favorieten konden niet opgehaald worden"
|
||||||
|
|
||||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:437
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:437
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Can't like object"
|
msgid "Can't like object"
|
||||||
msgstr ""
|
msgstr "Object kan niet geliked worden"
|
||||||
|
|
||||||
#: lib/pleroma/web/common_api/utils.ex:556
|
#: lib/pleroma/web/common_api/utils.ex:556
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Cannot post an empty status without attachments"
|
msgid "Cannot post an empty status without attachments"
|
||||||
msgstr ""
|
msgstr "Status kan niet geplaatst worden zonder tekst of bijlagen"
|
||||||
|
|
||||||
#: lib/pleroma/web/common_api/utils.ex:504
|
#: lib/pleroma/web/common_api/utils.ex:504
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "Comment must be up to %{max_size} characters"
|
msgid "Comment must be up to %{max_size} characters"
|
||||||
msgstr ""
|
msgstr "Opmerking dient maximaal %{max_size} karakters te bevatten"
|
||||||
|
|
||||||
#: lib/pleroma/config/config_db.ex:222
|
#: lib/pleroma/config/config_db.ex:222
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddRecipientsContainBlockedDomainsFunction do
|
||||||
|
use Ecto.Migration
|
||||||
|
@disable_ddl_transaction true
|
||||||
|
|
||||||
|
def up do
|
||||||
|
statement = """
|
||||||
|
CREATE OR REPLACE FUNCTION recipients_contain_blocked_domains(recipients varchar[], blocked_domains varchar[]) RETURNS boolean AS $$
|
||||||
|
DECLARE
|
||||||
|
recipient_domain varchar;
|
||||||
|
recipient varchar;
|
||||||
|
BEGIN
|
||||||
|
FOREACH recipient IN ARRAY recipients LOOP
|
||||||
|
recipient_domain = split_part(recipient, '/', 3)::varchar;
|
||||||
|
|
||||||
|
IF recipient_domain = ANY(blocked_domains) THEN
|
||||||
|
RETURN TRUE;
|
||||||
|
END IF;
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
RETURN FALSE;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
"""
|
||||||
|
|
||||||
|
execute(statement)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
execute(
|
||||||
|
"drop function if exists recipients_contain_blocked_domains(recipients varchar[], blocked_domains varchar[])"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
115
priv/static/embed.css
Normal file
115
priv/static/embed.css
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
body {
|
||||||
|
background-color: #282c37;
|
||||||
|
font-family: sans-serif;
|
||||||
|
color: white;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1em;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar img {
|
||||||
|
float: left;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-content {
|
||||||
|
padding-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date a,
|
||||||
|
.counts {
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.counts dt,
|
||||||
|
.counts dd {
|
||||||
|
float: left;
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-card {
|
||||||
|
min-height: 48px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-card a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-card a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-name {
|
||||||
|
padding-top: 4px;
|
||||||
|
display: block;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* keep emoji from being hilariously huge */
|
||||||
|
.display-name img {
|
||||||
|
max-height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-name .nickname {
|
||||||
|
padding-top: 4px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nickname:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pull-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse {
|
||||||
|
margin: 0;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.button {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
color: white;
|
||||||
|
background-color: #419bdd;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
padding: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.button:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: #61a6d9;
|
||||||
|
}
|
43
priv/static/embed.js
Normal file
43
priv/static/embed.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
(function () {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
var ready = function (loaded) {
|
||||||
|
if (['interactive', 'complete'].indexOf(document.readyState) !== -1) {
|
||||||
|
loaded()
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', loaded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ready(function () {
|
||||||
|
var iframes = []
|
||||||
|
|
||||||
|
window.addEventListener('message', function (e) {
|
||||||
|
var data = e.data || {}
|
||||||
|
|
||||||
|
if (data.type !== 'setHeightPleromaEmbed' || !iframes[data.id]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
iframes[data.id].height = data.height
|
||||||
|
});
|
||||||
|
|
||||||
|
[].forEach.call(document.querySelectorAll('iframe.pleroma-embed'), function (iframe) {
|
||||||
|
iframe.scrolling = 'no'
|
||||||
|
iframe.style.overflow = 'hidden'
|
||||||
|
|
||||||
|
iframes.push(iframe)
|
||||||
|
|
||||||
|
var id = iframes.length - 1
|
||||||
|
|
||||||
|
iframe.onload = function () {
|
||||||
|
iframe.contentWindow.postMessage({
|
||||||
|
type: 'setHeightPleromaEmbed',
|
||||||
|
id: id
|
||||||
|
}, '*')
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe.onload()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})()
|
|
@ -21,7 +21,7 @@ test "paginates by min_id", %{notes: notes} do
|
||||||
id = Enum.at(notes, 2).id |> Integer.to_string()
|
id = Enum.at(notes, 2).id |> Integer.to_string()
|
||||||
|
|
||||||
%{total: total, items: paginated} =
|
%{total: total, items: paginated} =
|
||||||
Pagination.fetch_paginated(Object, %{"min_id" => id, "total" => true})
|
Pagination.fetch_paginated(Object, %{min_id: id, total: true})
|
||||||
|
|
||||||
assert length(paginated) == 2
|
assert length(paginated) == 2
|
||||||
assert total == 5
|
assert total == 5
|
||||||
|
@ -31,7 +31,7 @@ test "paginates by since_id", %{notes: notes} do
|
||||||
id = Enum.at(notes, 2).id |> Integer.to_string()
|
id = Enum.at(notes, 2).id |> Integer.to_string()
|
||||||
|
|
||||||
%{total: total, items: paginated} =
|
%{total: total, items: paginated} =
|
||||||
Pagination.fetch_paginated(Object, %{"since_id" => id, "total" => true})
|
Pagination.fetch_paginated(Object, %{since_id: id, total: true})
|
||||||
|
|
||||||
assert length(paginated) == 2
|
assert length(paginated) == 2
|
||||||
assert total == 5
|
assert total == 5
|
||||||
|
@ -41,7 +41,7 @@ test "paginates by max_id", %{notes: notes} do
|
||||||
id = Enum.at(notes, 1).id |> Integer.to_string()
|
id = Enum.at(notes, 1).id |> Integer.to_string()
|
||||||
|
|
||||||
%{total: total, items: paginated} =
|
%{total: total, items: paginated} =
|
||||||
Pagination.fetch_paginated(Object, %{"max_id" => id, "total" => true})
|
Pagination.fetch_paginated(Object, %{max_id: id, total: true})
|
||||||
|
|
||||||
assert length(paginated) == 1
|
assert length(paginated) == 1
|
||||||
assert total == 5
|
assert total == 5
|
||||||
|
@ -50,7 +50,7 @@ test "paginates by max_id", %{notes: notes} do
|
||||||
test "paginates by min_id & limit", %{notes: notes} do
|
test "paginates by min_id & limit", %{notes: notes} do
|
||||||
id = Enum.at(notes, 2).id |> Integer.to_string()
|
id = Enum.at(notes, 2).id |> Integer.to_string()
|
||||||
|
|
||||||
paginated = Pagination.fetch_paginated(Object, %{"min_id" => id, "limit" => 1})
|
paginated = Pagination.fetch_paginated(Object, %{min_id: id, limit: 1})
|
||||||
|
|
||||||
assert length(paginated) == 1
|
assert length(paginated) == 1
|
||||||
end
|
end
|
||||||
|
@ -64,13 +64,13 @@ test "paginates by min_id & limit", %{notes: notes} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "paginates by limit" do
|
test "paginates by limit" do
|
||||||
paginated = Pagination.fetch_paginated(Object, %{"limit" => 2}, :offset)
|
paginated = Pagination.fetch_paginated(Object, %{limit: 2}, :offset)
|
||||||
|
|
||||||
assert length(paginated) == 2
|
assert length(paginated) == 2
|
||||||
end
|
end
|
||||||
|
|
||||||
test "paginates by limit & offset" do
|
test "paginates by limit & offset" do
|
||||||
paginated = Pagination.fetch_paginated(Object, %{"limit" => 2, "offset" => 4}, :offset)
|
paginated = Pagination.fetch_paginated(Object, %{limit: 2, offset: 4}, :offset)
|
||||||
|
|
||||||
assert length(paginated) == 1
|
assert length(paginated) == 1
|
||||||
end
|
end
|
||||||
|
|
|
@ -62,10 +62,11 @@ test "relay is unfollowed" do
|
||||||
|
|
||||||
[undo_activity] =
|
[undo_activity] =
|
||||||
ActivityPub.fetch_activities([], %{
|
ActivityPub.fetch_activities([], %{
|
||||||
"type" => "Undo",
|
type: "Undo",
|
||||||
"actor_id" => follower_id,
|
actor_id: follower_id,
|
||||||
"limit" => 1,
|
limit: 1,
|
||||||
"skip_preload" => true
|
skip_preload: true,
|
||||||
|
invisible_actors: true
|
||||||
})
|
})
|
||||||
|
|
||||||
assert undo_activity.data["type"] == "Undo"
|
assert undo_activity.data["type"] == "Undo"
|
||||||
|
|
|
@ -1122,7 +1122,7 @@ test "hide a user's statuses from timelines and notifications" do
|
||||||
|
|
||||||
assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] ==
|
assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] ==
|
||||||
ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
|
ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
|
||||||
"user" => user2
|
user: user2
|
||||||
})
|
})
|
||||||
|
|
||||||
{:ok, _user} = User.deactivate(user)
|
{:ok, _user} = User.deactivate(user)
|
||||||
|
@ -1132,7 +1132,7 @@ test "hide a user's statuses from timelines and notifications" do
|
||||||
|
|
||||||
assert [] ==
|
assert [] ==
|
||||||
ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
|
ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
|
||||||
"user" => user2
|
user: user2
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1159,6 +1159,9 @@ test "it deactivates a user, all follow relationships and all activities", %{use
|
||||||
follower = insert(:user)
|
follower = insert(:user)
|
||||||
{:ok, follower} = User.follow(follower, user)
|
{:ok, follower} = User.follow(follower, user)
|
||||||
|
|
||||||
|
locked_user = insert(:user, name: "locked", locked: true)
|
||||||
|
{:ok, _} = User.follow(user, locked_user, :follow_pending)
|
||||||
|
|
||||||
object = insert(:note, user: user)
|
object = insert(:note, user: user)
|
||||||
activity = insert(:note_activity, user: user, note: object)
|
activity = insert(:note_activity, user: user, note: object)
|
||||||
|
|
||||||
|
@ -1177,6 +1180,8 @@ test "it deactivates a user, all follow relationships and all activities", %{use
|
||||||
refute User.following?(follower, user)
|
refute User.following?(follower, user)
|
||||||
assert %{deactivated: true} = User.get_by_id(user.id)
|
assert %{deactivated: true} = User.get_by_id(user.id)
|
||||||
|
|
||||||
|
assert [] == User.get_follow_requests(locked_user)
|
||||||
|
|
||||||
user_activities =
|
user_activities =
|
||||||
user.ap_id
|
user.ap_id
|
||||||
|> Activity.Queries.by_actor()
|
|> Activity.Queries.by_actor()
|
||||||
|
|
|
@ -804,17 +804,63 @@ test "it requires authentication", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /users/:nickname/outbox" do
|
describe "GET /users/:nickname/outbox" do
|
||||||
|
test "it paginates correctly", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
conn = assign(conn, :user, user)
|
||||||
|
outbox_endpoint = user.ap_id <> "/outbox"
|
||||||
|
|
||||||
|
_posts =
|
||||||
|
for i <- 0..25 do
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"})
|
||||||
|
activity
|
||||||
|
end
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get(outbox_endpoint <> "?page=true")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
result_ids = Enum.map(result["orderedItems"], fn x -> x["id"] end)
|
||||||
|
assert length(result["orderedItems"]) == 20
|
||||||
|
assert length(result_ids) == 20
|
||||||
|
assert result["next"]
|
||||||
|
assert String.starts_with?(result["next"], outbox_endpoint)
|
||||||
|
|
||||||
|
result_next =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get(result["next"])
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
result_next_ids = Enum.map(result_next["orderedItems"], fn x -> x["id"] end)
|
||||||
|
assert length(result_next["orderedItems"]) == 6
|
||||||
|
assert length(result_next_ids) == 6
|
||||||
|
refute Enum.find(result_next_ids, fn x -> x in result_ids end)
|
||||||
|
refute Enum.find(result_ids, fn x -> x in result_next_ids end)
|
||||||
|
assert String.starts_with?(result["id"], outbox_endpoint)
|
||||||
|
|
||||||
|
result_next_again =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get(result_next["id"])
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert result_next == result_next_again
|
||||||
|
end
|
||||||
|
|
||||||
test "it returns 200 even if there're no activities", %{conn: conn} do
|
test "it returns 200 even if there're no activities", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
outbox_endpoint = user.ap_id <> "/outbox"
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> put_req_header("accept", "application/activity+json")
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|> get("/users/#{user.nickname}/outbox")
|
|> get(outbox_endpoint)
|
||||||
|
|
||||||
result = json_response(conn, 200)
|
result = json_response(conn, 200)
|
||||||
assert user.ap_id <> "/outbox" == result["id"]
|
assert outbox_endpoint == result["id"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns a note activity in a collection", %{conn: conn} do
|
test "it returns a note activity in a collection", %{conn: conn} do
|
||||||
|
|
|
@ -82,30 +82,28 @@ test "it restricts by the appropriate visibility" do
|
||||||
|
|
||||||
{:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
|
{:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_activities([], %{visibility: "direct", actor_id: user.ap_id})
|
||||||
ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id})
|
|
||||||
|
|
||||||
assert activities == [direct_activity]
|
assert activities == [direct_activity]
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([], %{:visibility => "unlisted", "actor_id" => user.ap_id})
|
ActivityPub.fetch_activities([], %{visibility: "unlisted", actor_id: user.ap_id})
|
||||||
|
|
||||||
assert activities == [unlisted_activity]
|
assert activities == [unlisted_activity]
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([], %{:visibility => "private", "actor_id" => user.ap_id})
|
ActivityPub.fetch_activities([], %{visibility: "private", actor_id: user.ap_id})
|
||||||
|
|
||||||
assert activities == [private_activity]
|
assert activities == [private_activity]
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_activities([], %{visibility: "public", actor_id: user.ap_id})
|
||||||
ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id})
|
|
||||||
|
|
||||||
assert activities == [public_activity]
|
assert activities == [public_activity]
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([], %{
|
ActivityPub.fetch_activities([], %{
|
||||||
:visibility => ~w[private public],
|
visibility: ~w[private public],
|
||||||
"actor_id" => user.ap_id
|
actor_id: user.ap_id
|
||||||
})
|
})
|
||||||
|
|
||||||
assert activities == [public_activity, private_activity]
|
assert activities == [public_activity, private_activity]
|
||||||
|
@ -126,8 +124,8 @@ test "it excludes by the appropriate visibility" do
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([], %{
|
ActivityPub.fetch_activities([], %{
|
||||||
"exclude_visibilities" => "direct",
|
exclude_visibilities: "direct",
|
||||||
"actor_id" => user.ap_id
|
actor_id: user.ap_id
|
||||||
})
|
})
|
||||||
|
|
||||||
assert public_activity in activities
|
assert public_activity in activities
|
||||||
|
@ -137,8 +135,8 @@ test "it excludes by the appropriate visibility" do
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([], %{
|
ActivityPub.fetch_activities([], %{
|
||||||
"exclude_visibilities" => "unlisted",
|
exclude_visibilities: "unlisted",
|
||||||
"actor_id" => user.ap_id
|
actor_id: user.ap_id
|
||||||
})
|
})
|
||||||
|
|
||||||
assert public_activity in activities
|
assert public_activity in activities
|
||||||
|
@ -148,8 +146,8 @@ test "it excludes by the appropriate visibility" do
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([], %{
|
ActivityPub.fetch_activities([], %{
|
||||||
"exclude_visibilities" => "private",
|
exclude_visibilities: "private",
|
||||||
"actor_id" => user.ap_id
|
actor_id: user.ap_id
|
||||||
})
|
})
|
||||||
|
|
||||||
assert public_activity in activities
|
assert public_activity in activities
|
||||||
|
@ -159,8 +157,8 @@ test "it excludes by the appropriate visibility" do
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([], %{
|
ActivityPub.fetch_activities([], %{
|
||||||
"exclude_visibilities" => "public",
|
exclude_visibilities: "public",
|
||||||
"actor_id" => user.ap_id
|
actor_id: user.ap_id
|
||||||
})
|
})
|
||||||
|
|
||||||
refute public_activity in activities
|
refute public_activity in activities
|
||||||
|
@ -193,23 +191,22 @@ test "it fetches the appropriate tag-restricted posts" do
|
||||||
{:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"})
|
{:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"})
|
||||||
{:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"})
|
{:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"})
|
||||||
|
|
||||||
fetch_one = ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => "test"})
|
fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"})
|
||||||
|
|
||||||
fetch_two =
|
fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]})
|
||||||
ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => ["test", "essais"]})
|
|
||||||
|
|
||||||
fetch_three =
|
fetch_three =
|
||||||
ActivityPub.fetch_activities([], %{
|
ActivityPub.fetch_activities([], %{
|
||||||
"type" => "Create",
|
type: "Create",
|
||||||
"tag" => ["test", "essais"],
|
tag: ["test", "essais"],
|
||||||
"tag_reject" => ["reject"]
|
tag_reject: ["reject"]
|
||||||
})
|
})
|
||||||
|
|
||||||
fetch_four =
|
fetch_four =
|
||||||
ActivityPub.fetch_activities([], %{
|
ActivityPub.fetch_activities([], %{
|
||||||
"type" => "Create",
|
type: "Create",
|
||||||
"tag" => ["test"],
|
tag: ["test"],
|
||||||
"tag_all" => ["test", "reject"]
|
tag_all: ["test", "reject"]
|
||||||
})
|
})
|
||||||
|
|
||||||
assert fetch_one == [status_one, status_three]
|
assert fetch_one == [status_one, status_three]
|
||||||
|
@ -375,7 +372,7 @@ test "can be fetched into a timeline" do
|
||||||
_listen_activity_2 = insert(:listen)
|
_listen_activity_2 = insert(:listen)
|
||||||
_listen_activity_3 = insert(:listen)
|
_listen_activity_3 = insert(:listen)
|
||||||
|
|
||||||
timeline = ActivityPub.fetch_activities([], %{"type" => ["Listen"]})
|
timeline = ActivityPub.fetch_activities([], %{type: ["Listen"]})
|
||||||
|
|
||||||
assert length(timeline) == 3
|
assert length(timeline) == 3
|
||||||
end
|
end
|
||||||
|
@ -507,7 +504,7 @@ test "retrieves activities that have a given context" do
|
||||||
|
|
||||||
{:ok, _user_relationship} = User.block(user, %{ap_id: activity_five.data["actor"]})
|
{:ok, _user_relationship} = User.block(user, %{ap_id: activity_five.data["actor"]})
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities_for_context("2hu", %{"blocking_user" => user})
|
activities = ActivityPub.fetch_activities_for_context("2hu", %{blocking_user: user})
|
||||||
assert activities == [activity_two, activity]
|
assert activities == [activity_two, activity]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -520,8 +517,7 @@ test "doesn't return blocked activities" do
|
||||||
booster = insert(:user)
|
booster = insert(:user)
|
||||||
{:ok, _user_relationship} = User.block(user, %{ap_id: activity_one.data["actor"]})
|
{:ok, _user_relationship} = User.block(user, %{ap_id: activity_one.data["actor"]})
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
|
||||||
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
|
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
assert Enum.member?(activities, activity_three)
|
assert Enum.member?(activities, activity_three)
|
||||||
|
@ -529,8 +525,7 @@ test "doesn't return blocked activities" do
|
||||||
|
|
||||||
{:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
|
{:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
|
||||||
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
|
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
assert Enum.member?(activities, activity_three)
|
assert Enum.member?(activities, activity_three)
|
||||||
|
@ -541,16 +536,14 @@ test "doesn't return blocked activities" do
|
||||||
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
||||||
activity_three = Activity.get_by_id(activity_three.id)
|
activity_three = Activity.get_by_id(activity_three.id)
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
|
||||||
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
|
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
refute Enum.member?(activities, activity_three)
|
refute Enum.member?(activities, activity_three)
|
||||||
refute Enum.member?(activities, boost_activity)
|
refute Enum.member?(activities, boost_activity)
|
||||||
assert Enum.member?(activities, activity_one)
|
assert Enum.member?(activities, activity_one)
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_activities([], %{blocking_user: nil, skip_preload: true})
|
||||||
ActivityPub.fetch_activities([], %{"blocking_user" => nil, "skip_preload" => true})
|
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
assert Enum.member?(activities, activity_three)
|
assert Enum.member?(activities, activity_three)
|
||||||
|
@ -573,7 +566,7 @@ test "doesn't return transitive interactions concerning blocked users" do
|
||||||
|
|
||||||
{:ok, activity_four} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"})
|
{:ok, activity_four} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"})
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
|
activities = ActivityPub.fetch_activities([], %{blocking_user: blocker})
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_one)
|
assert Enum.member?(activities, activity_one)
|
||||||
refute Enum.member?(activities, activity_two)
|
refute Enum.member?(activities, activity_two)
|
||||||
|
@ -595,7 +588,7 @@ test "doesn't return announce activities concerning blocked users" do
|
||||||
{:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)
|
{:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
|
ActivityPub.fetch_activities([], %{blocking_user: blocker})
|
||||||
|> Enum.map(fn act -> act.id end)
|
|> Enum.map(fn act -> act.id end)
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_one.id)
|
assert Enum.member?(activities, activity_one.id)
|
||||||
|
@ -611,8 +604,7 @@ test "doesn't return activities from blocked domains" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, user} = User.block_domain(user, domain)
|
{:ok, user} = User.block_domain(user, domain)
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
|
||||||
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
|
|
||||||
|
|
||||||
refute activity in activities
|
refute activity in activities
|
||||||
|
|
||||||
|
@ -620,8 +612,7 @@ test "doesn't return activities from blocked domains" do
|
||||||
ActivityPub.follow(user, followed_user)
|
ActivityPub.follow(user, followed_user)
|
||||||
{:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)
|
{:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
|
||||||
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
|
|
||||||
|
|
||||||
refute repeat_activity in activities
|
refute repeat_activity in activities
|
||||||
end
|
end
|
||||||
|
@ -641,8 +632,7 @@ test "does return activities from followed users on blocked domains" do
|
||||||
note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
|
note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
|
||||||
activity = insert(:note_activity, %{note: note})
|
activity = insert(:note_activity, %{note: note})
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
|
||||||
ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
|
|
||||||
|
|
||||||
assert activity in activities
|
assert activity in activities
|
||||||
|
|
||||||
|
@ -653,8 +643,7 @@ test "does return activities from followed users on blocked domains" do
|
||||||
bad_activity = insert(:note_activity, %{note: bad_note})
|
bad_activity = insert(:note_activity, %{note: bad_note})
|
||||||
{:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)
|
{:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
|
||||||
ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
|
|
||||||
|
|
||||||
refute repeat_activity in activities
|
refute repeat_activity in activities
|
||||||
end
|
end
|
||||||
|
@ -669,8 +658,7 @@ test "doesn't return muted activities" do
|
||||||
activity_one_actor = User.get_by_ap_id(activity_one.data["actor"])
|
activity_one_actor = User.get_by_ap_id(activity_one.data["actor"])
|
||||||
{:ok, _user_relationships} = User.mute(user, activity_one_actor)
|
{:ok, _user_relationships} = User.mute(user, activity_one_actor)
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
|
||||||
ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
|
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
assert Enum.member?(activities, activity_three)
|
assert Enum.member?(activities, activity_three)
|
||||||
|
@ -679,9 +667,9 @@ test "doesn't return muted activities" do
|
||||||
# Calling with 'with_muted' will deliver muted activities, too.
|
# Calling with 'with_muted' will deliver muted activities, too.
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([], %{
|
ActivityPub.fetch_activities([], %{
|
||||||
"muting_user" => user,
|
muting_user: user,
|
||||||
"with_muted" => true,
|
with_muted: true,
|
||||||
"skip_preload" => true
|
skip_preload: true
|
||||||
})
|
})
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
|
@ -690,8 +678,7 @@ test "doesn't return muted activities" do
|
||||||
|
|
||||||
{:ok, _user_mute} = User.unmute(user, activity_one_actor)
|
{:ok, _user_mute} = User.unmute(user, activity_one_actor)
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
|
||||||
ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
|
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
assert Enum.member?(activities, activity_three)
|
assert Enum.member?(activities, activity_three)
|
||||||
|
@ -703,15 +690,14 @@ test "doesn't return muted activities" do
|
||||||
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
||||||
activity_three = Activity.get_by_id(activity_three.id)
|
activity_three = Activity.get_by_id(activity_three.id)
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
|
||||||
ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
|
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
refute Enum.member?(activities, activity_three)
|
refute Enum.member?(activities, activity_three)
|
||||||
refute Enum.member?(activities, boost_activity)
|
refute Enum.member?(activities, boost_activity)
|
||||||
assert Enum.member?(activities, activity_one)
|
assert Enum.member?(activities, activity_one)
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true})
|
activities = ActivityPub.fetch_activities([], %{muting_user: nil, skip_preload: true})
|
||||||
|
|
||||||
assert Enum.member?(activities, activity_two)
|
assert Enum.member?(activities, activity_two)
|
||||||
assert Enum.member?(activities, activity_three)
|
assert Enum.member?(activities, activity_three)
|
||||||
|
@ -727,7 +713,7 @@ test "doesn't return thread muted activities" do
|
||||||
|
|
||||||
{:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
|
{:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
|
||||||
|
|
||||||
assert [_activity_one] = ActivityPub.fetch_activities([], %{"muting_user" => user})
|
assert [_activity_one] = ActivityPub.fetch_activities([], %{muting_user: user})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns thread muted activities when with_muted is set" do
|
test "returns thread muted activities when with_muted is set" do
|
||||||
|
@ -739,7 +725,7 @@ test "returns thread muted activities when with_muted is set" do
|
||||||
{:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
|
{:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
|
||||||
|
|
||||||
assert [_activity_two, _activity_one] =
|
assert [_activity_two, _activity_one] =
|
||||||
ActivityPub.fetch_activities([], %{"muting_user" => user, "with_muted" => true})
|
ActivityPub.fetch_activities([], %{muting_user: user, with_muted: true})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does include announces on request" do
|
test "does include announces on request" do
|
||||||
|
@ -761,7 +747,7 @@ test "excludes reblogs on request" do
|
||||||
{:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
|
{:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
|
||||||
{:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
|
{:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
|
||||||
|
|
||||||
[activity] = ActivityPub.fetch_user_activities(user, nil, %{"exclude_reblogs" => "true"})
|
[activity] = ActivityPub.fetch_user_activities(user, nil, %{exclude_reblogs: true})
|
||||||
|
|
||||||
assert activity == expected_activity
|
assert activity == expected_activity
|
||||||
end
|
end
|
||||||
|
@ -804,7 +790,7 @@ test "retrieves ids starting from a since_id" do
|
||||||
expected_activities = ActivityBuilder.insert_list(10)
|
expected_activities = ActivityBuilder.insert_list(10)
|
||||||
since_id = List.last(activities).id
|
since_id = List.last(activities).id
|
||||||
|
|
||||||
activities = ActivityPub.fetch_public_activities(%{"since_id" => since_id})
|
activities = ActivityPub.fetch_public_activities(%{since_id: since_id})
|
||||||
|
|
||||||
assert collect_ids(activities) == collect_ids(expected_activities)
|
assert collect_ids(activities) == collect_ids(expected_activities)
|
||||||
assert length(activities) == 10
|
assert length(activities) == 10
|
||||||
|
@ -819,7 +805,7 @@ test "retrieves ids up to max_id" do
|
||||||
|> ActivityBuilder.insert_list()
|
|> ActivityBuilder.insert_list()
|
||||||
|> List.first()
|
|> List.first()
|
||||||
|
|
||||||
activities = ActivityPub.fetch_public_activities(%{"max_id" => max_id})
|
activities = ActivityPub.fetch_public_activities(%{max_id: max_id})
|
||||||
|
|
||||||
assert length(activities) == 20
|
assert length(activities) == 20
|
||||||
assert collect_ids(activities) == collect_ids(expected_activities)
|
assert collect_ids(activities) == collect_ids(expected_activities)
|
||||||
|
@ -831,8 +817,7 @@ test "paginates via offset/limit" do
|
||||||
|
|
||||||
later_activities = ActivityBuilder.insert_list(10)
|
later_activities = ActivityBuilder.insert_list(10)
|
||||||
|
|
||||||
activities =
|
activities = ActivityPub.fetch_public_activities(%{page: "2", page_size: "20"}, :offset)
|
||||||
ActivityPub.fetch_public_activities(%{"page" => "2", "page_size" => "20"}, :offset)
|
|
||||||
|
|
||||||
assert length(activities) == 20
|
assert length(activities) == 20
|
||||||
|
|
||||||
|
@ -848,7 +833,7 @@ test "doesn't return reblogs for users for whom reblogs have been muted" do
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.repeat(activity.id, booster)
|
{:ok, activity} = CommonAPI.repeat(activity.id, booster)
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
|
activities = ActivityPub.fetch_activities([], %{muting_user: user})
|
||||||
|
|
||||||
refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
|
refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
|
||||||
end
|
end
|
||||||
|
@ -862,7 +847,7 @@ test "returns reblogs for users for whom reblogs have not been muted" do
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.repeat(activity.id, booster)
|
{:ok, activity} = CommonAPI.repeat(activity.id, booster)
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
|
activities = ActivityPub.fetch_activities([], %{muting_user: user})
|
||||||
|
|
||||||
assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
|
assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
|
||||||
end
|
end
|
||||||
|
@ -1066,7 +1051,7 @@ test "it filters broken threads" do
|
||||||
assert length(activities) == 3
|
assert length(activities) == 3
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1})
|
ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{user: user1})
|
||||||
|> Enum.map(fn a -> a.id end)
|
|> Enum.map(fn a -> a.id end)
|
||||||
|
|
||||||
assert [public_activity.id, private_activity_1.id] == activities
|
assert [public_activity.id, private_activity_1.id] == activities
|
||||||
|
@ -1115,7 +1100,7 @@ test "returned pinned statuses" do
|
||||||
CommonAPI.pin(activity_three.id, user)
|
CommonAPI.pin(activity_three.id, user)
|
||||||
user = refresh_record(user)
|
user = refresh_record(user)
|
||||||
|
|
||||||
activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
|
activities = ActivityPub.fetch_user_activities(user, nil, %{pinned: true})
|
||||||
|
|
||||||
assert 3 = length(activities)
|
assert 3 = length(activities)
|
||||||
end
|
end
|
||||||
|
@ -1226,7 +1211,7 @@ test "fetch_activities/2 returns activities addressed to a list " do
|
||||||
activity = Repo.preload(activity, :bookmark)
|
activity = Repo.preload(activity, :bookmark)
|
||||||
activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
|
activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
|
||||||
|
|
||||||
assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
|
assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
|
||||||
end
|
end
|
||||||
|
|
||||||
def data_uri do
|
def data_uri do
|
||||||
|
@ -1400,7 +1385,7 @@ test "returns a favourite activities sorted by adds to favorite" do
|
||||||
|
|
||||||
assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
|
assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
|
||||||
|
|
||||||
result = ActivityPub.fetch_favourites(user, %{"limit" => 2})
|
result = ActivityPub.fetch_favourites(user, %{limit: 2})
|
||||||
assert Enum.map(result, & &1.id) == [a1.id, a5.id]
|
assert Enum.map(result, & &1.id) == [a1.id, a5.id]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1470,7 +1455,7 @@ test "doesn't retrieve replies activities with exclude_replies" do
|
||||||
|
|
||||||
{:ok, _reply} = CommonAPI.post(user, %{status: "yeah", in_reply_to_status_id: activity.id})
|
{:ok, _reply} = CommonAPI.post(user, %{status: "yeah", in_reply_to_status_id: activity.id})
|
||||||
|
|
||||||
[result] = ActivityPub.fetch_public_activities(%{"exclude_replies" => "true"})
|
[result] = ActivityPub.fetch_public_activities(%{exclude_replies: true})
|
||||||
|
|
||||||
assert result.id == activity.id
|
assert result.id == activity.id
|
||||||
|
|
||||||
|
@ -1483,11 +1468,11 @@ test "doesn't retrieve replies activities with exclude_replies" do
|
||||||
test "public timeline", %{users: %{u1: user}} do
|
test "public timeline", %{users: %{u1: user}} do
|
||||||
activities_ids =
|
activities_ids =
|
||||||
%{}
|
%{}
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("local_only", false)
|
|> Map.put(:local_only, false)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|> Enum.map(& &1.id)
|
|> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
@ -1504,12 +1489,12 @@ test "public timeline with reply_visibility `following`", %{
|
||||||
} do
|
} do
|
||||||
activities_ids =
|
activities_ids =
|
||||||
%{}
|
%{}
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("local_only", false)
|
|> Map.put(:local_only, false)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("reply_visibility", "following")
|
|> Map.put(:reply_visibility, "following")
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|> Enum.map(& &1.id)
|
|> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
@ -1531,12 +1516,12 @@ test "public timeline with reply_visibility `self`", %{
|
||||||
} do
|
} do
|
||||||
activities_ids =
|
activities_ids =
|
||||||
%{}
|
%{}
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("local_only", false)
|
|> Map.put(:local_only, false)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("reply_visibility", "self")
|
|> Map.put(:reply_visibility, "self")
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|> Enum.map(& &1.id)
|
|> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
@ -1555,11 +1540,11 @@ test "home timeline", %{
|
||||||
} do
|
} do
|
||||||
params =
|
params =
|
||||||
%{}
|
%{}
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("user", user)
|
|> Map.put(:user, user)
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|
|
||||||
activities_ids =
|
activities_ids =
|
||||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
||||||
|
@ -1593,12 +1578,12 @@ test "home timeline with reply_visibility `following`", %{
|
||||||
} do
|
} do
|
||||||
params =
|
params =
|
||||||
%{}
|
%{}
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("user", user)
|
|> Map.put(:user, user)
|
||||||
|> Map.put("reply_visibility", "following")
|
|> Map.put(:reply_visibility, "following")
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|
|
||||||
activities_ids =
|
activities_ids =
|
||||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
||||||
|
@ -1632,12 +1617,12 @@ test "home timeline with reply_visibility `self`", %{
|
||||||
} do
|
} do
|
||||||
params =
|
params =
|
||||||
%{}
|
%{}
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("user", user)
|
|> Map.put(:user, user)
|
||||||
|> Map.put("reply_visibility", "self")
|
|> Map.put(:reply_visibility, "self")
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|
|
||||||
activities_ids =
|
activities_ids =
|
||||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
||||||
|
@ -1666,11 +1651,11 @@ test "home timeline with reply_visibility `self`", %{
|
||||||
test "public timeline", %{users: %{u1: user}} do
|
test "public timeline", %{users: %{u1: user}} do
|
||||||
activities_ids =
|
activities_ids =
|
||||||
%{}
|
%{}
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("local_only", false)
|
|> Map.put(:local_only, false)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("user", user)
|
|> Map.put(:user, user)
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|> Enum.map(& &1.id)
|
|> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
@ -1680,13 +1665,13 @@ test "public timeline", %{users: %{u1: user}} do
|
||||||
test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
|
test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
|
||||||
activities_ids =
|
activities_ids =
|
||||||
%{}
|
%{}
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("local_only", false)
|
|> Map.put(:local_only, false)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("reply_visibility", "following")
|
|> Map.put(:reply_visibility, "following")
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|> Map.put("user", user)
|
|> Map.put(:user, user)
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|> Enum.map(& &1.id)
|
|> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
@ -1696,13 +1681,13 @@ test "public timeline with default reply_visibility `following`", %{users: %{u1:
|
||||||
test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
|
test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
|
||||||
activities_ids =
|
activities_ids =
|
||||||
%{}
|
%{}
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("local_only", false)
|
|> Map.put(:local_only, false)
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("reply_visibility", "self")
|
|> Map.put(:reply_visibility, "self")
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|> Map.put("user", user)
|
|> Map.put(:user, user)
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|> Enum.map(& &1.id)
|
|> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
@ -1712,10 +1697,10 @@ test "public timeline with default reply_visibility `self`", %{users: %{u1: user
|
||||||
test "home timeline", %{users: %{u1: user}} do
|
test "home timeline", %{users: %{u1: user}} do
|
||||||
params =
|
params =
|
||||||
%{}
|
%{}
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("user", user)
|
|> Map.put(:user, user)
|
||||||
|
|
||||||
activities_ids =
|
activities_ids =
|
||||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
||||||
|
@ -1727,12 +1712,12 @@ test "home timeline", %{users: %{u1: user}} do
|
||||||
test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
|
test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
|
||||||
params =
|
params =
|
||||||
%{}
|
%{}
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("user", user)
|
|> Map.put(:user, user)
|
||||||
|> Map.put("reply_visibility", "following")
|
|> Map.put(:reply_visibility, "following")
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|
|
||||||
activities_ids =
|
activities_ids =
|
||||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
||||||
|
@ -1751,12 +1736,12 @@ test "home timeline with default reply_visibility `self`", %{
|
||||||
} do
|
} do
|
||||||
params =
|
params =
|
||||||
%{}
|
%{}
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|> Map.put("blocking_user", user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put("muting_user", user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put("user", user)
|
|> Map.put(:user, user)
|
||||||
|> Map.put("reply_visibility", "self")
|
|> Map.put(:reply_visibility, "self")
|
||||||
|> Map.put("reply_filtering_user", user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|
|
||||||
activities_ids =
|
activities_ids =
|
||||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
||||||
|
|
|
@ -158,35 +158,4 @@ test "sets correct totalItems when follows are hidden but the follow counter is
|
||||||
assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
|
assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "activity collection page aginates correctly" do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
posts =
|
|
||||||
for i <- 0..25 do
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"})
|
|
||||||
activity
|
|
||||||
end
|
|
||||||
|
|
||||||
# outbox sorts chronologically, newest first, with ten per page
|
|
||||||
posts = Enum.reverse(posts)
|
|
||||||
|
|
||||||
%{"next" => next_url} =
|
|
||||||
UserView.render("activity_collection_page.json", %{
|
|
||||||
iri: "#{user.ap_id}/outbox",
|
|
||||||
activities: Enum.take(posts, 10)
|
|
||||||
})
|
|
||||||
|
|
||||||
next_id = Enum.at(posts, 9).id
|
|
||||||
assert next_url =~ next_id
|
|
||||||
|
|
||||||
%{"next" => next_url} =
|
|
||||||
UserView.render("activity_collection_page.json", %{
|
|
||||||
iri: "#{user.ap_id}/outbox",
|
|
||||||
activities: Enum.take(Enum.drop(posts, 10), 10)
|
|
||||||
})
|
|
||||||
|
|
||||||
next_id = Enum.at(posts, 19).id
|
|
||||||
assert next_url =~ next_id
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
File diff suppressed because it is too large
Load diff
1290
test/web/admin_api/controllers/config_controller_test.exs
Normal file
1290
test/web/admin_api/controllers/config_controller_test.exs
Normal file
File diff suppressed because it is too large
Load diff
281
test/web/admin_api/controllers/invite_controller_test.exs
Normal file
281
test/web/admin_api/controllers/invite_controller_test.exs
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.InviteControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.UserInviteToken
|
||||||
|
|
||||||
|
setup do
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
token = insert(:oauth_admin_token, user: admin)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|
||||||
|
{:ok, %{admin: admin, token: token, conn: conn}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/users/email_invite, with valid config" do
|
||||||
|
setup do: clear_config([:instance, :registrations_open], false)
|
||||||
|
setup do: clear_config([:instance, :invites_enabled], true)
|
||||||
|
|
||||||
|
test "sends invitation and returns 204", %{admin: admin, conn: conn} do
|
||||||
|
recipient_email = "foo@bar.com"
|
||||||
|
recipient_name = "J. D."
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json;charset=utf-8")
|
||||||
|
|> post("/api/pleroma/admin/users/email_invite", %{
|
||||||
|
email: recipient_email,
|
||||||
|
name: recipient_name
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, :no_content)
|
||||||
|
|
||||||
|
token_record = List.last(Repo.all(Pleroma.UserInviteToken))
|
||||||
|
assert token_record
|
||||||
|
refute token_record.used
|
||||||
|
|
||||||
|
notify_email = Config.get([:instance, :notify_email])
|
||||||
|
instance_name = Config.get([:instance, :name])
|
||||||
|
|
||||||
|
email =
|
||||||
|
Pleroma.Emails.UserEmail.user_invitation_email(
|
||||||
|
admin,
|
||||||
|
token_record,
|
||||||
|
recipient_email,
|
||||||
|
recipient_name
|
||||||
|
)
|
||||||
|
|
||||||
|
Swoosh.TestAssertions.assert_email_sent(
|
||||||
|
from: {instance_name, notify_email},
|
||||||
|
to: {recipient_name, recipient_email},
|
||||||
|
html_body: email.html_body
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns 403 if requested by a non-admin" do
|
||||||
|
non_admin_user = insert(:user)
|
||||||
|
token = insert(:oauth_token, user: non_admin_user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, non_admin_user)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> put_req_header("content-type", "application/json;charset=utf-8")
|
||||||
|
|> post("/api/pleroma/admin/users/email_invite", %{
|
||||||
|
email: "foo@bar.com",
|
||||||
|
name: "JD"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response(conn, :forbidden)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "email with +", %{conn: conn, admin: admin} do
|
||||||
|
recipient_email = "foo+bar@baz.com"
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json;charset=utf-8")
|
||||||
|
|> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email})
|
||||||
|
|> json_response_and_validate_schema(:no_content)
|
||||||
|
|
||||||
|
token_record =
|
||||||
|
Pleroma.UserInviteToken
|
||||||
|
|> Repo.all()
|
||||||
|
|> List.last()
|
||||||
|
|
||||||
|
assert token_record
|
||||||
|
refute token_record.used
|
||||||
|
|
||||||
|
notify_email = Config.get([:instance, :notify_email])
|
||||||
|
instance_name = Config.get([:instance, :name])
|
||||||
|
|
||||||
|
email =
|
||||||
|
Pleroma.Emails.UserEmail.user_invitation_email(
|
||||||
|
admin,
|
||||||
|
token_record,
|
||||||
|
recipient_email
|
||||||
|
)
|
||||||
|
|
||||||
|
Swoosh.TestAssertions.assert_email_sent(
|
||||||
|
from: {instance_name, notify_email},
|
||||||
|
to: recipient_email,
|
||||||
|
html_body: email.html_body
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
|
||||||
|
setup do: clear_config([:instance, :registrations_open])
|
||||||
|
setup do: clear_config([:instance, :invites_enabled])
|
||||||
|
|
||||||
|
test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
|
||||||
|
Config.put([:instance, :registrations_open], false)
|
||||||
|
Config.put([:instance, :invites_enabled], false)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/email_invite", %{
|
||||||
|
email: "foo@bar.com",
|
||||||
|
name: "JD"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, :bad_request) ==
|
||||||
|
%{
|
||||||
|
"error" =>
|
||||||
|
"To send invites you need to set the `invites_enabled` option to true."
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
|
||||||
|
Config.put([:instance, :registrations_open], true)
|
||||||
|
Config.put([:instance, :invites_enabled], true)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/email_invite", %{
|
||||||
|
email: "foo@bar.com",
|
||||||
|
name: "JD"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, :bad_request) ==
|
||||||
|
%{
|
||||||
|
"error" =>
|
||||||
|
"To send invites you need to set the `registrations_open` option to false."
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/users/invite_token" do
|
||||||
|
test "without options", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/invite_token")
|
||||||
|
|
||||||
|
invite_json = json_response_and_validate_schema(conn, 200)
|
||||||
|
invite = UserInviteToken.find_by_token!(invite_json["token"])
|
||||||
|
refute invite.used
|
||||||
|
refute invite.expires_at
|
||||||
|
refute invite.max_use
|
||||||
|
assert invite.invite_type == "one_time"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with expires_at", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/invite_token", %{
|
||||||
|
"expires_at" => Date.to_string(Date.utc_today())
|
||||||
|
})
|
||||||
|
|
||||||
|
invite_json = json_response_and_validate_schema(conn, 200)
|
||||||
|
invite = UserInviteToken.find_by_token!(invite_json["token"])
|
||||||
|
|
||||||
|
refute invite.used
|
||||||
|
assert invite.expires_at == Date.utc_today()
|
||||||
|
refute invite.max_use
|
||||||
|
assert invite.invite_type == "date_limited"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with max_use", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
|
||||||
|
|
||||||
|
invite_json = json_response_and_validate_schema(conn, 200)
|
||||||
|
invite = UserInviteToken.find_by_token!(invite_json["token"])
|
||||||
|
refute invite.used
|
||||||
|
refute invite.expires_at
|
||||||
|
assert invite.max_use == 150
|
||||||
|
assert invite.invite_type == "reusable"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with max use and expires_at", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/invite_token", %{
|
||||||
|
"max_use" => 150,
|
||||||
|
"expires_at" => Date.to_string(Date.utc_today())
|
||||||
|
})
|
||||||
|
|
||||||
|
invite_json = json_response_and_validate_schema(conn, 200)
|
||||||
|
invite = UserInviteToken.find_by_token!(invite_json["token"])
|
||||||
|
refute invite.used
|
||||||
|
assert invite.expires_at == Date.utc_today()
|
||||||
|
assert invite.max_use == 150
|
||||||
|
assert invite.invite_type == "reusable_date_limited"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/users/invites" do
|
||||||
|
test "no invites", %{conn: conn} do
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users/invites")
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, 200) == %{"invites" => []}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with invite", %{conn: conn} do
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite()
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users/invites")
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, 200) == %{
|
||||||
|
"invites" => [
|
||||||
|
%{
|
||||||
|
"expires_at" => nil,
|
||||||
|
"id" => invite.id,
|
||||||
|
"invite_type" => "one_time",
|
||||||
|
"max_use" => nil,
|
||||||
|
"token" => invite.token,
|
||||||
|
"used" => false,
|
||||||
|
"uses" => 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/users/revoke_invite" do
|
||||||
|
test "with token", %{conn: conn} do
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite()
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, 200) == %{
|
||||||
|
"expires_at" => nil,
|
||||||
|
"id" => invite.id,
|
||||||
|
"invite_type" => "one_time",
|
||||||
|
"max_use" => nil,
|
||||||
|
"token" => invite.token,
|
||||||
|
"used" => true,
|
||||||
|
"uses" => 0
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with invalid token", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
220
test/web/admin_api/controllers/oauth_app_controller_test.exs
Normal file
220
test/web/admin_api/controllers/oauth_app_controller_test.exs
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.OAuthAppControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Web
|
||||||
|
|
||||||
|
setup do
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
token = insert(:oauth_admin_token, user: admin)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|
||||||
|
{:ok, %{admin: admin, token: token, conn: conn}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/oauth_app" do
|
||||||
|
test "errors", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/oauth_app", %{})
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"error" => "Missing field: name. Missing field: redirect_uris."
|
||||||
|
} = response
|
||||||
|
end
|
||||||
|
|
||||||
|
test "success", %{conn: conn} do
|
||||||
|
base_url = Web.base_url()
|
||||||
|
app_name = "Trusted app"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/oauth_app", %{
|
||||||
|
name: app_name,
|
||||||
|
redirect_uris: base_url
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"client_id" => _,
|
||||||
|
"client_secret" => _,
|
||||||
|
"name" => ^app_name,
|
||||||
|
"redirect_uri" => ^base_url,
|
||||||
|
"trusted" => false
|
||||||
|
} = response
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with trusted", %{conn: conn} do
|
||||||
|
base_url = Web.base_url()
|
||||||
|
app_name = "Trusted app"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/oauth_app", %{
|
||||||
|
name: app_name,
|
||||||
|
redirect_uris: base_url,
|
||||||
|
trusted: true
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"client_id" => _,
|
||||||
|
"client_secret" => _,
|
||||||
|
"name" => ^app_name,
|
||||||
|
"redirect_uri" => ^base_url,
|
||||||
|
"trusted" => true
|
||||||
|
} = response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/oauth_app" do
|
||||||
|
setup do
|
||||||
|
app = insert(:oauth_app)
|
||||||
|
{:ok, app: app}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "list", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{"apps" => apps, "count" => count, "page_size" => _} = response
|
||||||
|
|
||||||
|
assert length(apps) == count
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with page size", %{conn: conn} do
|
||||||
|
insert(:oauth_app)
|
||||||
|
page_size = 1
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app?page_size=#{page_size}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response
|
||||||
|
|
||||||
|
assert length(apps) == page_size
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search by client name", %{conn: conn, app: app} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app?name=#{app.client_name}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
||||||
|
|
||||||
|
assert returned["client_id"] == app.client_id
|
||||||
|
assert returned["name"] == app.client_name
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search by client id", %{conn: conn, app: app} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app?client_id=#{app.client_id}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
||||||
|
|
||||||
|
assert returned["client_id"] == app.client_id
|
||||||
|
assert returned["name"] == app.client_name
|
||||||
|
end
|
||||||
|
|
||||||
|
test "only trusted", %{conn: conn} do
|
||||||
|
app = insert(:oauth_app, trusted: true)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app?trusted=true")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
||||||
|
|
||||||
|
assert returned["client_id"] == app.client_id
|
||||||
|
assert returned["name"] == app.client_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE /api/pleroma/admin/oauth_app/:id" do
|
||||||
|
test "with id", %{conn: conn} do
|
||||||
|
app = insert(:oauth_app)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id))
|
||||||
|
|> json_response_and_validate_schema(:no_content)
|
||||||
|
|
||||||
|
assert response == ""
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with non existance id", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> delete("/api/pleroma/admin/oauth_app/0")
|
||||||
|
|> json_response_and_validate_schema(:bad_request)
|
||||||
|
|
||||||
|
assert response == ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PATCH /api/pleroma/admin/oauth_app/:id" do
|
||||||
|
test "with id", %{conn: conn} do
|
||||||
|
app = insert(:oauth_app)
|
||||||
|
|
||||||
|
name = "another name"
|
||||||
|
url = "https://example.com"
|
||||||
|
scopes = ["admin"]
|
||||||
|
id = app.id
|
||||||
|
website = "http://website.com"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/oauth_app/#{id}", %{
|
||||||
|
name: name,
|
||||||
|
trusted: true,
|
||||||
|
redirect_uris: url,
|
||||||
|
scopes: scopes,
|
||||||
|
website: website
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"client_id" => _,
|
||||||
|
"client_secret" => _,
|
||||||
|
"id" => ^id,
|
||||||
|
"name" => ^name,
|
||||||
|
"redirect_uri" => ^url,
|
||||||
|
"trusted" => true,
|
||||||
|
"website" => ^website
|
||||||
|
} = response
|
||||||
|
end
|
||||||
|
|
||||||
|
test "without id", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/oauth_app/0")
|
||||||
|
|> json_response_and_validate_schema(:bad_request)
|
||||||
|
|
||||||
|
assert response == ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
92
test/web/admin_api/controllers/relay_controller_test.exs
Normal file
92
test/web/admin_api/controllers/relay_controller_test.exs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.RelayControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
setup_all do
|
||||||
|
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
setup do
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
token = insert(:oauth_admin_token, user: admin)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|
||||||
|
{:ok, %{admin: admin, token: token, conn: conn}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "relays" do
|
||||||
|
test "POST /relay", %{conn: conn, admin: admin} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/relay", %{
|
||||||
|
relay_url: "http://mastodon.example.org/users/admin"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, 200) ==
|
||||||
|
"http://mastodon.example.org/users/admin"
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "GET /relay", %{conn: conn} do
|
||||||
|
relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
|
||||||
|
|
||||||
|
["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
|
||||||
|
|> Enum.each(fn ap_id ->
|
||||||
|
{:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
|
||||||
|
User.follow(relay_user, user)
|
||||||
|
end)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/relay")
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, 200)["relays"] --
|
||||||
|
["mastodon.example.org", "mstdn.io"] == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "DELETE /relay", %{conn: conn, admin: admin} do
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/relay", %{
|
||||||
|
relay_url: "http://mastodon.example.org/users/admin"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> delete("/api/pleroma/admin/relay", %{
|
||||||
|
relay_url: "http://mastodon.example.org/users/admin"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, 200) ==
|
||||||
|
"http://mastodon.example.org/users/admin"
|
||||||
|
|
||||||
|
[log_entry_one, log_entry_two] = Repo.all(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry_one) ==
|
||||||
|
"@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry_two) ==
|
||||||
|
"@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
374
test/web/admin_api/controllers/report_controller_test.exs
Normal file
374
test/web/admin_api/controllers/report_controller_test.exs
Normal file
|
@ -0,0 +1,374 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.ReportNote
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
setup do
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
token = insert(:oauth_admin_token, user: admin)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|
||||||
|
{:ok, %{admin: admin, token: token, conn: conn}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/reports/:id" do
|
||||||
|
test "returns report by its id", %{conn: conn} do
|
||||||
|
[reporter, target_user] = insert_pair(:user)
|
||||||
|
activity = insert(:note_activity, user: target_user)
|
||||||
|
|
||||||
|
{:ok, %{id: report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I feel offended",
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/reports/#{report_id}")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert response["id"] == report_id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 404 when report id is invalid", %{conn: conn} do
|
||||||
|
conn = get(conn, "/api/pleroma/admin/reports/test")
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PATCH /api/pleroma/admin/reports" do
|
||||||
|
setup do
|
||||||
|
[reporter, target_user] = insert_pair(:user)
|
||||||
|
activity = insert(:note_activity, user: target_user)
|
||||||
|
|
||||||
|
{:ok, %{id: report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I feel offended",
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, %{id: second_report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I feel very offended",
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
%{
|
||||||
|
id: report_id,
|
||||||
|
second_report_id: second_report_id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
|
||||||
|
read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
|
||||||
|
write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:token, read_token)
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [%{"state" => "resolved", "id" => id}]
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(403)
|
||||||
|
|
||||||
|
assert response == %{
|
||||||
|
"error" => "Insufficient permissions: admin:write:reports."
|
||||||
|
}
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> assign(:token, write_token)
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [%{"state" => "resolved", "id" => id}]
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:no_content)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [
|
||||||
|
%{"state" => "resolved", "id" => id}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:no_content)
|
||||||
|
|
||||||
|
activity = Activity.get_by_id(id)
|
||||||
|
assert activity.data["state"] == "resolved"
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} updated report ##{id} with 'resolved' state"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "closes report", %{conn: conn, id: id, admin: admin} do
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [
|
||||||
|
%{"state" => "closed", "id" => id}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:no_content)
|
||||||
|
|
||||||
|
activity = Activity.get_by_id(id)
|
||||||
|
assert activity.data["state"] == "closed"
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} updated report ##{id} with 'closed' state"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 400 when state is unknown", %{conn: conn, id: id} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [
|
||||||
|
%{"state" => "test", "id" => id}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert "Unsupported state" =
|
||||||
|
hd(json_response_and_validate_schema(conn, :bad_request))["error"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 404 when report is not exist", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [
|
||||||
|
%{"state" => "closed", "id" => "test"}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert hd(json_response_and_validate_schema(conn, :bad_request))["error"] == "not_found"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates state of multiple reports", %{
|
||||||
|
conn: conn,
|
||||||
|
id: id,
|
||||||
|
admin: admin,
|
||||||
|
second_report_id: second_report_id
|
||||||
|
} do
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/pleroma/admin/reports", %{
|
||||||
|
"reports" => [
|
||||||
|
%{"state" => "resolved", "id" => id},
|
||||||
|
%{"state" => "closed", "id" => second_report_id}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:no_content)
|
||||||
|
|
||||||
|
activity = Activity.get_by_id(id)
|
||||||
|
second_activity = Activity.get_by_id(second_report_id)
|
||||||
|
assert activity.data["state"] == "resolved"
|
||||||
|
assert second_activity.data["state"] == "closed"
|
||||||
|
|
||||||
|
[first_log_entry, second_log_entry] = Repo.all(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(first_log_entry) ==
|
||||||
|
"@#{admin.nickname} updated report ##{id} with 'resolved' state"
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(second_log_entry) ==
|
||||||
|
"@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/reports" do
|
||||||
|
test "returns empty response when no reports created", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/reports")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert Enum.empty?(response["reports"])
|
||||||
|
assert response["total"] == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns reports", %{conn: conn} do
|
||||||
|
[reporter, target_user] = insert_pair(:user)
|
||||||
|
activity = insert(:note_activity, user: target_user)
|
||||||
|
|
||||||
|
{:ok, %{id: report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I feel offended",
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/reports")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
[report] = response["reports"]
|
||||||
|
|
||||||
|
assert length(response["reports"]) == 1
|
||||||
|
assert report["id"] == report_id
|
||||||
|
|
||||||
|
assert response["total"] == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns reports with specified state", %{conn: conn} do
|
||||||
|
[reporter, target_user] = insert_pair(:user)
|
||||||
|
activity = insert(:note_activity, user: target_user)
|
||||||
|
|
||||||
|
{:ok, %{id: first_report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I feel offended",
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, %{id: second_report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I don't like this user"
|
||||||
|
})
|
||||||
|
|
||||||
|
CommonAPI.update_report_state(second_report_id, "closed")
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/reports?state=open")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert [open_report] = response["reports"]
|
||||||
|
|
||||||
|
assert length(response["reports"]) == 1
|
||||||
|
assert open_report["id"] == first_report_id
|
||||||
|
|
||||||
|
assert response["total"] == 1
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/reports?state=closed")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert [closed_report] = response["reports"]
|
||||||
|
|
||||||
|
assert length(response["reports"]) == 1
|
||||||
|
assert closed_report["id"] == second_report_id
|
||||||
|
|
||||||
|
assert response["total"] == 1
|
||||||
|
|
||||||
|
assert %{"total" => 0, "reports" => []} ==
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/reports?state=resolved", %{
|
||||||
|
"" => ""
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 403 when requested by a non-admin" do
|
||||||
|
user = insert(:user)
|
||||||
|
token = insert(:oauth_token, user: user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> get("/api/pleroma/admin/reports")
|
||||||
|
|
||||||
|
assert json_response(conn, :forbidden) ==
|
||||||
|
%{"error" => "User is not an admin or OAuth admin scope is not granted."}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 403 when requested by anonymous" do
|
||||||
|
conn = get(build_conn(), "/api/pleroma/admin/reports")
|
||||||
|
|
||||||
|
assert json_response(conn, :forbidden) == %{
|
||||||
|
"error" => "Invalid credentials."
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/reports/:id/notes" do
|
||||||
|
setup %{conn: conn, admin: admin} do
|
||||||
|
[reporter, target_user] = insert_pair(:user)
|
||||||
|
activity = insert(:note_activity, user: target_user)
|
||||||
|
|
||||||
|
{:ok, %{id: report_id}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "I feel offended",
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
|
||||||
|
content: "this is disgusting!"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
|
||||||
|
content: "this is disgusting2!"
|
||||||
|
})
|
||||||
|
|
||||||
|
%{
|
||||||
|
admin_id: admin.id,
|
||||||
|
report_id: report_id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
|
||||||
|
assert [note, _] = Repo.all(ReportNote)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
activity_id: ^report_id,
|
||||||
|
content: "this is disgusting!",
|
||||||
|
user_id: ^admin_id
|
||||||
|
} = note
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns reports with notes", %{conn: conn, admin: admin} do
|
||||||
|
conn = get(conn, "/api/pleroma/admin/reports")
|
||||||
|
|
||||||
|
response = json_response_and_validate_schema(conn, 200)
|
||||||
|
notes = hd(response["reports"])["notes"]
|
||||||
|
[note, _] = notes
|
||||||
|
|
||||||
|
assert note["user"]["nickname"] == admin.nickname
|
||||||
|
assert note["content"] == "this is disgusting!"
|
||||||
|
assert note["created_at"]
|
||||||
|
assert response["total"] == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it deletes the note", %{conn: conn, report_id: report_id} do
|
||||||
|
assert ReportNote |> Repo.all() |> length() == 2
|
||||||
|
assert [note, _] = Repo.all(ReportNote)
|
||||||
|
|
||||||
|
delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
|
||||||
|
|
||||||
|
assert ReportNote |> Repo.all() |> length() == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -42,6 +42,14 @@ test "shows activity", %{conn: conn} do
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert response["id"] == activity.id
|
assert response["id"] == activity.id
|
||||||
|
|
||||||
|
account = response["account"]
|
||||||
|
actor = User.get_by_ap_id(activity.actor)
|
||||||
|
|
||||||
|
assert account["id"] == actor.id
|
||||||
|
assert account["nickname"] == actor.nickname
|
||||||
|
assert account["deactivated"] == actor.deactivated
|
||||||
|
assert account["confirmation_pending"] == actor.confirmation_pending
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||||
|
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Mock
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
setup do: clear_config([:instance, :max_account_fields])
|
setup do: clear_config([:instance, :max_account_fields])
|
||||||
|
@ -52,6 +53,10 @@ test "sets user settings in a generic way", %{conn: conn} do
|
||||||
|
|
||||||
user = Repo.get(User, user_data["id"])
|
user = Repo.get(User, user_data["id"])
|
||||||
|
|
||||||
|
clear_config([:instance, :federating], true)
|
||||||
|
|
||||||
|
with_mock Pleroma.Web.Federator,
|
||||||
|
publish: fn _activity -> :ok end do
|
||||||
res_conn =
|
res_conn =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|
@ -70,6 +75,9 @@ test "sets user settings in a generic way", %{conn: conn} do
|
||||||
"pleroma_fe" => %{"theme" => "bla"},
|
"pleroma_fe" => %{"theme" => "bla"},
|
||||||
"masto_fe" => %{"theme" => "blub"}
|
"masto_fe" => %{"theme" => "blub"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_called(Pleroma.Web.Federator.publish(:_))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "updates the user's bio", %{conn: conn} do
|
test "updates the user's bio", %{conn: conn} do
|
||||||
|
|
|
@ -71,10 +71,48 @@ test "search", %{conn: conn} do
|
||||||
get(conn, "/api/v2/search?q=天子")
|
get(conn, "/api/v2/search?q=天子")
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert results["hashtags"] == [
|
||||||
|
%{"name" => "天子", "url" => "#{Web.base_url()}/tag/天子"}
|
||||||
|
]
|
||||||
|
|
||||||
[status] = results["statuses"]
|
[status] = results["statuses"]
|
||||||
assert status["id"] == to_string(activity.id)
|
assert status["id"] == to_string(activity.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "constructs hashtags from search query", %{conn: conn} do
|
||||||
|
results =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/search?#{URI.encode_query(%{q: "some text with #explicit #hashtags"})}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert results["hashtags"] == [
|
||||||
|
%{"name" => "explicit", "url" => "#{Web.base_url()}/tag/explicit"},
|
||||||
|
%{"name" => "hashtags", "url" => "#{Web.base_url()}/tag/hashtags"}
|
||||||
|
]
|
||||||
|
|
||||||
|
results =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/search?#{URI.encode_query(%{q: "john doe JOHN DOE"})}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert results["hashtags"] == [
|
||||||
|
%{"name" => "john", "url" => "#{Web.base_url()}/tag/john"},
|
||||||
|
%{"name" => "doe", "url" => "#{Web.base_url()}/tag/doe"},
|
||||||
|
%{"name" => "JohnDoe", "url" => "#{Web.base_url()}/tag/JohnDoe"}
|
||||||
|
]
|
||||||
|
|
||||||
|
results =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/search?#{URI.encode_query(%{q: "accident-prone"})}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert results["hashtags"] == [
|
||||||
|
%{"name" => "accident", "url" => "#{Web.base_url()}/tag/accident"},
|
||||||
|
%{"name" => "prone", "url" => "#{Web.base_url()}/tag/prone"},
|
||||||
|
%{"name" => "AccidentProne", "url" => "#{Web.base_url()}/tag/AccidentProne"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
test "excludes a blocked users from search results", %{conn: conn} do
|
test "excludes a blocked users from search results", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"})
|
user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"})
|
||||||
|
@ -179,7 +217,7 @@ test "search", %{conn: conn} do
|
||||||
[account | _] = results["accounts"]
|
[account | _] = results["accounts"]
|
||||||
assert account["id"] == to_string(user_three.id)
|
assert account["id"] == to_string(user_three.id)
|
||||||
|
|
||||||
assert results["hashtags"] == []
|
assert results["hashtags"] == ["2hu"]
|
||||||
|
|
||||||
[status] = results["statuses"]
|
[status] = results["statuses"]
|
||||||
assert status["id"] == to_string(activity.id)
|
assert status["id"] == to_string(activity.id)
|
||||||
|
|
|
@ -60,9 +60,9 @@ test "the home timeline when the direct messages are excluded", %{user: user, co
|
||||||
describe "public" do
|
describe "public" do
|
||||||
@tag capture_log: true
|
@tag capture_log: true
|
||||||
test "the public timeline", %{conn: conn} do
|
test "the public timeline", %{conn: conn} do
|
||||||
following = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
{:ok, _activity} = CommonAPI.post(following, %{status: "test"})
|
{:ok, activity} = CommonAPI.post(user, %{status: "test"})
|
||||||
|
|
||||||
_activity = insert(:note_activity, local: false)
|
_activity = insert(:note_activity, local: false)
|
||||||
|
|
||||||
|
@ -77,6 +77,13 @@ test "the public timeline", %{conn: conn} do
|
||||||
conn = get(build_conn(), "/api/v1/timelines/public?local=1")
|
conn = get(build_conn(), "/api/v1/timelines/public?local=1")
|
||||||
|
|
||||||
assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
|
assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
|
||||||
|
|
||||||
|
# does not contain repeats
|
||||||
|
{:ok, _} = CommonAPI.repeat(activity.id, user)
|
||||||
|
|
||||||
|
conn = get(build_conn(), "/api/v1/timelines/public?local=true")
|
||||||
|
|
||||||
|
assert [_] = json_response_and_validate_schema(conn, :ok)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "the public timeline includes only public statuses for an authenticated user" do
|
test "the public timeline includes only public statuses for an authenticated user" do
|
||||||
|
@ -90,6 +97,49 @@ test "the public timeline includes only public statuses for an authenticated use
|
||||||
res_conn = get(conn, "/api/v1/timelines/public")
|
res_conn = get(conn, "/api/v1/timelines/public")
|
||||||
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
|
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "doesn't return replies if follower is posting with blocked user" do
|
||||||
|
%{conn: conn, user: blocker} = oauth_access(["read:statuses"])
|
||||||
|
[blockee, friend] = insert_list(2, :user)
|
||||||
|
{:ok, blocker} = User.follow(blocker, friend)
|
||||||
|
{:ok, _} = User.block(blocker, blockee)
|
||||||
|
|
||||||
|
conn = assign(conn, :user, blocker)
|
||||||
|
|
||||||
|
{:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"})
|
||||||
|
|
||||||
|
{:ok, reply_from_blockee} =
|
||||||
|
CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity})
|
||||||
|
|
||||||
|
{:ok, _reply_from_friend} =
|
||||||
|
CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee})
|
||||||
|
|
||||||
|
res_conn = get(conn, "/api/v1/timelines/public")
|
||||||
|
[%{"id" => ^activity_id}] = json_response_and_validate_schema(res_conn, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't return replies if follow is posting with users from blocked domain" do
|
||||||
|
%{conn: conn, user: blocker} = oauth_access(["read:statuses"])
|
||||||
|
friend = insert(:user)
|
||||||
|
blockee = insert(:user, ap_id: "https://example.com/users/blocked")
|
||||||
|
{:ok, blocker} = User.follow(blocker, friend)
|
||||||
|
{:ok, blocker} = User.block_domain(blocker, "example.com")
|
||||||
|
|
||||||
|
conn = assign(conn, :user, blocker)
|
||||||
|
|
||||||
|
{:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"})
|
||||||
|
|
||||||
|
{:ok, reply_from_blockee} =
|
||||||
|
CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity})
|
||||||
|
|
||||||
|
{:ok, _reply_from_friend} =
|
||||||
|
CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee})
|
||||||
|
|
||||||
|
res_conn = get(conn, "/api/v1/timelines/public")
|
||||||
|
|
||||||
|
activities = json_response_and_validate_schema(res_conn, 200)
|
||||||
|
[%{"id" => ^activity_id}] = activities
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp local_and_remote_activities do
|
defp local_and_remote_activities do
|
||||||
|
|
Loading…
Reference in a new issue