Merge branch 'develop' into global-status-expiration

This commit is contained in:
Egor Kislitsyn 2020-06-08 17:21:47 +04:00
commit da22119c2f
No known key found for this signature in database
GPG key ID: 1B49CB15B71E7805
82 changed files with 5293 additions and 3565 deletions

View file

@ -22,8 +22,21 @@ defmodule Pleroma.LoadTesting.Activities do
@max_concurrency 10
@visibility ~w(public private direct unlisted)
@types ~w(simple emoji mentions hell_thread attachment tag like reblog simple_thread remote)
@groups ~w(user friends non_friends)
@types [
: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
def generate(user, opts \\ []) do
@ -34,33 +47,24 @@ defmodule Pleroma.LoadTesting.Activities do
opts = Keyword.merge(@defaults, opts)
friends =
user
|> Users.get_users(limit: opts[:friends_used], local: :local, friends?: true)
|> Enum.shuffle()
users = Users.prepare_users(user, opts)
non_friends =
user
|> Users.get_users(limit: opts[:non_friends_used], local: :local, friends?: false)
|> Enum.shuffle()
{:ok, _} = Agent.start_link(fn -> users[:non_friends_remote] end, name: :non_friends_remote)
task_data =
for visibility <- @visibility,
type <- @types,
group <- @groups,
group <- [:user | @groups],
do: {visibility, type, group}
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 ->
generate_long_thread("public", user, friends_thread, non_friends_thread, opts)
generate_long_thread("public", users, opts)
end
private_long_thread = fn ->
generate_long_thread("private", user, friends_thread, non_friends_thread, opts)
generate_long_thread("private", users, opts)
end
iterations = opts[:iterations]
@ -73,10 +77,10 @@ defmodule Pleroma.LoadTesting.Activities do
i when i == iterations - 2 ->
spawn(public_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)
@ -127,16 +131,16 @@ defmodule Pleroma.LoadTesting.Activities do
end)
end
defp generate_long_thread(visibility, user, friends, non_friends, _opts) do
defp generate_long_thread(visibility, users, _opts) do
group =
if visibility == "public",
do: "friends",
else: "user"
do: :friends_local,
else: :user
tasks = get_reply_tasks(visibility, group) |> Stream.cycle() |> Enum.take(50)
{:ok, activity} =
CommonAPI.post(user, %{
CommonAPI.post(users[:user], %{
status: "Start of #{visibility} long thread",
visibility: visibility
})
@ -150,31 +154,28 @@ defmodule Pleroma.LoadTesting.Activities do
Map.put(state, key, activity)
end)
acc = {activity.id, ["@" <> user.nickname, "reply to long thread"]}
insert_replies_for_long_thread(tasks, visibility, user, friends, non_friends, acc)
acc = {activity.id, ["@" <> users[:user].nickname, "reply to long thread"]}
insert_replies_for_long_thread(tasks, visibility, users, acc)
IO.puts("Generating #{visibility} long thread ended\n")
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
"friend", {id, data} ->
friend = Enum.random(friends)
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} ->
:user, {id, data} ->
user = users[:user]
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
defp generate_activities(user, friends, non_friends, task_data, opts) do
defp generate_activities(users, task_data, opts) do
Task.async_stream(
task_data,
fn {visibility, type, group} ->
insert_activity(type, visibility, group, user, friends, non_friends, opts)
insert_activity(type, visibility, group, users, opts)
end,
max_concurrency: @max_concurrency,
timeout: 30_000
@ -182,67 +183,104 @@ defmodule Pleroma.LoadTesting.Activities do
|> Stream.run()
end
defp insert_activity("simple", visibility, group, user, friends, non_friends, _opts) do
{:ok, _activity} =
defp insert_local_activity(visibility, group, users, status) do
{:ok, _} =
group
|> get_actor(user, friends, non_friends)
|> CommonAPI.post(%{status: "Simple status", visibility: visibility})
|> get_actor(users)
|> CommonAPI.post(%{status: status, visibility: visibility})
end
defp insert_activity("emoji", visibility, group, user, friends, non_friends, _opts) do
{:ok, _activity} =
group
|> get_actor(user, friends, non_friends)
|> CommonAPI.post(%{
status: "Simple status with emoji :firefox:",
visibility: visibility
})
defp insert_remote_activity(visibility, group, users, status) do
actor = get_actor(group, users)
{act_data, obj_data} = prepare_activity_data(actor, visibility, users[:user])
{activity_data, object_data} = other_data(actor, status)
activity_data
|> Map.merge(act_data)
|> Map.put("object", Map.merge(object_data, obj_data))
|> Pleroma.Web.ActivityPub.ActivityPub.insert(false)
end
defp insert_activity("mentions", visibility, group, user, friends, non_friends, _opts) do
defp user_mentions(users) do
user_mentions =
get_random_mentions(friends, Enum.random(0..3)) ++
get_random_mentions(non_friends, Enum.random(0..3))
Enum.reduce(
@groups,
[],
fn group, acc ->
acc ++ get_random_mentions(users[group], Enum.random(0..2))
end
)
user_mentions =
if Enum.random([true, false]),
do: ["@" <> user.nickname | 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
})
if Enum.random([true, false]),
do: ["@" <> users[:user].nickname | user_mentions],
else: user_mentions
end
defp insert_activity("hell_thread", visibility, group, user, friends, non_friends, _opts) do
mentions =
with {:ok, nil} <- Cachex.get(:user_cache, "hell_thread_mentions") do
cached =
([user | Enum.take(friends, 10)] ++ Enum.take(non_friends, 10))
|> Enum.map(&"@#{&1.nickname}")
|> Enum.join(", ")
defp hell_thread_mentions(users) do
with {:ok, nil} <- Cachex.get(:user_cache, "hell_thread_mentions") do
cached =
@groups
|> Enum.reduce([users[:user]], fn group, acc ->
acc ++ Enum.take(users[group], 5)
end)
|> Enum.map(&"@#{&1.nickname}")
|> Enum.join(", ")
Cachex.put(:user_cache, "hell_thread_mentions", cached)
cached
else
{:ok, cached} -> cached
end
{:ok, _activity} =
group
|> get_actor(user, friends, non_friends)
|> CommonAPI.post(%{
status: mentions <> " hell thread status",
visibility: visibility
})
Cachex.put(:user_cache, "hell_thread_mentions", cached)
cached
else
{:ok, cached} -> cached
end
end
defp insert_activity("attachment", visibility, group, user, friends, non_friends, _opts) do
actor = get_actor(group, user, friends, non_friends)
defp insert_activity(:simple, visibility, group, users, _opts)
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 = %{
"actor" => actor.ap_id,
@ -268,67 +306,54 @@ defmodule Pleroma.LoadTesting.Activities do
})
end
defp insert_activity("tag", visibility, group, user, friends, non_friends, _opts) do
{:ok, _activity} =
group
|> get_actor(user, friends, non_friends)
|> CommonAPI.post(%{status: "Status with #tag", visibility: visibility})
defp insert_activity(:tag, visibility, group, users, _opts) do
insert_local_activity(visibility, group, users, "Status with #tag")
end
defp insert_activity("like", visibility, group, user, friends, non_friends, opts) do
actor = get_actor(group, user, friends, non_friends)
defp insert_activity(:like, visibility, group, users, opts) do
actor = get_actor(group, users)
with activity_id when not is_nil(activity_id) <- get_random_create_activity_id(),
{:ok, _activity} <- CommonAPI.favorite(actor, activity_id) do
:ok
else
{:error, _} ->
insert_activity("like", visibility, group, user, friends, non_friends, opts)
insert_activity(:like, visibility, group, users, opts)
nil ->
Process.sleep(15)
insert_activity("like", visibility, group, user, friends, non_friends, opts)
insert_activity(:like, visibility, group, users, opts)
end
end
defp insert_activity("reblog", visibility, group, user, friends, non_friends, opts) do
actor = get_actor(group, user, friends, non_friends)
defp insert_activity(:reblog, visibility, group, users, opts) do
actor = get_actor(group, users)
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
else
{:error, _} ->
insert_activity("reblog", visibility, group, user, friends, non_friends, opts)
insert_activity(:reblog, visibility, group, users, opts)
nil ->
Process.sleep(15)
insert_activity("reblog", visibility, group, user, friends, non_friends, opts)
insert_activity(:reblog, visibility, group, users, opts)
end
end
defp insert_activity("simple_thread", visibility, group, user, friends, non_friends, _opts)
when visibility in ["public", "unlisted", "private"] do
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)
defp insert_activity(:simple_thread, "direct", group, users, _opts) do
actor = get_actor(group, users)
tasks = get_reply_tasks("direct", group)
list =
case group do
"non_friends" ->
Enum.take(non_friends, 3)
:user ->
group = Enum.random(@friends_groups)
Enum.take(users[group], 3)
_ ->
Enum.take(friends, 3)
Enum.take(users[group], 3)
end
data = Enum.map(list, &("@" <> &1.nickname))
@ -339,40 +364,30 @@ defmodule Pleroma.LoadTesting.Activities do
visibility: "direct"
})
acc = {activity.id, ["@" <> user.nickname | data] ++ ["reply to status"]}
insert_direct_replies(tasks, user, list, acc)
acc = {activity.id, ["@" <> users[:user].nickname | data] ++ ["reply to status"]}
insert_direct_replies(tasks, users[:user], list, acc)
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
remote_friends =
Users.get_users(user, limit: opts[:friends_used], local: :external, friends?: true)
{:ok, activity} =
CommonAPI.post(users[:user], %{status: "Simple status", visibility: visibility})
remote_non_friends =
Users.get_users(user, limit: opts[:non_friends_used], local: :external, friends?: false)
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)
acc = {activity.id, ["@" <> actor.nickname, "reply to status"]}
insert_replies(tasks, visibility, users, acc)
end
defp get_actor("user", user, _friends, _non_friends), do: user
defp get_actor("friends", _user, friends, _non_friends), do: Enum.random(friends)
defp get_actor("non_friends", _user, _friends, non_friends), do: Enum.random(non_friends)
defp get_actor(:user, %{user: user}), do: user
defp get_actor(group, users), do: Enum.random(users[group])
defp other_data(actor) do
defp other_data(actor, content) do
%{host: host} = URI.parse(actor.ap_id)
datetime = DateTime.utc_now()
context_id = "http://#{host}:4000/contexts/#{UUID.generate()}"
activity_id = "http://#{host}:4000/activities/#{UUID.generate()}"
object_id = "http://#{host}:4000/objects/#{UUID.generate()}"
context_id = "https://#{host}/contexts/#{UUID.generate()}"
activity_id = "https://#{host}/activities/#{UUID.generate()}"
object_id = "https://#{host}/objects/#{UUID.generate()}"
activity_data = %{
"actor" => actor.ap_id,
@ -389,7 +404,7 @@ defmodule Pleroma.LoadTesting.Activities do
"attributedTo" => actor.ap_id,
"bcc" => [],
"bto" => [],
"content" => "Remote post",
"content" => content,
"context" => context_id,
"conversation" => context_id,
"emoji" => %{},
@ -475,51 +490,65 @@ defmodule Pleroma.LoadTesting.Activities do
{act_data, obj_data}
end
defp get_reply_tasks("public", "user"), do: ~w(friend non_friend user)
defp get_reply_tasks("public", "friends"), do: ~w(non_friend user friend)
defp get_reply_tasks("public", "non_friends"), do: ~w(user friend non_friend)
defp get_reply_tasks("public", :user) do
[:friends_local, :friends_remote, :non_friends_local, :non_friends_remote, :user]
end
defp get_reply_tasks(visibility, "user") when visibility in ["unlisted", "private"],
do: ~w(friend user friend)
defp get_reply_tasks("public", group) when group in @friends_groups do
[:non_friends_local, :non_friends_remote, :user, :friends_local, :friends_remote]
end
defp get_reply_tasks(visibility, "friends") when visibility in ["unlisted", "private"],
do: ~w(user friend user)
defp get_reply_tasks("public", group) when group in @non_friends_groups do
[:user, :friends_local, :friends_remote, :non_friends_local, :non_friends_remote]
end
defp get_reply_tasks(visibility, "non_friends") when visibility in ["unlisted", "private"],
do: []
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("direct", "user"), do: ~w(friend user friend)
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 get_reply_tasks(visibility, group)
when visibility in ["unlisted", "private"] and group in @friends_groups do
[:user, :friends_remote, :friends_local, :user]
end
defp insert_replies(tasks, visibility, user, friends, non_friends, acc) do
defp get_reply_tasks(visibility, group)
when visibility in ["unlisted", "private"] and
group in @non_friends_groups,
do: []
defp get_reply_tasks("direct", :user), do: [:friends_local, :user, :friends_remote]
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
"friend", {id, data} ->
friend = Enum.random(friends)
insert_reply(friend, data, id, visibility)
:user, {id, data} ->
insert_reply(users[:user], data, id, visibility)
"non_friend", {id, data} ->
non_friend = Enum.random(non_friends)
insert_reply(non_friend, data, id, visibility)
"user", {id, data} ->
insert_reply(user, data, id, visibility)
group, {id, data} ->
replier = Enum.random(users[group])
insert_reply(replier, data, id, visibility)
end)
end
defp insert_direct_replies(tasks, user, list, acc) do
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)
{reply_id, _} =
insert_reply(actor, List.delete(data, "@" <> actor.nickname), id, "direct")
{reply_id, data}
"user", {id, data} ->
{reply_id, _} = insert_reply(user, List.delete(data, "@" <> user.nickname), id, "direct")
{reply_id, data}
end)
end

View file

@ -36,6 +36,7 @@ defmodule Pleroma.LoadTesting.Fetcher do
fetch_home_timeline(user)
fetch_direct_timeline(user)
fetch_public_timeline(user)
fetch_public_timeline(user, :with_blocks)
fetch_public_timeline(user, :local)
fetch_public_timeline(user, :tag)
fetch_notifications(user)
@ -51,12 +52,12 @@ defmodule Pleroma.LoadTesting.Fetcher do
defp opts_for_home_timeline(user) do
%{
"blocking_user" => user,
"count" => "20",
"muting_user" => user,
"type" => ["Create", "Announce"],
"user" => user,
"with_muted" => "true"
blocking_user: user,
count: "20",
muting_user: user,
type: ["Create", "Announce"],
user: user,
with_muted: true
}
end
@ -69,17 +70,17 @@ defmodule Pleroma.LoadTesting.Fetcher do
ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse() |> List.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()
|> List.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()
|> List.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()
|> List.last()
@ -89,19 +90,19 @@ defmodule Pleroma.LoadTesting.Fetcher do
},
inputs: %{
"1 page" => opts,
"2 page" => Map.put(opts, "max_id", first_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),
"5 page" => Map.put(opts, "max_id", forth_page_last.id),
"1 page only media" => Map.put(opts, "only_media", "true"),
"2 page" => Map.put(opts, :max_id, first_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),
"5 page" => Map.put(opts, :max_id, forth_page_last.id),
"1 page only media" => Map.put(opts, :only_media, true),
"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" =>
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" =>
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" =>
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()
)
@ -109,12 +110,12 @@ defmodule Pleroma.LoadTesting.Fetcher do
defp opts_for_direct_timeline(user) do
%{
:visibility => "direct",
"blocking_user" => user,
"count" => "20",
"type" => "Create",
"user" => user,
"with_muted" => "true"
visibility: "direct",
blocking_user: user,
count: "20",
type: "Create",
user: user,
with_muted: true
}
end
@ -129,7 +130,7 @@ defmodule Pleroma.LoadTesting.Fetcher do
|> Pagination.fetch_paginated(opts)
|> 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 =
recipients
@ -137,7 +138,7 @@ defmodule Pleroma.LoadTesting.Fetcher do
|> Pagination.fetch_paginated(opts2)
|> 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 =
recipients
@ -145,7 +146,7 @@ defmodule Pleroma.LoadTesting.Fetcher do
|> Pagination.fetch_paginated(opts3)
|> 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 =
recipients
@ -164,7 +165,7 @@ defmodule Pleroma.LoadTesting.Fetcher do
"2 page" => opts2,
"3 page" => opts3,
"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()
)
@ -172,34 +173,34 @@ defmodule Pleroma.LoadTesting.Fetcher do
defp opts_for_public_timeline(user) do
%{
"type" => ["Create", "Announce"],
"local_only" => false,
"blocking_user" => user,
"muting_user" => user
type: ["Create", "Announce"],
local_only: false,
blocking_user: user,
muting_user: user
}
end
defp opts_for_public_timeline(user, :local) do
%{
"type" => ["Create", "Announce"],
"local_only" => true,
"blocking_user" => user,
"muting_user" => user
type: ["Create", "Announce"],
local_only: true,
blocking_user: user,
muting_user: user
}
end
defp opts_for_public_timeline(user, :tag) do
%{
"blocking_user" => user,
"count" => "20",
"local_only" => nil,
"muting_user" => user,
"tag" => ["tag"],
"tag_all" => [],
"tag_reject" => [],
"type" => "Create",
"user" => user,
"with_muted" => "true"
blocking_user: user,
count: "20",
local_only: nil,
muting_user: user,
tag: ["tag"],
tag_all: [],
tag_reject: [],
type: "Create",
user: user,
with_muted: true
}
end
@ -222,24 +223,72 @@ defmodule Pleroma.LoadTesting.Fetcher do
end
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")
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
first_page_last = ActivityPub.fetch_public_activities(opts) |> List.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()
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()
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()
Benchee.run(
@ -250,17 +299,17 @@ defmodule Pleroma.LoadTesting.Fetcher do
},
inputs: %{
"1 page" => opts,
"2 page" => Map.put(opts, "max_id", first_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),
"5 page" => Map.put(opts, "max_id", forth_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),
"4 page" => Map.put(opts, :max_id, third_page_last.id),
"5 page" => Map.put(opts, :max_id, forth_page_last.id)
},
formatters: formatters()
)
end
defp opts_for_notifications do
%{"count" => "20", "with_muted" => "true"}
%{count: "20", with_muted: true}
end
defp fetch_notifications(user) do
@ -269,15 +318,15 @@ defmodule Pleroma.LoadTesting.Fetcher do
first_page_last = MastodonAPI.get_notifications(user, opts) |> List.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()
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()
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()
Benchee.run(
@ -288,10 +337,10 @@ defmodule Pleroma.LoadTesting.Fetcher do
},
inputs: %{
"1 page" => opts,
"2 page" => Map.put(opts, "max_id", first_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),
"5 page" => Map.put(opts, "max_id", forth_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),
"4 page" => Map.put(opts, :max_id, third_page_last.id),
"5 page" => Map.put(opts, :max_id, forth_page_last.id)
},
formatters: formatters()
)
@ -301,13 +350,13 @@ defmodule Pleroma.LoadTesting.Fetcher do
first_page_last = ActivityPub.fetch_favourites(user) |> List.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 =
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 =
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(
%{
@ -317,10 +366,10 @@ defmodule Pleroma.LoadTesting.Fetcher do
},
inputs: %{
"1 page" => %{},
"2 page" => %{"max_id" => first_page_last.id},
"3 page" => %{"max_id" => second_page_last.id},
"4 page" => %{"max_id" => third_page_last.id},
"5 page" => %{"max_id" => forth_page_last.id}
"2 page" => %{:max_id => first_page_last.id},
"3 page" => %{:max_id => second_page_last.id},
"4 page" => %{:max_id => third_page_last.id},
"5 page" => %{:max_id => forth_page_last.id}
},
formatters: formatters()
)
@ -328,8 +377,8 @@ defmodule Pleroma.LoadTesting.Fetcher do
defp opts_for_long_thread(user) do
%{
"blocking_user" => user,
"user" => user
blocking_user: user,
user: user
}
end
@ -339,9 +388,9 @@ defmodule Pleroma.LoadTesting.Fetcher do
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(
%{
@ -461,13 +510,13 @@ defmodule Pleroma.LoadTesting.Fetcher do
public_context =
ActivityPub.fetch_activities_for_context(
public.data["context"],
Map.put(fetch_opts, "exclude_id", public.id)
Map.put(fetch_opts, :exclude_id, public.id)
)
private_context =
ActivityPub.fetch_activities_for_context(
private.data["context"],
Map.put(fetch_opts, "exclude_id", private.id)
Map.put(fetch_opts, :exclude_id, private.id)
)
Benchee.run(
@ -498,14 +547,14 @@ defmodule Pleroma.LoadTesting.Fetcher do
end,
"Public timeline with reply filtering - following" => fn ->
public_params
|> Map.put("reply_visibility", "following")
|> Map.put("reply_filtering_user", user)
|> Map.put(:reply_visibility, "following")
|> Map.put(:reply_filtering_user, user)
|> ActivityPub.fetch_public_activities()
end,
"Public timeline with reply filtering - self" => fn ->
public_params
|> Map.put("reply_visibility", "self")
|> Map.put("reply_filtering_user", user)
|> Map.put(:reply_visibility, "self")
|> Map.put(:reply_filtering_user, user)
|> ActivityPub.fetch_public_activities()
end
},
@ -524,16 +573,16 @@ defmodule Pleroma.LoadTesting.Fetcher do
"Home timeline with reply filtering - following" => fn ->
private_params =
private_params
|> Map.put("reply_filtering_user", user)
|> Map.put("reply_visibility", "following")
|> Map.put(:reply_filtering_user, user)
|> Map.put(:reply_visibility, "following")
ActivityPub.fetch_activities(recipients, private_params)
end,
"Home timeline with reply filtering - self" => fn ->
private_params =
private_params
|> Map.put("reply_filtering_user", user)
|> Map.put("reply_visibility", "self")
|> Map.put(:reply_filtering_user, user)
|> Map.put(:reply_visibility, "self")
ActivityPub.fetch_activities(recipients, private_params)
end

View file

@ -27,7 +27,7 @@ defmodule Pleroma.LoadTesting.Users do
make_friends(main_user, opts[:friends])
Repo.get(User, main_user.id)
User.get_by_id(main_user.id)
end
def generate_users(max) do
@ -166,4 +166,24 @@ defmodule Pleroma.LoadTesting.Users do
)
|> Stream.run()
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

View file

@ -5,7 +5,6 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Tags do
import Ecto.Query
alias Pleroma.Repo
alias Pleroma.Web.MastodonAPI.TimelineController
def run(_args) do
Mix.Pleroma.start_pleroma()
@ -37,7 +36,7 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Tags do
Benchee.run(
%{
"Hashtag fetching, any" => fn tags ->
TimelineController.hashtag_fetching(
hashtag_fetching(
%{
"any" => tags
},
@ -47,7 +46,7 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Tags do
end,
# Will always return zero results because no overlapping hashtags are generated.
"Hashtag fetching, all" => fn tags ->
TimelineController.hashtag_fetching(
hashtag_fetching(
%{
"all" => tags
},
@ -67,7 +66,7 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Tags do
Benchee.run(
%{
"Hashtag fetching" => fn tag ->
TimelineController.hashtag_fetching(
hashtag_fetching(
%{
"tag" => tag
},
@ -80,4 +79,35 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Tags do
time: 5
)
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

View file

@ -171,7 +171,8 @@ config :mime, :types, %{
"application/ld+json" => ["activity+json"]
}
config :tesla, adapter: Tesla.Adapter.Gun
config :tesla, adapter: Tesla.Adapter.Hackney
# Configures http settings, upstream proxy etc.
config :pleroma, :http,
proxy_url: nil,
@ -183,7 +184,7 @@ config :pleroma, :instance,
name: "Pleroma",
email: "example@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",
limit: 5_000,
chat_limit: 5_000,

View file

@ -547,7 +547,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
```json
{
"totalReports" : 1,
"total" : 1,
"reports": [
{
"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
- 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

View file

@ -42,6 +42,12 @@ Feel free to contact us to be added to this list!
- Platforms: SailfishOS
- 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
- 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>

View file

@ -92,10 +92,10 @@ defmodule Pleroma.BBS.Handler do
params =
%{}
|> Map.put("type", ["Create"])
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)
|> Map.put(:type, ["Create"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
activities =
[user.ap_id | Pleroma.User.following(user)]

View file

@ -24,6 +24,6 @@ defmodule Pleroma.Constants do
const(static_only_files,
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

View file

@ -163,8 +163,8 @@ defmodule Pleroma.Conversation.Participation do
|> Enum.map(fn participation ->
activity_id =
ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{
"user" => user,
"blocking_user" => user
user: user,
blocking_user: user
})
%{

View file

@ -141,6 +141,12 @@ defmodule Pleroma.FollowingRelationship do
|> where([r], r.state == ^:follow_accept)
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
following =
following_query(user)

View file

@ -17,14 +17,6 @@ defmodule Pleroma.Helpers.UriHelper do
|> URI.to_string()
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: uri
end

15
lib/pleroma/maps.ex Normal file
View 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

View file

@ -23,12 +23,12 @@ defmodule Pleroma.Pagination do
@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, %{"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: total,
items: fetch_paginated(query, Map.drop(params, ["total"]), :keyset, table_binding)
items: fetch_paginated(query, Map.drop(params, [:total]), :keyset, table_binding)
}
end
@ -41,7 +41,7 @@ defmodule Pleroma.Pagination do
|> enforce_order(options)
end
def fetch_paginated(query, %{"total" => true} = params, :offset, table_binding) do
def fetch_paginated(query, %{total: true} = params, :offset, table_binding) do
total =
query
|> Ecto.Query.exclude(:left_join)
@ -49,7 +49,7 @@ defmodule Pleroma.Pagination do
%{
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
@ -90,12 +90,6 @@ defmodule Pleroma.Pagination do
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.changes
end

View file

@ -1489,6 +1489,8 @@ defmodule Pleroma.User do
delete_user_activities(user)
delete_outgoing_pending_follow_requests(user)
delete_or_deactivate(user)
end
@ -1611,6 +1613,12 @@ defmodule Pleroma.User do
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
Pleroma.HTML.Scrubber.TwitterText
end

View file

@ -45,7 +45,7 @@ defmodule Pleroma.User.Query do
is_admin: boolean(),
is_moderator: boolean(),
super_users: boolean(),
exclude_service_users: boolean(),
invisible: boolean(),
followers: User.t(),
friends: User.t(),
recipients_from_activity: [String.t()],
@ -89,8 +89,8 @@ defmodule Pleroma.User.Query do
where(query, [u], ilike(field(u, ^key), ^"%#{value}%"))
end
defp compose_query({:exclude_service_users, _}, query) do
where(query, [u], not like(u.ap_id, "%/relay") and not like(u.ap_id, "%/internal/fetch"))
defp compose_query({:invisible, bool}, query) when is_boolean(bool) do
where(query, [u], u.invisible == ^bool)
end
defp compose_query({key, value}, query)

View file

@ -10,6 +10,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.Constants
alias Pleroma.Conversation
alias Pleroma.Conversation.Participation
alias Pleroma.Maps
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Object.Containment
@ -20,7 +21,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.User
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.Streamer
alias Pleroma.Web.WebFinger
alias Pleroma.Workers.BackgroundWorker
@ -68,16 +68,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
{recipients, to, cc}
end
defp check_actor_is_active(actor) do
if not is_nil(actor) do
with user <- User.get_cached_by_ap_id(actor),
false <- user.deactivated do
true
else
_e -> false
end
else
true
defp check_actor_is_active(nil), do: true
defp check_actor_is_active(actor) when is_binary(actor) do
case User.get_cached_by_ap_id(actor) do
%User{deactivated: deactivated} -> not deactivated
_ -> false
end
end
@ -88,7 +84,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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}
end
@ -96,36 +92,26 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
end
def increase_replies_count_if_reply(%{
"object" => %{"inReplyTo" => reply_ap_id} = object,
"type" => "Create"
}) do
defp increase_replies_count_if_reply(%{
"object" => %{"inReplyTo" => reply_ap_id} = object,
"type" => "Create"
}) do
if is_public?(object) do
Object.increase_replies_count(reply_ap_id)
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{
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},
"type" => "Create",
"actor" => actor
}) do
defp increase_poll_votes_if_vote(%{
"object" => %{"inReplyTo" => reply_ap_id, "name" => name},
"type" => "Create",
"actor" => actor
}) do
Object.increase_vote_count(reply_ap_id, name, actor)
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()}
def persist(object, meta) do
@ -164,12 +150,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> maybe_create_activity_expiration()
# Splice in the child object if we have one.
activity =
if not is_nil(object) do
Map.put(activity, :object, object)
else
activity
end
activity = Maps.put_if_present(activity, :object, object)
BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
@ -214,8 +195,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp create_or_bump_conversation(activity, actor) do
with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
%User{} = user <- User.get_cached_by_ap_id(actor),
Participation.mark_as_read(user, conversation) do
%User{} = user <- User.get_cached_by_ap_id(actor) do
Participation.mark_as_read(user, conversation)
{:ok, conversation}
end
end
@ -237,13 +218,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
def stream_out_participations(%Object{data: %{"context" => context}}, user) do
with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
conversation = Repo.preload(conversation, :participations),
last_activity_id =
fetch_latest_activity_id_for_context(conversation.ap_id, %{
"user" => user,
"blocking_user" => user
}) do
with %Conversation{} = conversation <- Conversation.get_for_ap_id(context) do
conversation = Repo.preload(conversation, :participations)
last_activity_id =
fetch_latest_activity_id_for_context(conversation.ap_id, %{
user: user,
blocking_user: user
})
if last_activity_id do
stream_out_participations(conversation.participations)
end
@ -277,12 +260,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
published = params[:published]
quick_insert? = Config.get([:env]) == :benchmark
with create_data <-
make_create_data(
%{to: to, actor: actor, published: published, context: context, object: object},
additional
),
{:ok, activity} <- insert(create_data, local, fake),
create_data =
make_create_data(
%{to: to, actor: actor, published: published, context: context, object: object},
additional
)
with {:ok, activity} <- insert(create_data, local, fake),
{:fake, false, activity} <- {:fake, fake, activity},
_ <- increase_replies_count_if_reply(create_data),
_ <- increase_poll_votes_if_vote(create_data),
@ -310,12 +294,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
local = !(params[:local] == false)
published = params[:published]
with listen_data <-
make_listen_data(
%{to: to, actor: actor, published: published, context: context, object: object},
additional
),
{:ok, activity} <- insert(listen_data, local),
listen_data =
make_listen_data(
%{to: to, actor: actor, published: published, context: context, object: object},
additional
)
with {:ok, activity} <- insert(listen_data, local),
_ <- notify_and_stream(activity),
:ok <- maybe_federate(activity) do
{:ok, activity}
@ -333,14 +318,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
@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)
activity_id = Map.get(params, :activity_id, nil)
with data <-
%{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
|> Utils.maybe_put("id", activity_id),
{:ok, activity} <- insert(data, local),
data =
%{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
|> Maps.put_if_present("id", activity_id)
with {:ok, activity} <- insert(data, local),
_ <- notify_and_stream(activity),
:ok <- maybe_federate(activity) do
{:ok, activity}
@ -352,15 +338,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
local = !(params[:local] == false)
activity_id = params[:activity_id]
with data <- %{
"to" => to,
"cc" => cc,
"type" => "Update",
"actor" => actor,
"object" => object
},
data <- Utils.maybe_put(data, "id", activity_id),
{:ok, activity} <- insert(data, local),
data =
%{
"to" => to,
"cc" => cc,
"type" => "Update",
"actor" => actor,
"object" => object
}
|> Maps.put_if_present("id", activity_id)
with {:ok, activity} <- insert(data, local),
_ <- notify_and_stream(activity),
:ok <- maybe_federate(activity) do
{:ok, activity}
@ -377,8 +365,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
defp do_follow(follower, followed, activity_id, local) do
with data <- make_follow_data(follower, followed, activity_id),
{:ok, activity} <- insert(data, local),
data = make_follow_data(follower, followed, activity_id)
with {:ok, activity} <- insert(data, local),
_ <- notify_and_stream(activity),
:ok <- maybe_federate(activity) do
{:ok, activity}
@ -422,13 +411,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp do_block(blocker, blocked, activity_id, local) do
unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
if unfollow_blocked do
follow_activity = fetch_latest_follow(blocker, blocked)
if follow_activity, do: unfollow(blocker, blocked, nil, local)
if unfollow_blocked and fetch_latest_follow(blocker, blocked) do
unfollow(blocker, blocked, nil, local)
end
with block_data <- make_block_data(blocker, blocked, activity_id),
{:ok, activity} <- insert(block_data, local),
block_data = make_block_data(blocker, blocked, activity_id)
with {:ok, activity} <- insert(block_data, local),
_ <- notify_and_stream(activity),
:ok <- maybe_federate(activity) do
{:ok, activity}
@ -507,8 +496,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
public = [Constants.as_public()]
recipients =
if opts["user"],
do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
if opts[:user],
do: [opts[:user].ap_id | User.following(opts[:user])] ++ public,
else: public
from(activity in Activity)
@ -516,7 +505,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> maybe_preload_bookmarks(opts)
|> maybe_set_thread_muted_field(opts)
|> restrict_blocked(opts)
|> restrict_recipients(recipients, opts["user"])
|> restrict_recipients(recipients, opts[:user])
|> where(
[activity],
fragment(
@ -543,7 +532,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
FlakeId.Ecto.CompatType.t() | nil
def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
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)
|> select([a], a.id)
|> Repo.one()
@ -551,24 +540,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
@spec fetch_public_or_unlisted_activities(map(), Pagination.type()) :: [Activity.t()]
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)
query =
if opts["restrict_unlisted"] do
restrict_unlisted(query)
else
query
end
Pagination.fetch_paginated(query, opts, pagination)
[Constants.as_public()]
|> fetch_activities_query(opts)
|> restrict_unlisted(opts)
|> Pagination.fetch_paginated(opts, pagination)
end
@spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
opts
|> Map.put("restrict_unlisted", true)
|> Map.put(:restrict_unlisted, true)
|> fetch_public_or_unlisted_activities(pagination)
end
@ -577,20 +560,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_visibility(query, %{visibility: visibility})
when is_list(visibility) do
if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
query =
from(
a in query,
where:
fragment(
"activity_visibility(?, ?, ?) = ANY (?)",
a.actor,
a.recipients,
a.data,
^visibility
)
)
query
from(
a in query,
where:
fragment(
"activity_visibility(?, ?, ?) = ANY (?)",
a.actor,
a.recipients,
a.data,
^visibility
)
)
else
Logger.error("Could not restrict visibility to #{visibility}")
end
@ -612,7 +592,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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
if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
from(
@ -632,7 +612,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
defp exclude_visibility(query, %{exclude_visibilities: visibility})
when visibility in @valid_visibilities do
from(
a in query,
@ -647,7 +627,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
)
end
defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
defp exclude_visibility(query, %{exclude_visibilities: visibility})
when visibility not in [nil | @valid_visibilities] do
Logger.error("Could not exclude visibility to #{visibility}")
query
@ -658,14 +638,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
do: query
defp restrict_thread_visibility(
query,
%{"user" => %User{skip_thread_containment: true}},
_
),
do: query
defp restrict_thread_visibility(query, %{user: %User{skip_thread_containment: true}}, _),
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(
a in query,
where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
@ -677,87 +653,79 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
params =
params
|> Map.put("user", reading_user)
|> Map.put("actor_id", user.ap_id)
|> Map.put(:user, reading_user)
|> Map.put(:actor_id, user.ap_id)
recipients =
user_activities_recipients(%{
"godmode" => params["godmode"],
"reading_user" => reading_user
})
fetch_activities(recipients, params)
%{
godmode: params[:godmode],
reading_user: reading_user
}
|> user_activities_recipients()
|> fetch_activities(params)
|> Enum.reverse()
end
def fetch_user_activities(user, reading_user, params \\ %{}) do
params =
params
|> Map.put("type", ["Create", "Announce"])
|> Map.put("user", reading_user)
|> Map.put("actor_id", user.ap_id)
|> Map.put("pinned_activity_ids", user.pinned_activities)
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:user, reading_user)
|> Map.put(:actor_id, user.ap_id)
|> Map.put(:pinned_activity_ids, user.pinned_activities)
params =
if User.blocks?(reading_user, user) do
params
else
params
|> Map.put("blocking_user", reading_user)
|> Map.put("muting_user", reading_user)
|> Map.put(:blocking_user, reading_user)
|> Map.put(:muting_user, reading_user)
end
recipients =
user_activities_recipients(%{
"godmode" => params["godmode"],
"reading_user" => reading_user
})
fetch_activities(recipients, params)
%{
godmode: params[:godmode],
reading_user: reading_user
}
|> user_activities_recipients()
|> fetch_activities(params)
|> Enum.reverse()
end
def fetch_statuses(reading_user, params) do
params =
params
|> Map.put("type", ["Create", "Announce"])
params = Map.put(params, :type, ["Create", "Announce"])
recipients =
user_activities_recipients(%{
"godmode" => params["godmode"],
"reading_user" => reading_user
})
fetch_activities(recipients, params, :offset)
%{
godmode: params[:godmode],
reading_user: reading_user
}
|> user_activities_recipients()
|> fetch_activities(params, :offset)
|> Enum.reverse()
end
defp user_activities_recipients(%{"godmode" => true}) do
[]
end
defp user_activities_recipients(%{godmode: true}), do: []
defp user_activities_recipients(%{"reading_user" => reading_user}) do
defp user_activities_recipients(%{reading_user: 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
[Constants.as_public()]
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)
end
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!"
end
defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
when is_list(tag_reject) and tag_reject != [] do
defp restrict_tag_reject(query, %{tag_reject: [_ | _] = tag_reject}) do
from(
[_activity, object] in query,
where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
@ -766,12 +734,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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!"
end
defp restrict_tag_all(query, %{"tag_all" => tag_all})
when is_list(tag_all) and tag_all != [] do
defp restrict_tag_all(query, %{tag_all: [_ | _] = tag_all}) do
from(
[_activity, object] in query,
where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
@ -780,18 +747,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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!"
end
defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
defp restrict_tag(query, %{tag: tag}) when is_list(tag) do
from(
[_activity, object] in query,
where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
)
end
defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
defp restrict_tag(query, %{tag: tag}) when is_binary(tag) do
from(
[_activity, object] in query,
where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
@ -814,35 +781,35 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
)
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)
end
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)
end
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))
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))
end
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))
end
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(
[_activity, object] in query,
where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
@ -851,11 +818,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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!"
end
defp restrict_media(query, %{"only_media" => val}) when val in [true, "true", "1"] do
defp restrict_media(query, %{only_media: true}) do
from(
[_activity, object] in query,
where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
@ -864,7 +831,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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(
[_activity, object] in query,
where: fragment("?->>'inReplyTo' is null", object.data)
@ -872,8 +839,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
defp restrict_replies(query, %{
"reply_filtering_user" => user,
"reply_visibility" => "self"
reply_filtering_user: user,
reply_visibility: "self"
}) do
from(
[activity, object] in query,
@ -888,8 +855,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
defp restrict_replies(query, %{
"reply_filtering_user" => user,
"reply_visibility" => "following"
reply_filtering_user: user,
reply_visibility: "following"
}) do
from(
[activity, object] in query,
@ -908,16 +875,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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))
end
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
mutes = opts["muted_users_ap_ids"] || User.muted_users_ap_ids(user)
defp restrict_muted(query, %{muting_user: %User{} = user} = opts) do
mutes = opts[:muted_users_ap_ids] || User.muted_users_ap_ids(user)
query =
from([activity] in query,
@ -925,7 +892,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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))
else
query
@ -934,8 +901,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_muted(query, _), do: query
defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
defp restrict_blocked(query, %{blocking_user: %User{} = user} = opts) do
blocked_ap_ids = opts[:blocked_users_ap_ids] || User.blocked_users_ap_ids(user)
domain_blocks = user.domain_blocks || []
following_ap_ids = User.get_friends_ap_ids(user)
@ -947,6 +914,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
[activity, object: o] in query,
where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
where:
fragment(
"recipients_contain_blocked_domains(?, ?) = false",
activity.recipients,
^domain_blocks
),
where:
fragment(
"not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
@ -975,7 +948,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_blocked(query, _), do: query
defp restrict_unlisted(query) do
defp restrict_unlisted(query, %{restrict_unlisted: true}) do
from(
activity in query,
where:
@ -987,19 +960,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
)
end
# TODO: when all endpoints migrated to OpenAPI compare `pinned` with `true` (boolean) only,
# the same for `restrict_media/2`, `restrict_replies/2`, 'restrict_reblogs/2'
# and `restrict_muted/2`
defp restrict_unlisted(query, _), do: query
defp restrict_pinned(query, %{"pinned" => pinned, "pinned_activity_ids" => ids})
when pinned in [true, "true", "1"] do
defp restrict_pinned(query, %{pinned: true, pinned_activity_ids: ids}) do
from(activity in query, where: activity.id in ^ids)
end
defp restrict_pinned(query, _), do: query
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)
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)
from(
activity in query,
@ -1015,7 +985,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_muted_reblogs(query, _), do: query
defp restrict_instance(query, %{"instance" => instance}) do
defp restrict_instance(query, %{instance: instance}) do
users =
from(
u in User,
@ -1029,7 +999,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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
if has_named_binding?(query, :object) do
@ -1041,38 +1011,49 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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)
end
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
query
|> Activity.with_preloaded_object()
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
query
|> Activity.with_preloaded_bookmark(opts["user"])
|> Activity.with_preloaded_bookmark(opts[:user])
end
defp maybe_preload_report_notes(query, %{"preload_report_notes" => true}) do
defp maybe_preload_report_notes(query, %{preload_report_notes: true}) do
query
|> Activity.with_preloaded_report_notes()
end
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
query
|> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
|> Activity.with_set_thread_muted_field(opts[:muting_user] || opts[:user])
end
defp maybe_order(query, %{order: :desc}) do
@ -1088,24 +1069,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp maybe_order(query, _), do: query
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 =
ap_id_relationships ++
if opts["blocking_user"] && opts["blocking_user"] == source_user do
[:block]
else
[]
end
if opts[:blocking_user] && opts[:blocking_user] == source_user do
[:block | ap_id_relationships]
else
ap_id_relationships
end
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_muted_opts = Map.merge(%{"muted_users_ap_ids" => preloaded_ap_ids[:mute]}, 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_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}
end
@ -1124,7 +1104,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> maybe_preload_report_notes(opts)
|> maybe_set_thread_muted_field(opts)
|> maybe_order(opts)
|> restrict_recipients(recipients, opts["user"])
|> restrict_recipients(recipients, opts[:user])
|> restrict_replies(opts)
|> restrict_tag(opts)
|> restrict_tag_reject(opts)
@ -1146,16 +1126,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_instance(opts)
|> Activity.restrict_deactivated_users()
|> exclude_poll_votes(opts)
|> exclude_invisible_actors(opts)
|> exclude_visibility(opts)
end
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)
|> Pagination.fetch_paginated(opts, pagination)
|> Enum.reverse()
|> maybe_update_cc(list_memberships, opts["user"])
|> maybe_update_cc(list_memberships, opts[:user])
end
@doc """
@ -1171,16 +1152,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> select([_like, object, activity], %{activity | object: object})
|> order_by([like, _, _], desc_nulls_last: like.id)
|> Pagination.fetch_paginated(
Map.merge(params, %{"skip_order" => true}),
Map.merge(params, %{skip_order: true}),
pagination,
:object_activity
)
end
defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
when is_list(list_memberships) and length(list_memberships) > 0 do
defp maybe_update_cc(activities, [_ | _] = list_memberships, %User{ap_id: user_ap_id}) do
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
update_in(activity.data["cc"], &[user_ap_id | &1])
else
@ -1194,7 +1174,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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,
where:
fragment("? && ?", activity.recipients, ^recipients) or
@ -1218,12 +1198,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
def upload(file, opts \\ []) do
with {:ok, data} <- Upload.store(file, opts) do
obj_data =
if opts[:actor] do
Map.put(data, "actor", opts[:actor])
else
data
end
obj_data = Maps.put_if_present(data, "actor", opts[:actor])
Repo.insert(%Object{data: obj_data})
end
@ -1269,8 +1244,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
%{"type" => "Emoji"} -> true
_ -> false
end)
|> Enum.reduce(%{}, fn %{"icon" => %{"url" => url}, "name" => name}, acc ->
Map.put(acc, String.trim(name, ":"), url)
|> Map.new(fn %{"icon" => %{"url" => url}, "name" => name} ->
{String.trim(name, ":"), url}
end)
locked = data["manuallyApprovesFollowers"] || false
@ -1316,18 +1291,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
}
# nickname can be nil because of virtual actors
user_data =
if data["preferredUsername"] do
Map.put(
user_data,
:nickname,
"#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
)
else
Map.put(user_data, :nickname, nil)
end
{:ok, user_data}
if data["preferredUsername"] do
Map.put(
user_data,
:nickname,
"#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
)
else
Map.put(user_data, :nickname, nil)
end
end
def fetch_follow_information_for_user(user) do
@ -1402,9 +1374,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp collection_private(_data), do: {:ok, true}
def user_data_from_user_object(data) do
with {:ok, data} <- MRF.filter(data),
{:ok, data} <- object_to_user_data(data) do
{:ok, data}
with {:ok, data} <- MRF.filter(data) do
{:ok, object_to_user_data(data)}
else
e -> {:error, e}
end
@ -1412,15 +1383,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub 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),
{:ok, data} <- user_data_from_user_object(data),
data <- maybe_update_follow_information(data) do
{:ok, data}
{:ok, data} <- user_data_from_user_object(data) do
{:ok, maybe_update_follow_information(data)}
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)}")
{:error, e}
e ->
{:error, e} ->
Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
{:error, e}
end
@ -1443,8 +1413,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Repo.insert()
|> User.set_cache()
end
else
e -> {:error, e}
end
end
end
@ -1458,7 +1426,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
# 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)
end
@ -1469,7 +1437,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def fetch_direct_messages_query do
Activity
|> restrict_type(%{"type" => "Create"})
|> restrict_type(%{type: "Create"})
|> restrict_visibility(%{visibility: "direct"})
|> order_by([activity], asc: activity.id)
end

View file

@ -21,6 +21,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
alias Pleroma.Web.ActivityPub.UserView
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.ControllerHelper
alias Pleroma.Web.Endpoint
alias Pleroma.Web.FederatingPlug
alias Pleroma.Web.Federator
@ -230,27 +231,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
when page? in [true, "true"] do
with %User{} = user <- User.get_cached_by_nickname(nickname),
{:ok, user} <- User.ensure_keys_present(user) do
activities =
if params["max_id"] do
ActivityPub.fetch_user_activities(user, for_user, %{
"max_id" => params["max_id"],
# This is a hack because postgres generates inefficient queries when filtering by
# 'Answer', poll votes will be hidden by the visibility filter in this case anyway
"include_poll_votes" => true,
"limit" => 10
})
else
ActivityPub.fetch_user_activities(user, for_user, %{
"limit" => 10,
"include_poll_votes" => true
})
end
# "include_poll_votes" is a hack because postgres generates inefficient
# queries when filtering by 'Answer', poll votes will be hidden by the
# visibility filter in this case anyway
params =
params
|> Map.drop(["nickname", "page"])
|> Map.put("include_poll_votes", true)
|> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end)
activities = ActivityPub.fetch_user_activities(user, for_user, params)
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
|> render("activity_collection_page.json", %{
activities: activities,
pagination: ControllerHelper.get_pagination_fields(conn, activities),
iri: "#{user.ap_id}/outbox"
})
end
@ -353,21 +350,24 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
%{"nickname" => nickname, "page" => page?} = params
)
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 =
if params["max_id"] do
ActivityPub.fetch_activities([user.ap_id | User.following(user)], %{
"max_id" => params["max_id"],
"limit" => 10
})
else
ActivityPub.fetch_activities([user.ap_id | User.following(user)], %{"limit" => 10})
end
[user.ap_id | User.following(user)]
|> ActivityPub.fetch_activities(params)
|> Enum.reverse()
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
|> render("activity_collection_page.json", %{
activities: activities,
pagination: ControllerHelper.get_pagination_fields(conn, activities),
iri: "#{user.ap_id}/inbox"
})
end

View file

@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
alias Pleroma.Activity
alias Pleroma.EarmarkRenderer
alias Pleroma.FollowingRelationship
alias Pleroma.Maps
alias Pleroma.Object
alias Pleroma.Object.Containment
alias Pleroma.Repo
@ -208,12 +209,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Map.put("conversation", context)
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
attachments =
Enum.map(attachment, fn data ->
@ -241,13 +236,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
attachment_url =
%{"href" => href}
|> add_if_present("mediaType", media_type)
|> add_if_present("type", Map.get(url || %{}, "type"))
|> Maps.put_if_present("mediaType", media_type)
|> Maps.put_if_present("type", Map.get(url || %{}, "type"))
%{"url" => [attachment_url]}
|> add_if_present("mediaType", media_type)
|> add_if_present("type", data["type"])
|> add_if_present("name", data["name"])
|> Maps.put_if_present("mediaType", media_type)
|> Maps.put_if_present("type", data["type"])
|> Maps.put_if_present("name", data["name"])
end)
Map.put(object, "attachment", attachments)

View file

@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
alias Ecto.UUID
alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.Maps
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Repo
@ -244,7 +245,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
Inserts a full object if it is contained in an activity.
"""
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
map = Map.put(map, "object", object.data["id"])
@ -307,7 +308,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"cc" => cc,
"context" => object.data["context"]
}
|> maybe_put("id", activity_id)
|> Maps.put_if_present("id", activity_id)
end
def make_emoji_reaction_data(user, object, emoji, activity_id) do
@ -477,7 +478,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"object" => followed_id,
"state" => "pending"
}
|> maybe_put("id", activity_id)
|> Maps.put_if_present("id", activity_id)
end
def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
@ -546,7 +547,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"cc" => [],
"context" => object.data["context"]
}
|> maybe_put("id", activity_id)
|> Maps.put_if_present("id", activity_id)
end
def make_announce_data(
@ -563,7 +564,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"cc" => [Pleroma.Constants.as_public()],
"context" => object.data["context"]
}
|> maybe_put("id", activity_id)
|> Maps.put_if_present("id", activity_id)
end
def make_undo_data(
@ -582,7 +583,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"cc" => [Pleroma.Constants.as_public()],
"context" => context
}
|> maybe_put("id", activity_id)
|> Maps.put_if_present("id", activity_id)
end
@spec add_announce_to_object(Activity.t(), Object.t()) ::
@ -627,7 +628,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"to" => [followed.ap_id],
"object" => follow_activity.data
}
|> maybe_put("id", activity_id)
|> Maps.put_if_present("id", activity_id)
end
#### Block-related helpers
@ -650,7 +651,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"to" => [blocked.ap_id],
"object" => blocked.ap_id
}
|> maybe_put("id", activity_id)
|> Maps.put_if_present("id", activity_id)
end
#### Create-related helpers
@ -740,12 +741,12 @@ defmodule Pleroma.Web.ActivityPub.Utils do
def get_reports(params, page, page_size) do
params =
params
|> Map.put("type", "Flag")
|> Map.put("skip_preload", true)
|> Map.put("preload_report_notes", true)
|> Map.put("total", true)
|> Map.put("limit", page_size)
|> Map.put("offset", (page - 1) * page_size)
|> Map.put(:type, "Flag")
|> Map.put(:skip_preload, true)
|> Map.put(:preload_report_notes, true)
|> Map.put(:total, true)
|> Map.put(:limit, page_size)
|> Map.put(:offset, (page - 1) * page_size)
ActivityPub.fetch_activities([], params, :offset)
end
@ -870,7 +871,4 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> where([a, object: o], fragment("(?)->>'type' = 'Answer'", o.data))
|> Repo.all()
end
def maybe_put(map, _key, nil), do: map
def maybe_put(map, key, value), do: Map.put(map, key, value)
end

View file

@ -213,34 +213,24 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|> Map.merge(Utils.make_json_ld_header())
end
def render("activity_collection_page.json", %{activities: activities, iri: iri}) do
# this is sorted chronologically, so first activity is the newest (max)
{max_id, min_id, collection} =
if length(activities) > 0 do
{
Enum.at(activities, 0).id,
Enum.at(Enum.reverse(activities), 0).id,
Enum.map(activities, fn act ->
{:ok, data} = Transmogrifier.prepare_outgoing(act.data)
data
end)
}
else
{
0,
0,
[]
}
end
def render("activity_collection_page.json", %{
activities: activities,
iri: iri,
pagination: pagination
}) do
collection =
Enum.map(activities, fn activity ->
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
data
end)
%{
"id" => "#{iri}?max_id=#{max_id}&page=true",
"type" => "OrderedCollectionPage",
"partOf" => iri,
"orderedItems" => collection,
"next" => "#{iri}?max_id=#{min_id}&page=true"
"orderedItems" => collection
}
|> Map.merge(Utils.make_json_ld_header())
|> Map.merge(pagination)
end
defp maybe_put_total_items(map, false, _total), do: map

View file

@ -7,38 +7,24 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.ConfigDB
alias Pleroma.MFA
alias Pleroma.ModerationLog
alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.ReportNote
alias Pleroma.Stats
alias Pleroma.User
alias Pleroma.UserInviteToken
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.AdminAPI
alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.AdminAPI.ConfigView
alias Pleroma.Web.AdminAPI.ModerationLogView
alias Pleroma.Web.AdminAPI.Report
alias Pleroma.Web.AdminAPI.ReportView
alias Pleroma.Web.AdminAPI.Search
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Endpoint
alias Pleroma.Web.MastodonAPI
alias Pleroma.Web.MastodonAPI.AppView
alias Pleroma.Web.OAuth.App
alias Pleroma.Web.Router
require Logger
@descriptions Pleroma.Docs.JSON.compile()
@users_page_size 50
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(
OAuthScopesPlug,
%{scopes: ["write:follows"], admin: true}
when action in [:user_follow, :user_unfollow, :relay_follow, :relay_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]
when action in [:user_follow, :user_unfollow]
)
plug(
@ -105,11 +71,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
OAuthScopesPlug,
%{scopes: ["read"], admin: true}
when action in [
:config_show,
:list_log,
:stats,
:relay_list,
:config_descriptions,
:need_reboot
]
)
@ -119,13 +82,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
%{scopes: ["write"], admin: true}
when action in [
:restart,
:config_update,
:resend_confirmation_email,
:confirm_email,
:oauth_app_create,
:oauth_app_list,
:oauth_app_update,
:oauth_app_delete,
:reload_emoji
]
)
@ -268,10 +226,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
activities =
ActivityPub.fetch_statuses(nil, %{
"instance" => instance,
"limit" => page_size,
"offset" => (page - 1) * page_size,
"exclude_reblogs" => !with_reblogs && "true"
instance: instance,
limit: page_size,
offset: (page - 1) * page_size,
exclude_reblogs: not with_reblogs
})
conn
@ -288,13 +246,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
activities =
ActivityPub.fetch_user_activities(user, nil, %{
"limit" => page_size,
"godmode" => godmode,
"exclude_reblogs" => !with_reblogs && "true"
limit: page_size,
godmode: godmode,
exclude_reblogs: not with_reblogs
})
conn
|> put_view(MastodonAPI.StatusView)
|> put_view(AdminAPI.StatusView)
|> render("index.json", %{activities: activities, as: :activity})
else
_ -> {:error, :not_found}
@ -531,113 +489,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
render_error(conn, :forbidden, "You can't revoke your own admin status.")
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"
def get_password_reset(conn, %{"nickname" => nickname}) do
(%User{local: true} = user) = User.get_cached_by_nickname(nickname)
@ -724,85 +575,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
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
{page, page_size} = page_params(params)
@ -821,105 +593,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|> render("index.json", %{log: log})
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
with :ok <- configurable_from_database() do
Restarter.Pleroma.restart(Config.get(:env), 50)
@ -940,28 +613,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
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
Pleroma.Emoji.reload()
@ -996,83 +647,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
conn |> json("")
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
count = Stats.get_status_visibility_count()

View 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

View 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

View file

@ -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

View 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

View 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

View file

@ -29,11 +29,11 @@ defmodule Pleroma.Web.AdminAPI.StatusController do
def index(%{assigns: %{user: _admin}} = conn, params) do
activities =
ActivityPub.fetch_statuses(nil, %{
"godmode" => params.godmode,
"local_only" => params.local_only,
"limit" => params.page_size,
"offset" => (params.page - 1) * params.page_size,
"exclude_reblogs" => not params.with_reblogs
godmode: params.godmode,
local_only: params.local_only,
limit: params.page_size,
offset: (params.page - 1) * params.page_size,
exclude_reblogs: not params.with_reblogs
})
render(conn, "index.json", activities: activities, as: :activity)
@ -41,9 +41,7 @@ defmodule Pleroma.Web.AdminAPI.StatusController do
def show(conn, %{id: id}) do
with %Activity{} = activity <- Activity.get_by_id(id) do
conn
|> put_view(MastodonAPI.StatusView)
|> render("show.json", %{activity: activity})
render(conn, "show.json", %{activity: activity})
else
nil -> {:error, :not_found}
end

View file

@ -21,7 +21,7 @@ defmodule Pleroma.Web.AdminAPI.Search do
query =
params
|> Map.drop([:page, :page_size])
|> Map.put(:exclude_service_users, true)
|> Map.put(:invisible, false)
|> User.Query.build()
|> order_by([u], u.nickname)
@ -31,7 +31,6 @@ defmodule Pleroma.Web.AdminAPI.Search do
count = Repo.aggregate(query, :count, :id)
results = Repo.all(paginated_query)
{:ok, results, count}
end
end

View file

@ -80,24 +80,6 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
}
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
%{
type: "success",

View 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

View file

@ -18,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
%{
reports:
reports[:items]
|> Enum.map(&Report.extract_report_info(&1))
|> Enum.map(&Report.extract_report_info/1)
|> Enum.map(&render(__MODULE__, "show.json", &1))
|> Enum.reverse(),
total: reports[:total]

View 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

View 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

View 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

View 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

View 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

View file

@ -74,7 +74,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do
parameters: [id_param()],
security: [%{"oAuth" => ["read:statuses"]}],
responses: %{
200 => Operation.response("Status", "application/json", Status),
200 => Operation.response("Status", "application/json", status()),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
@ -123,7 +123,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do
}
end
defp admin_account do
def admin_account do
%Schema{
type: :object,
properties: %{

View file

@ -137,7 +137,7 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do
"background_upload_limit" => 4_000_000,
"background_image" => "/static/image.png",
"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",
"languages" => ["en"],
"max_toot_chars" => 5000,

View file

@ -5,6 +5,8 @@
defmodule Pleroma.Web.ControllerHelper do
use Pleroma.Web, :controller
alias Pleroma.Pagination
# 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"]
@ -46,33 +48,8 @@ defmodule Pleroma.Web.ControllerHelper do
do: conn
def add_link_headers(conn, activities, extra_params) do
case List.last(activities) do
%{id: max_id} ->
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}))
case get_pagination_fields(conn, activities, extra_params) do
%{"next" => next_url, "prev" => prev_url} ->
put_resp_header(conn, "link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"")
_ ->
@ -80,9 +57,43 @@ defmodule Pleroma.Web.ControllerHelper do
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
# TODO: use `conn.params[:id]` only after moving to OpenAPI
case Pleroma.User.get_cached_by_id(conn.params[:id] || conn.params["id"]) do
case Pleroma.User.get_cached_by_id(conn.params.id) do
%Pleroma.User{} = account -> assign(conn, :account, account)
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
end
@ -99,11 +110,6 @@ defmodule Pleroma.Web.ControllerHelper do
render_error(conn, :not_implemented, "Can't display this activity")
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 """
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

View 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

View file

@ -9,14 +9,12 @@ defmodule Pleroma.Web.Feed.TagController do
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.Feed.FeedView
import Pleroma.Web.ControllerHelper, only: [put_if_exist: 3]
def feed(conn, %{"tag" => raw_tag} = params) do
{format, tag} = parse_tag(raw_tag)
activities =
%{"type" => ["Create"], "tag" => tag}
|> put_if_exist("max_id", params["max_id"])
%{type: ["Create"], tag: tag}
|> Pleroma.Maps.put_if_present(:max_id, params["max_id"])
|> ActivityPub.fetch_public_activities()
conn

View file

@ -11,8 +11,6 @@ defmodule Pleroma.Web.Feed.UserController do
alias Pleroma.Web.ActivityPub.ActivityPubController
alias Pleroma.Web.Feed.FeedView
import Pleroma.Web.ControllerHelper, only: [put_if_exist: 3]
plug(Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect])
action_fallback(:errors)
@ -52,10 +50,10 @@ defmodule Pleroma.Web.Feed.UserController do
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
activities =
%{
"type" => ["Create"],
"actor_id" => user.ap_id
type: ["Create"],
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()
conn

View file

@ -14,6 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
json_response: 3
]
alias Pleroma.Maps
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Plugs.RateLimiter
@ -139,9 +140,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
end
@doc "PATCH /api/v1/accounts/update_credentials"
def update_credentials(%{assigns: %{user: original_user}, body_params: params} = conn, _params) do
user = original_user
def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _params) do
params =
params
|> Enum.filter(fn {_, value} -> not is_nil(value) end)
@ -162,41 +161,49 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
:discoverable
]
|> 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)
|> add_if_present(params, :display_name, :name)
|> add_if_present(params, :note, :bio)
|> add_if_present(params, :avatar, :avatar)
|> add_if_present(params, :header, :banner)
|> add_if_present(params, :pleroma_background_image, :background)
|> add_if_present(
params,
:fields_attributes,
|> Maps.put_if_present(:name, params[:display_name])
|> Maps.put_if_present(:bio, params[:note])
|> Maps.put_if_present(:avatar, params[:avatar])
|> Maps.put_if_present(:banner, params[:header])
|> Maps.put_if_present(:background, params[:pleroma_background_image])
|> Maps.put_if_present(
:raw_fields,
params[:fields_attributes],
&{:ok, normalize_fields_attributes(&1)}
)
|> add_if_present(params, :pleroma_settings_store, :pleroma_settings_store)
|> add_if_present(params, :default_scope, :default_scope)
|> add_if_present(params["source"], "privacy", :default_scope)
|> add_if_present(params, :actor_type, :actor_type)
|> Maps.put_if_present(:pleroma_settings_store, params[:pleroma_settings_store])
|> Maps.put_if_present(:default_scope, params[:default_scope])
|> Maps.put_if_present(:default_scope, params["source"]["privacy"])
|> Maps.put_if_present(:actor_type, params[:actor_type])
changeset = User.update_changeset(user, user_params)
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)
else
_e -> render_error(conn, :forbidden, "Invalid request")
end
end
defp add_if_present(map, params, params_field, map_field, value_function \\ &{:ok, &1}) do
with true <- is_map(params),
true <- Map.has_key?(params, params_field),
{:ok, new_value} <- value_function.(Map.get(params, params_field)) do
Map.put(map, map_field, new_value)
else
_ -> map
end
# Hotfix, handling will be redone with the pipeline
defp build_update_activity_params(user) do
object =
Pleroma.Web.ActivityPub.UserView.render("user.json", user: user)
|> Map.delete("@context")
%{
local: true,
to: [user.follower_address],
cc: [],
object: object,
actor: user.ap_id
}
end
defp normalize_fields_attributes(fields) do
@ -237,9 +244,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
params =
params
|> Map.delete(:tagged)
|> Enum.filter(&(not is_nil(&1)))
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("tag", params[:tagged])
|> Map.put(:tag, params[:tagged])
activities = ActivityPub.fetch_user_activities(user, reading_user, params)

View file

@ -21,7 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.ConversationController do
@doc "GET /api/v1/conversations"
def index(%{assigns: %{user: user}} = conn, params) do
params = stringify_pagination_params(params)
participations = Participation.for_user_with_last_activity_id(user, params)
conn
@ -37,20 +36,4 @@ defmodule Pleroma.Web.MastodonAPI.ConversationController do
render(conn, "participation.json", participation: participation, for: user)
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

View file

@ -113,22 +113,44 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
query
|> prepare_tags()
|> Enum.map(fn tag ->
tag = String.trim_leading(tag, "#")
%{name: tag, url: tags_path <> tag}
end)
end
defp resource_search(:v1, "hashtags", query, _options) do
query
|> prepare_tags()
|> Enum.map(fn tag -> String.trim_leading(tag, "#") end)
prepare_tags(query)
end
defp prepare_tags(query) do
query
|> String.split()
|> Enum.uniq()
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
defp prepare_tags(query, add_joined_tag \\ true) do
tags =
query
|> String.split(~r/[^#\w]+/u, trim: true)
|> Enum.uniq_by(&String.downcase/1)
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
defp with_fallback(f, fallback \\ []) do

View file

@ -359,9 +359,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
with %Activity{} = activity <- Activity.get_by_id(id) do
activities =
ActivityPub.fetch_activities_for_context(activity.data["context"], %{
"blocking_user" => user,
"user" => user,
"exclude_id" => activity.id
blocking_user: user,
user: user,
exclude_id: activity.id
})
render(conn, "context.json", activity: activity, activities: activities, user: user)
@ -370,11 +370,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
@doc "GET /api/v1/favourites"
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)
conn

View file

@ -44,17 +44,14 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
def home(%{assigns: %{user: user}} = conn, params) do
params =
params
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("type", ["Create", "Announce"])
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("reply_filtering_user", user)
|> Map.put("user", user)
recipients = [user.ap_id | User.following(user)]
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_filtering_user, user)
|> Map.put(:user, user)
activities =
recipients
[user.ap_id | User.following(user)]
|> ActivityPub.fetch_activities(params)
|> Enum.reverse()
@ -71,10 +68,9 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
def direct(%{assigns: %{user: user}} = conn, params) do
params =
params
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("type", "Create")
|> Map.put("blocking_user", user)
|> Map.put("user", user)
|> Map.put(:type, "Create")
|> Map.put(:blocking_user, user)
|> Map.put(:user, user)
|> Map.put(:visibility, "direct")
activities =
@ -93,9 +89,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
# GET /api/v1/timelines/public
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 =
if local_only do
@ -111,11 +105,11 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
else
activities =
params
|> Map.put("type", ["Create", "Announce"])
|> Map.put("local_only", local_only)
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("reply_filtering_user", user)
|> Map.put(:type, ["Create"])
|> Map.put(:local_only, local_only)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_filtering_user, user)
|> ActivityPub.fetch_public_activities()
conn
@ -130,39 +124,38 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
defp hashtag_fetching(params, user, local_only) do
tags =
[params["tag"], params["any"]]
[params[:tag], params[:any]]
|> List.flatten()
|> Enum.uniq()
|> Enum.filter(& &1)
|> Enum.map(&String.downcase(&1))
|> Enum.reject(&is_nil/1)
|> Enum.map(&String.downcase/1)
tag_all =
params
|> Map.get("all", [])
|> Enum.map(&String.downcase(&1))
|> Map.get(:all, [])
|> Enum.map(&String.downcase/1)
tag_reject =
params
|> Map.get("none", [])
|> Enum.map(&String.downcase(&1))
|> 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)
|> 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)
|> ActivityPub.fetch_public_activities()
end
# GET /api/v1/timelines/tag/:tag
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)
conn

View file

@ -45,10 +45,6 @@ defmodule Pleroma.Web.MastodonAPI.AppView do
defp with_vapid_key(data) do
vapid_key = Application.get_env(:web_push_encryption, :vapid_details, [])[:public_key]
if vapid_key do
Map.put(data, "vapid_key", vapid_key)
else
data
end
Pleroma.Maps.put_if_present(data, "vapid_key", vapid_key)
end
end

View file

@ -24,8 +24,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
last_activity_id =
with nil <- participation.last_activity_id do
ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{
"user" => user,
"blocking_user" => user
user: user,
blocking_user: user
})
end

View file

@ -30,7 +30,7 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do
defp with_media_attachments(data, _), do: data
defp status_params(params) do
data = %{
%{
text: params["status"],
sensitive: params["sensitive"],
spoiler_text: params["spoiler_text"],
@ -39,10 +39,6 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do
poll: params["poll"],
in_reply_to_id: params["in_reply_to_id"]
}
case params["media_ids"] do
nil -> data
media_ids -> Map.put(data, :media_ids, media_ids)
end
|> Pleroma.Maps.put_if_present(:media_ids, params["media_ids"])
end
end

View file

@ -25,12 +25,12 @@ defmodule Pleroma.Web.OAuth.App do
timestamps()
end
@spec changeset(App.t(), map()) :: Ecto.Changeset.t()
@spec changeset(t(), map()) :: Ecto.Changeset.t()
def changeset(struct, params) do
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted])
end
@spec register_changeset(App.t(), map()) :: Ecto.Changeset.t()
@spec register_changeset(t(), map()) :: Ecto.Changeset.t()
def register_changeset(struct, params \\ %{}) do
changeset =
struct
@ -52,18 +52,19 @@ defmodule Pleroma.Web.OAuth.App do
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
with changeset <- __MODULE__.register_changeset(%__MODULE__{}, params) do
Repo.insert(changeset)
end
%__MODULE__{}
|> register_changeset(params)
|> Repo.insert()
end
@spec update(map()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
def update(params) do
with %__MODULE__{} = app <- Repo.get(__MODULE__, params["id"]),
changeset <- changeset(app, params) do
Repo.update(changeset)
@spec update(pos_integer(), map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
def update(id, params) do
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
app
|> changeset(params)
|> Repo.update()
end
end
@ -71,7 +72,7 @@ defmodule Pleroma.Web.OAuth.App do
Gets app by attrs or create new with attrs.
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
with %__MODULE__{} = app <- Repo.get_by(__MODULE__, attrs) do
update_scopes(app, scopes)
@ -92,7 +93,7 @@ defmodule Pleroma.Web.OAuth.App do
|> Repo.update()
end
@spec search(map()) :: {:ok, [App.t()], non_neg_integer()}
@spec search(map()) :: {:ok, [t()], non_neg_integer()}
def search(params) do
query = from(a in __MODULE__)
@ -128,7 +129,7 @@ defmodule Pleroma.Web.OAuth.App do
{:ok, Repo.all(query), count}
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
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
Repo.delete(app)

View file

@ -6,6 +6,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
use Pleroma.Web, :controller
alias Pleroma.Helpers.UriHelper
alias Pleroma.Maps
alias Pleroma.MFA
alias Pleroma.Plugs.RateLimiter
alias Pleroma.Registration
@ -108,7 +109,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
if redirect_uri in String.split(app.redirect_uris) do
redirect_uri = redirect_uri(conn, redirect_uri)
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)
redirect(conn, external: url)
else
@ -147,7 +148,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
if redirect_uri in String.split(app.redirect_uris) do
redirect_uri = redirect_uri(conn, redirect_uri)
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)
redirect(conn, external: url)
else

View file

@ -126,10 +126,9 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
def favourites(%{assigns: %{user: for_user, account: user}} = conn, params) do
params =
params
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("type", "Create")
|> Map.put("favorited_by", user.ap_id)
|> Map.put("blocking_user", for_user)
|> Map.put(:type, "Create")
|> Map.put(:favorited_by, user.ap_id)
|> Map.put(:blocking_user, for_user)
recipients =
if for_user do

View file

@ -42,15 +42,14 @@ defmodule Pleroma.Web.PleromaAPI.ConversationController do
Participation.get(participation_id, preload: [:conversation]) do
params =
params
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
activities =
participation.conversation.ap_id
|> 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()
conn

View file

@ -36,10 +36,7 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleController 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
params =
params
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("type", ["Listen"])
params = Map.put(params, :type, ["Listen"])
activities = ActivityPub.fetch_user_abstract_activities(user, reading_user, params)

View file

@ -160,14 +160,14 @@ defmodule Pleroma.Web.Router do
:right_delete_multiple
)
get("/relay", AdminAPIController, :relay_list)
post("/relay", AdminAPIController, :relay_follow)
delete("/relay", AdminAPIController, :relay_unfollow)
get("/relay", RelayController, :index)
post("/relay", RelayController, :follow)
delete("/relay", RelayController, :unfollow)
post("/users/invite_token", AdminAPIController, :create_invite_token)
get("/users/invites", AdminAPIController, :invites)
post("/users/revoke_invite", AdminAPIController, :revoke_invite)
post("/users/email_invite", AdminAPIController, :email_invite)
post("/users/invite_token", InviteController, :create)
get("/users/invites", InviteController, :index)
post("/users/revoke_invite", InviteController, :revoke)
post("/users/email_invite", InviteController, :email)
get("/users/:nickname/password_reset", AdminAPIController, :get_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/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
get("/reports", AdminAPIController, :list_reports)
get("/reports/:id", AdminAPIController, :report_show)
patch("/reports", AdminAPIController, :reports_update)
post("/reports/:id/notes", AdminAPIController, :report_notes_create)
delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete)
get("/reports", ReportController, :index)
get("/reports/:id", ReportController, :show)
patch("/reports", ReportController, :update)
post("/reports/:id/notes", ReportController, :notes_create)
delete("/reports/:report_id/notes/:id", ReportController, :notes_delete)
get("/statuses/:id", StatusController, :show)
put("/statuses/:id", StatusController, :update)
delete("/statuses/:id", StatusController, :delete)
get("/statuses", StatusController, :index)
get("/config", AdminAPIController, :config_show)
post("/config", AdminAPIController, :config_update)
get("/config/descriptions", AdminAPIController, :config_descriptions)
get("/config", ConfigController, :show)
post("/config", ConfigController, :update)
get("/config/descriptions", ConfigController, :descriptions)
get("/need_reboot", AdminAPIController, :need_reboot)
get("/restart", AdminAPIController, :restart)
@ -205,10 +205,10 @@ defmodule Pleroma.Web.Router do
post("/reload_emoji", AdminAPIController, :reload_emoji)
get("/stats", AdminAPIController, :stats)
get("/oauth_app", AdminAPIController, :oauth_app_list)
post("/oauth_app", AdminAPIController, :oauth_app_create)
patch("/oauth_app/:id", AdminAPIController, :oauth_app_update)
delete("/oauth_app/:id", AdminAPIController, :oauth_app_delete)
get("/oauth_app", OAuthAppController, :index)
post("/oauth_app", OAuthAppController, :create)
patch("/oauth_app/:id", OAuthAppController, :update)
delete("/oauth_app/:id", OAuthAppController, :delete)
end
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
@ -571,13 +571,6 @@ defmodule Pleroma.Web.Router do
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
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
plug(:accepts, ["activity+json", "json"])
end
@ -602,6 +595,7 @@ defmodule Pleroma.Web.Router do
get("/api/ap/whoami", ActivityPubController, :whoami)
get("/users/:nickname/inbox", ActivityPubController, :read_inbox)
get("/users/:nickname/outbox", ActivityPubController, :outbox)
post("/users/:nickname/outbox", ActivityPubController, :update_outbox)
post("/api/ap/upload_media", ActivityPubController, :upload_media)
@ -664,6 +658,8 @@ defmodule Pleroma.Web.Router do
post("/auth/password", MastodonAPI.AuthController, :password_reset)
get("/web/*path", MastoFEController, :index)
get("/embed/:id", EmbedController, :show)
end
scope "/proxy/", Pleroma.Web.MediaProxy do

View file

@ -111,8 +111,14 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
%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 =
ActivityPub.fetch_user_activities(user, nil, Map.take(params, @page_keys))
user
|> ActivityPub.fetch_user_activities(nil, params)
|> Enum.map(&represent/1)
prev_page_id =

View 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 %>

View 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>

View 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>

View 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

View file

@ -3,14 +3,16 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-05-15 09:37+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"PO-Revision-Date: 2020-06-02 07:36+0000\n"
"Last-Translator: Fristi <fristi@subcon.town>\n"
"Language-Team: Dutch <https://translate.pleroma.social/projects/pleroma/"
"pleroma/nl/>\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Translate Toolkit 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.
##
@ -23,142 +25,142 @@ msgstr ""
## effect: edit them in PO (`.po`) files instead.
## From Ecto.Changeset.cast/4
msgid "can't be blank"
msgstr ""
msgstr "kan niet leeg zijn"
## From Ecto.Changeset.unique_constraint/3
msgid "has already been taken"
msgstr ""
msgstr "is al bezet"
## From Ecto.Changeset.put_change/3
msgid "is invalid"
msgstr ""
msgstr "is ongeldig"
## From Ecto.Changeset.validate_format/3
msgid "has invalid format"
msgstr ""
msgstr "heeft een ongeldig formaat"
## From Ecto.Changeset.validate_subset/3
msgid "has an invalid entry"
msgstr ""
msgstr "heeft een ongeldige entry"
## From Ecto.Changeset.validate_exclusion/3
msgid "is reserved"
msgstr ""
msgstr "is gereserveerd"
## From Ecto.Changeset.validate_confirmation/3
msgid "does not match confirmation"
msgstr ""
msgstr "komt niet overeen met bevestiging"
## From Ecto.Changeset.no_assoc_constraint/3
msgid "is still associated with this entry"
msgstr ""
msgstr "is nog geassocieerd met deze entry"
msgid "are still associated with this entry"
msgstr ""
msgstr "zijn nog geassocieerd met deze entry"
## From Ecto.Changeset.validate_length/3
msgid "should be %{count} character(s)"
msgid_plural "should be %{count} character(s)"
msgstr[0] ""
msgstr[1] ""
msgstr[0] "dient %{count} karakter te bevatten"
msgstr[1] "dient %{count} karakters te bevatten"
msgid "should have %{count} item(s)"
msgid_plural "should have %{count} item(s)"
msgstr[0] ""
msgstr[1] ""
msgstr[0] "dient %{count} item te bevatten"
msgstr[1] "dient %{count} items te bevatten"
msgid "should be at least %{count} character(s)"
msgid_plural "should be at least %{count} character(s)"
msgstr[0] ""
msgstr[1] ""
msgstr[0] "dient ten minste %{count} karakter te bevatten"
msgstr[1] "dient ten minste %{count} karakters te bevatten"
msgid "should have at least %{count} item(s)"
msgid_plural "should have at least %{count} item(s)"
msgstr[0] ""
msgstr[1] ""
msgstr[0] "dient ten minste %{count} item te bevatten"
msgstr[1] "dient ten minste %{count} items te bevatten"
msgid "should be at most %{count} character(s)"
msgid_plural "should be at most %{count} character(s)"
msgstr[0] ""
msgstr[1] ""
msgstr[0] "dient niet meer dan %{count} karakter te bevatten"
msgstr[1] "dient niet meer dan %{count} karakters te bevatten"
msgid "should have at most %{count} item(s)"
msgid_plural "should have at most %{count} item(s)"
msgstr[0] ""
msgstr[1] ""
msgstr[0] "dient niet meer dan %{count} item te bevatten"
msgstr[1] "dient niet meer dan %{count} items te bevatten"
## From Ecto.Changeset.validate_number/3
msgid "must be less than %{number}"
msgstr ""
msgstr "dient kleiner te zijn dan %{number}"
msgid "must be greater than %{number}"
msgstr ""
msgstr "dient groter te zijn dan %{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}"
msgstr ""
msgstr "dient groter dan of gelijk te zijn aan %{number}"
msgid "must be equal to %{number}"
msgstr ""
msgstr "dient gelijk te zijn aan %{number}"
#: lib/pleroma/web/common_api/common_api.ex:421
#, elixir-format
msgid "Account not found"
msgstr ""
msgstr "Account niet gevonden"
#: lib/pleroma/web/common_api/common_api.ex:249
#, elixir-format
msgid "Already voted"
msgstr ""
msgstr "Al gestemd"
#: lib/pleroma/web/oauth/oauth_controller.ex:360
#, elixir-format
msgid "Bad request"
msgstr ""
msgstr "Bad request"
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:425
#, elixir-format
msgid "Can't delete object"
msgstr ""
msgstr "Object kan niet verwijderd worden"
#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:196
#, elixir-format
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:101
#, elixir-format
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:254
#, elixir-format
msgid "Can't find user"
msgstr ""
msgstr "Gebruiker kan niet gevonden worden"
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:114
#, elixir-format
msgid "Can't get favorites"
msgstr ""
msgstr "Favorieten konden niet opgehaald worden"
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:437
#, elixir-format
msgid "Can't like object"
msgstr ""
msgstr "Object kan niet geliked worden"
#: lib/pleroma/web/common_api/utils.ex:556
#, elixir-format
msgid "Cannot post an empty status without attachments"
msgstr ""
msgstr "Status kan niet geplaatst worden zonder tekst of bijlagen"
#: lib/pleroma/web/common_api/utils.ex:504
#, elixir-format
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
#, elixir-format

View file

@ -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
View 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
View 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()
})
})
})()

View file

@ -21,7 +21,7 @@ defmodule Pleroma.PaginationTest do
id = Enum.at(notes, 2).id |> Integer.to_string()
%{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 total == 5
@ -31,7 +31,7 @@ defmodule Pleroma.PaginationTest do
id = Enum.at(notes, 2).id |> Integer.to_string()
%{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 total == 5
@ -41,7 +41,7 @@ defmodule Pleroma.PaginationTest do
id = Enum.at(notes, 1).id |> Integer.to_string()
%{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 total == 5
@ -50,7 +50,7 @@ defmodule Pleroma.PaginationTest do
test "paginates by min_id & limit", %{notes: notes} do
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
end
@ -64,13 +64,13 @@ defmodule Pleroma.PaginationTest do
end
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
end
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
end

View file

@ -62,10 +62,11 @@ defmodule Mix.Tasks.Pleroma.RelayTest do
[undo_activity] =
ActivityPub.fetch_activities([], %{
"type" => "Undo",
"actor_id" => follower_id,
"limit" => 1,
"skip_preload" => true
type: "Undo",
actor_id: follower_id,
limit: 1,
skip_preload: true,
invisible_actors: true
})
assert undo_activity.data["type"] == "Undo"

View file

@ -1122,7 +1122,7 @@ defmodule Pleroma.UserTest do
assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] ==
ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
"user" => user2
user: user2
})
{:ok, _user} = User.deactivate(user)
@ -1132,7 +1132,7 @@ defmodule Pleroma.UserTest do
assert [] ==
ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
"user" => user2
user: user2
})
end
end
@ -1159,6 +1159,9 @@ defmodule Pleroma.UserTest do
follower = insert(: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)
activity = insert(:note_activity, user: user, note: object)
@ -1177,6 +1180,8 @@ defmodule Pleroma.UserTest do
refute User.following?(follower, user)
assert %{deactivated: true} = User.get_by_id(user.id)
assert [] == User.get_follow_requests(locked_user)
user_activities =
user.ap_id
|> Activity.Queries.by_actor()

View file

@ -804,17 +804,63 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
end
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
user = insert(:user)
outbox_endpoint = user.ap_id <> "/outbox"
conn =
conn
|> assign(:user, user)
|> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}/outbox")
|> get(outbox_endpoint)
result = json_response(conn, 200)
assert user.ap_id <> "/outbox" == result["id"]
assert outbox_endpoint == result["id"]
end
test "it returns a note activity in a collection", %{conn: conn} do

View file

@ -82,30 +82,28 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
activities =
ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id})
activities = ActivityPub.fetch_activities([], %{visibility: "direct", actor_id: user.ap_id})
assert activities == [direct_activity]
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]
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]
activities =
ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id})
activities = ActivityPub.fetch_activities([], %{visibility: "public", actor_id: user.ap_id})
assert activities == [public_activity]
activities =
ActivityPub.fetch_activities([], %{
:visibility => ~w[private public],
"actor_id" => user.ap_id
visibility: ~w[private public],
actor_id: user.ap_id
})
assert activities == [public_activity, private_activity]
@ -126,8 +124,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
activities =
ActivityPub.fetch_activities([], %{
"exclude_visibilities" => "direct",
"actor_id" => user.ap_id
exclude_visibilities: "direct",
actor_id: user.ap_id
})
assert public_activity in activities
@ -137,8 +135,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
activities =
ActivityPub.fetch_activities([], %{
"exclude_visibilities" => "unlisted",
"actor_id" => user.ap_id
exclude_visibilities: "unlisted",
actor_id: user.ap_id
})
assert public_activity in activities
@ -148,8 +146,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
activities =
ActivityPub.fetch_activities([], %{
"exclude_visibilities" => "private",
"actor_id" => user.ap_id
exclude_visibilities: "private",
actor_id: user.ap_id
})
assert public_activity in activities
@ -159,8 +157,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
activities =
ActivityPub.fetch_activities([], %{
"exclude_visibilities" => "public",
"actor_id" => user.ap_id
exclude_visibilities: "public",
actor_id: user.ap_id
})
refute public_activity in activities
@ -193,23 +191,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"})
{: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 =
ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => ["test", "essais"]})
fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]})
fetch_three =
ActivityPub.fetch_activities([], %{
"type" => "Create",
"tag" => ["test", "essais"],
"tag_reject" => ["reject"]
type: "Create",
tag: ["test", "essais"],
tag_reject: ["reject"]
})
fetch_four =
ActivityPub.fetch_activities([], %{
"type" => "Create",
"tag" => ["test"],
"tag_all" => ["test", "reject"]
type: "Create",
tag: ["test"],
tag_all: ["test", "reject"]
})
assert fetch_one == [status_one, status_three]
@ -375,7 +372,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
_listen_activity_2 = insert(:listen)
_listen_activity_3 = insert(:listen)
timeline = ActivityPub.fetch_activities([], %{"type" => ["Listen"]})
timeline = ActivityPub.fetch_activities([], %{type: ["Listen"]})
assert length(timeline) == 3
end
@ -507,7 +504,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{: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]
end
end
@ -520,8 +517,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
booster = insert(:user)
{:ok, _user_relationship} = User.block(user, %{ap_id: activity_one.data["actor"]})
activities =
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
@ -529,8 +525,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
activities =
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
@ -541,16 +536,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
activity_three = Activity.get_by_id(activity_three.id)
activities =
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
assert Enum.member?(activities, activity_two)
refute Enum.member?(activities, activity_three)
refute Enum.member?(activities, boost_activity)
assert Enum.member?(activities, activity_one)
activities =
ActivityPub.fetch_activities([], %{"blocking_user" => nil, "skip_preload" => true})
activities = ActivityPub.fetch_activities([], %{blocking_user: nil, skip_preload: true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
@ -573,7 +566,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{: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)
refute Enum.member?(activities, activity_two)
@ -595,7 +588,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)
activities =
ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
ActivityPub.fetch_activities([], %{blocking_user: blocker})
|> Enum.map(fn act -> act.id end)
assert Enum.member?(activities, activity_one.id)
@ -611,8 +604,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
user = insert(:user)
{:ok, user} = User.block_domain(user, domain)
activities =
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
refute activity in activities
@ -620,8 +612,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
ActivityPub.follow(user, followed_user)
{:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)
activities =
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
refute repeat_activity in activities
end
@ -641,8 +632,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
activity = insert(:note_activity, %{note: note})
activities =
ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
assert activity in activities
@ -653,8 +643,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
bad_activity = insert(:note_activity, %{note: bad_note})
{:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)
activities =
ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
refute repeat_activity in activities
end
@ -669,8 +658,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
activity_one_actor = User.get_by_ap_id(activity_one.data["actor"])
{:ok, _user_relationships} = User.mute(user, activity_one_actor)
activities =
ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
@ -679,9 +667,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
# Calling with 'with_muted' will deliver muted activities, too.
activities =
ActivityPub.fetch_activities([], %{
"muting_user" => user,
"with_muted" => true,
"skip_preload" => true
muting_user: user,
with_muted: true,
skip_preload: true
})
assert Enum.member?(activities, activity_two)
@ -690,8 +678,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, _user_mute} = User.unmute(user, activity_one_actor)
activities =
ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
@ -703,15 +690,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
activity_three = Activity.get_by_id(activity_three.id)
activities =
ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
assert Enum.member?(activities, activity_two)
refute Enum.member?(activities, activity_three)
refute Enum.member?(activities, boost_activity)
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_three)
@ -727,7 +713,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{: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
test "returns thread muted activities when with_muted is set" do
@ -739,7 +725,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
assert [_activity_two, _activity_one] =
ActivityPub.fetch_activities([], %{"muting_user" => user, "with_muted" => true})
ActivityPub.fetch_activities([], %{muting_user: user, with_muted: true})
end
test "does include announces on request" do
@ -761,7 +747,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{: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
end
@ -804,7 +790,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
expected_activities = ActivityBuilder.insert_list(10)
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 length(activities) == 10
@ -819,7 +805,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|> ActivityBuilder.insert_list()
|> 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 collect_ids(activities) == collect_ids(expected_activities)
@ -831,8 +817,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
later_activities = ActivityBuilder.insert_list(10)
activities =
ActivityPub.fetch_public_activities(%{"page" => "2", "page_size" => "20"}, :offset)
activities = ActivityPub.fetch_public_activities(%{page: "2", page_size: "20"}, :offset)
assert length(activities) == 20
@ -848,7 +833,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{: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)
end
@ -862,7 +847,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{: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)
end
@ -1066,7 +1051,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert length(activities) == 3
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)
assert [public_activity.id, private_activity_1.id] == activities
@ -1115,7 +1100,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
CommonAPI.pin(activity_three.id, 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)
end
@ -1226,7 +1211,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
activity = Repo.preload(activity, :bookmark)
activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
end
def data_uri do
@ -1400,7 +1385,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
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]
end
end
@ -1470,7 +1455,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{: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
@ -1483,11 +1468,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
test "public timeline", %{users: %{u1: user}} do
activities_ids =
%{}
|> Map.put("type", ["Create", "Announce"])
|> Map.put("local_only", false)
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("reply_filtering_user", user)
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:local_only, false)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_filtering_user, user)
|> ActivityPub.fetch_public_activities()
|> Enum.map(& &1.id)
@ -1504,12 +1489,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
} do
activities_ids =
%{}
|> Map.put("type", ["Create", "Announce"])
|> Map.put("local_only", false)
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("reply_visibility", "following")
|> Map.put("reply_filtering_user", user)
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:local_only, false)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_visibility, "following")
|> Map.put(:reply_filtering_user, user)
|> ActivityPub.fetch_public_activities()
|> Enum.map(& &1.id)
@ -1531,12 +1516,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
} do
activities_ids =
%{}
|> Map.put("type", ["Create", "Announce"])
|> Map.put("local_only", false)
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("reply_visibility", "self")
|> Map.put("reply_filtering_user", user)
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:local_only, false)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_visibility, "self")
|> Map.put(:reply_filtering_user, user)
|> ActivityPub.fetch_public_activities()
|> Enum.map(& &1.id)
@ -1555,11 +1540,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
} do
params =
%{}
|> Map.put("type", ["Create", "Announce"])
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)
|> Map.put("reply_filtering_user", user)
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
|> Map.put(:reply_filtering_user, user)
activities_ids =
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
@ -1593,12 +1578,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
} do
params =
%{}
|> Map.put("type", ["Create", "Announce"])
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)
|> Map.put("reply_visibility", "following")
|> Map.put("reply_filtering_user", user)
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
|> Map.put(:reply_visibility, "following")
|> Map.put(:reply_filtering_user, user)
activities_ids =
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
@ -1632,12 +1617,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
} do
params =
%{}
|> Map.put("type", ["Create", "Announce"])
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)
|> Map.put("reply_visibility", "self")
|> Map.put("reply_filtering_user", user)
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
|> Map.put(:reply_visibility, "self")
|> Map.put(:reply_filtering_user, user)
activities_ids =
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
@ -1666,11 +1651,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
test "public timeline", %{users: %{u1: user}} do
activities_ids =
%{}
|> Map.put("type", ["Create", "Announce"])
|> Map.put("local_only", false)
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:local_only, false)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
|> ActivityPub.fetch_public_activities()
|> Enum.map(& &1.id)
@ -1680,13 +1665,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
activities_ids =
%{}
|> Map.put("type", ["Create", "Announce"])
|> Map.put("local_only", false)
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("reply_visibility", "following")
|> Map.put("reply_filtering_user", user)
|> Map.put("user", user)
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:local_only, false)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_visibility, "following")
|> Map.put(:reply_filtering_user, user)
|> Map.put(:user, user)
|> ActivityPub.fetch_public_activities()
|> Enum.map(& &1.id)
@ -1696,13 +1681,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
activities_ids =
%{}
|> Map.put("type", ["Create", "Announce"])
|> Map.put("local_only", false)
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("reply_visibility", "self")
|> Map.put("reply_filtering_user", user)
|> Map.put("user", user)
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:local_only, false)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_visibility, "self")
|> Map.put(:reply_filtering_user, user)
|> Map.put(:user, user)
|> ActivityPub.fetch_public_activities()
|> Enum.map(& &1.id)
@ -1712,10 +1697,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
test "home timeline", %{users: %{u1: user}} do
params =
%{}
|> Map.put("type", ["Create", "Announce"])
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
activities_ids =
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
@ -1727,12 +1712,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
params =
%{}
|> Map.put("type", ["Create", "Announce"])
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)
|> Map.put("reply_visibility", "following")
|> Map.put("reply_filtering_user", user)
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
|> Map.put(:reply_visibility, "following")
|> Map.put(:reply_filtering_user, user)
activities_ids =
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
@ -1751,12 +1736,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
} do
params =
%{}
|> Map.put("type", ["Create", "Announce"])
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)
|> Map.put("reply_visibility", "self")
|> Map.put("reply_filtering_user", user)
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
|> Map.put(:reply_visibility, "self")
|> Map.put(:reply_filtering_user, user)
activities_ids =
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)

View file

@ -158,35 +158,4 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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

View 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

View 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

View 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

View file

@ -42,6 +42,14 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
|> json_response_and_validate_schema(200)
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

View file

@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
use Pleroma.Web.ConnCase
import Mock
import Pleroma.Factory
setup do: clear_config([:instance, :max_account_fields])
@ -52,24 +53,31 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
user = Repo.get(User, user_data["id"])
res_conn =
conn
|> assign(:user, user)
|> patch("/api/v1/accounts/update_credentials", %{
"pleroma_settings_store" => %{
masto_fe: %{
theme: "blub"
clear_config([:instance, :federating], true)
with_mock Pleroma.Web.Federator,
publish: fn _activity -> :ok end do
res_conn =
conn
|> assign(:user, user)
|> patch("/api/v1/accounts/update_credentials", %{
"pleroma_settings_store" => %{
masto_fe: %{
theme: "blub"
}
}
}
})
})
assert user_data = json_response_and_validate_schema(res_conn, 200)
assert user_data = json_response_and_validate_schema(res_conn, 200)
assert user_data["pleroma"]["settings_store"] ==
%{
"pleroma_fe" => %{"theme" => "bla"},
"masto_fe" => %{"theme" => "blub"}
}
assert user_data["pleroma"]["settings_store"] ==
%{
"pleroma_fe" => %{"theme" => "bla"},
"masto_fe" => %{"theme" => "blub"}
}
assert_called(Pleroma.Web.Federator.publish(:_))
end
end
test "updates the user's bio", %{conn: conn} do

View file

@ -71,10 +71,48 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
get(conn, "/api/v2/search?q=天子")
|> json_response_and_validate_schema(200)
assert results["hashtags"] == [
%{"name" => "天子", "url" => "#{Web.base_url()}/tag/天子"}
]
[status] = results["statuses"]
assert status["id"] == to_string(activity.id)
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
user = insert(:user)
user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"})
@ -179,7 +217,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
[account | _] = results["accounts"]
assert account["id"] == to_string(user_three.id)
assert results["hashtags"] == []
assert results["hashtags"] == ["2hu"]
[status] = results["statuses"]
assert status["id"] == to_string(activity.id)

View file

@ -60,9 +60,9 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
describe "public" do
@tag capture_log: true
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)
@ -77,6 +77,13 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
conn = get(build_conn(), "/api/v1/timelines/public?local=1")
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
test "the public timeline includes only public statuses for an authenticated user" do
@ -90,6 +97,49 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
res_conn = get(conn, "/api/v1/timelines/public")
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
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
defp local_and_remote_activities do