Merge branch 'develop' into tests/mastodon_api_controller.ex
This commit is contained in:
commit
1053319cd6
56 changed files with 3986 additions and 2794 deletions
|
|
@ -13,6 +13,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
alias Pleroma.Object.Fetcher
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.SubscriptionNotification
|
||||
alias Pleroma.Upload
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
|
|
@ -151,6 +152,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
|
||||
|
||||
Notification.create_notifications(activity)
|
||||
SubscriptionNotification.create_notifications(activity)
|
||||
|
||||
participations =
|
||||
activity
|
||||
|
|
|
|||
|
|
@ -513,7 +513,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> render("status.json", %{activity: activity})
|
||||
|> render("show.json", %{activity: activity})
|
||||
else
|
||||
true ->
|
||||
{:param_cast, nil}
|
||||
|
|
@ -537,7 +537,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> render("status.json", %{activity: activity})
|
||||
|> render("show.json", %{activity: activity})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
219
lib/pleroma/web/common_api/activity_draft.ex
Normal file
219
lib/pleroma/web/common_api/activity_draft.ex
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
|
||||
import Pleroma.Web.Gettext
|
||||
|
||||
defstruct valid?: true,
|
||||
errors: [],
|
||||
user: nil,
|
||||
params: %{},
|
||||
status: nil,
|
||||
summary: nil,
|
||||
full_payload: nil,
|
||||
attachments: [],
|
||||
in_reply_to: nil,
|
||||
in_reply_to_conversation: nil,
|
||||
visibility: nil,
|
||||
expires_at: nil,
|
||||
poll: nil,
|
||||
emoji: %{},
|
||||
content_html: nil,
|
||||
mentions: [],
|
||||
tags: [],
|
||||
to: [],
|
||||
cc: [],
|
||||
context: nil,
|
||||
sensitive: false,
|
||||
object: nil,
|
||||
preview?: false,
|
||||
changes: %{}
|
||||
|
||||
def create(user, params) do
|
||||
%__MODULE__{user: user}
|
||||
|> put_params(params)
|
||||
|> status()
|
||||
|> summary()
|
||||
|> with_valid(&attachments/1)
|
||||
|> full_payload()
|
||||
|> expires_at()
|
||||
|> poll()
|
||||
|> with_valid(&in_reply_to/1)
|
||||
|> with_valid(&in_reply_to_conversation/1)
|
||||
|> with_valid(&visibility/1)
|
||||
|> content()
|
||||
|> with_valid(&to_and_cc/1)
|
||||
|> with_valid(&context/1)
|
||||
|> sensitive()
|
||||
|> with_valid(&object/1)
|
||||
|> preview?()
|
||||
|> with_valid(&changes/1)
|
||||
|> validate()
|
||||
end
|
||||
|
||||
defp put_params(draft, params) do
|
||||
params = Map.put_new(params, "in_reply_to_status_id", params["in_reply_to_id"])
|
||||
%__MODULE__{draft | params: params}
|
||||
end
|
||||
|
||||
defp status(%{params: %{"status" => status}} = draft) do
|
||||
%__MODULE__{draft | status: String.trim(status)}
|
||||
end
|
||||
|
||||
defp summary(%{params: params} = draft) do
|
||||
%__MODULE__{draft | summary: Map.get(params, "spoiler_text", "")}
|
||||
end
|
||||
|
||||
defp full_payload(%{status: status, summary: summary} = draft) do
|
||||
full_payload = String.trim(status <> summary)
|
||||
|
||||
case Utils.validate_character_limit(full_payload, draft.attachments) do
|
||||
:ok -> %__MODULE__{draft | full_payload: full_payload}
|
||||
{:error, message} -> add_error(draft, message)
|
||||
end
|
||||
end
|
||||
|
||||
defp attachments(%{params: params} = draft) do
|
||||
attachments = Utils.attachments_from_ids(params)
|
||||
%__MODULE__{draft | attachments: attachments}
|
||||
end
|
||||
|
||||
defp in_reply_to(draft) do
|
||||
case Map.get(draft.params, "in_reply_to_status_id") do
|
||||
"" -> draft
|
||||
nil -> draft
|
||||
id -> %__MODULE__{draft | in_reply_to: Activity.get_by_id(id)}
|
||||
end
|
||||
end
|
||||
|
||||
defp in_reply_to_conversation(draft) do
|
||||
in_reply_to_conversation = Participation.get(draft.params["in_reply_to_conversation_id"])
|
||||
%__MODULE__{draft | in_reply_to_conversation: in_reply_to_conversation}
|
||||
end
|
||||
|
||||
defp visibility(%{params: params} = draft) do
|
||||
case CommonAPI.get_visibility(params, draft.in_reply_to, draft.in_reply_to_conversation) do
|
||||
{visibility, "direct"} when visibility != "direct" ->
|
||||
add_error(draft, dgettext("errors", "The message visibility must be direct"))
|
||||
|
||||
{visibility, _} ->
|
||||
%__MODULE__{draft | visibility: visibility}
|
||||
end
|
||||
end
|
||||
|
||||
defp expires_at(draft) do
|
||||
case CommonAPI.check_expiry_date(draft.params["expires_in"]) do
|
||||
{:ok, expires_at} -> %__MODULE__{draft | expires_at: expires_at}
|
||||
{:error, message} -> add_error(draft, message)
|
||||
end
|
||||
end
|
||||
|
||||
defp poll(draft) do
|
||||
case Utils.make_poll_data(draft.params) do
|
||||
{:ok, {poll, poll_emoji}} ->
|
||||
%__MODULE__{draft | poll: poll, emoji: Map.merge(draft.emoji, poll_emoji)}
|
||||
|
||||
{:error, message} ->
|
||||
add_error(draft, message)
|
||||
end
|
||||
end
|
||||
|
||||
defp content(draft) do
|
||||
{content_html, mentions, tags} =
|
||||
Utils.make_content_html(
|
||||
draft.status,
|
||||
draft.attachments,
|
||||
draft.params,
|
||||
draft.visibility
|
||||
)
|
||||
|
||||
%__MODULE__{draft | content_html: content_html, mentions: mentions, tags: tags}
|
||||
end
|
||||
|
||||
defp to_and_cc(draft) do
|
||||
addressed_users =
|
||||
draft.mentions
|
||||
|> Enum.map(fn {_, mentioned_user} -> mentioned_user.ap_id end)
|
||||
|> Utils.get_addressed_users(draft.params["to"])
|
||||
|
||||
{to, cc} =
|
||||
Utils.get_to_and_cc(
|
||||
draft.user,
|
||||
addressed_users,
|
||||
draft.in_reply_to,
|
||||
draft.visibility,
|
||||
draft.in_reply_to_conversation
|
||||
)
|
||||
|
||||
%__MODULE__{draft | to: to, cc: cc}
|
||||
end
|
||||
|
||||
defp context(draft) do
|
||||
context = Utils.make_context(draft.in_reply_to, draft.in_reply_to_conversation)
|
||||
%__MODULE__{draft | context: context}
|
||||
end
|
||||
|
||||
defp sensitive(draft) do
|
||||
sensitive = draft.params["sensitive"] || Enum.member?(draft.tags, {"#nsfw", "nsfw"})
|
||||
%__MODULE__{draft | sensitive: sensitive}
|
||||
end
|
||||
|
||||
defp object(draft) do
|
||||
emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji)
|
||||
|
||||
object =
|
||||
Utils.make_note_data(
|
||||
draft.user.ap_id,
|
||||
draft.to,
|
||||
draft.context,
|
||||
draft.content_html,
|
||||
draft.attachments,
|
||||
draft.in_reply_to,
|
||||
draft.tags,
|
||||
draft.summary,
|
||||
draft.cc,
|
||||
draft.sensitive,
|
||||
draft.poll
|
||||
)
|
||||
|> Map.put("emoji", emoji)
|
||||
|
||||
%__MODULE__{draft | object: object}
|
||||
end
|
||||
|
||||
defp preview?(draft) do
|
||||
preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params["preview"]) || false
|
||||
%__MODULE__{draft | preview?: preview?}
|
||||
end
|
||||
|
||||
defp changes(draft) do
|
||||
direct? = draft.visibility == "direct"
|
||||
|
||||
changes =
|
||||
%{
|
||||
to: draft.to,
|
||||
actor: draft.user,
|
||||
context: draft.context,
|
||||
object: draft.object,
|
||||
additional: %{"cc" => draft.cc, "directMessage" => direct?}
|
||||
}
|
||||
|> Utils.maybe_add_list_data(draft.user, draft.visibility)
|
||||
|
||||
%__MODULE__{draft | changes: changes}
|
||||
end
|
||||
|
||||
defp with_valid(%{valid?: true} = draft, func), do: func.(draft)
|
||||
defp with_valid(draft, _func), do: draft
|
||||
|
||||
defp add_error(draft, message) do
|
||||
%__MODULE__{draft | valid?: false, errors: [message | draft.errors]}
|
||||
end
|
||||
|
||||
defp validate(%{valid?: true} = draft), do: {:ok, draft}
|
||||
defp validate(%{errors: [message | _]}), do: {:error, message}
|
||||
end
|
||||
|
|
@ -6,7 +6,6 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
alias Pleroma.Activity
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.ThreadMute
|
||||
alias Pleroma.User
|
||||
|
|
@ -18,14 +17,11 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
import Pleroma.Web.CommonAPI.Utils
|
||||
|
||||
def follow(follower, followed) do
|
||||
timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout])
|
||||
|
||||
with {:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
||||
{:ok, activity} <- ActivityPub.follow(follower, followed),
|
||||
{:ok, follower, followed} <-
|
||||
User.wait_and_refresh(
|
||||
Pleroma.Config.get([:activitypub, :follow_handshake_timeout]),
|
||||
follower,
|
||||
followed
|
||||
) do
|
||||
{:ok, follower, followed} <- User.wait_and_refresh(timeout, follower, followed) do
|
||||
{:ok, follower, followed, activity}
|
||||
end
|
||||
end
|
||||
|
|
@ -76,8 +72,7 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
{:ok, delete} <- ActivityPub.delete(object) do
|
||||
{:ok, delete}
|
||||
else
|
||||
_ ->
|
||||
{:error, dgettext("errors", "Could not delete")}
|
||||
_ -> {:error, dgettext("errors", "Could not delete")}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -87,18 +82,16 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
nil <- Utils.get_existing_announce(user.ap_id, object) do
|
||||
ActivityPub.announce(user, object)
|
||||
else
|
||||
_ ->
|
||||
{:error, dgettext("errors", "Could not repeat")}
|
||||
_ -> {:error, dgettext("errors", "Could not repeat")}
|
||||
end
|
||||
end
|
||||
|
||||
def unrepeat(id_or_ap_id, user) do
|
||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||
object <- Object.normalize(activity) do
|
||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id) do
|
||||
object = Object.normalize(activity)
|
||||
ActivityPub.unannounce(user, object)
|
||||
else
|
||||
_ ->
|
||||
{:error, dgettext("errors", "Could not unrepeat")}
|
||||
_ -> {:error, dgettext("errors", "Could not unrepeat")}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -108,30 +101,23 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
nil <- Utils.get_existing_like(user.ap_id, object) do
|
||||
ActivityPub.like(user, object)
|
||||
else
|
||||
_ ->
|
||||
{:error, dgettext("errors", "Could not favorite")}
|
||||
_ -> {:error, dgettext("errors", "Could not favorite")}
|
||||
end
|
||||
end
|
||||
|
||||
def unfavorite(id_or_ap_id, user) do
|
||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||
object <- Object.normalize(activity) do
|
||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id) do
|
||||
object = Object.normalize(activity)
|
||||
ActivityPub.unlike(user, object)
|
||||
else
|
||||
_ ->
|
||||
{:error, dgettext("errors", "Could not unfavorite")}
|
||||
_ -> {:error, dgettext("errors", "Could not unfavorite")}
|
||||
end
|
||||
end
|
||||
|
||||
def vote(user, object, choices) do
|
||||
with "Question" <- object.data["type"],
|
||||
{:author, false} <- {:author, object.data["actor"] == user.ap_id},
|
||||
{:existing_votes, []} <- {:existing_votes, Utils.get_existing_votes(user.ap_id, object)},
|
||||
{options, max_count} <- get_options_and_max_count(object),
|
||||
option_count <- Enum.count(options),
|
||||
{:choice_check, {choices, true}} <-
|
||||
{:choice_check, normalize_and_validate_choice_indices(choices, option_count)},
|
||||
{:count_check, true} <- {:count_check, Enum.count(choices) <= max_count} do
|
||||
def vote(user, %{data: %{"type" => "Question"}} = object, choices) do
|
||||
with :ok <- validate_not_author(object, user),
|
||||
:ok <- validate_existing_votes(user, object),
|
||||
{:ok, options, choices} <- normalize_and_validate_choices(choices, object) do
|
||||
answer_activities =
|
||||
Enum.map(choices, fn index ->
|
||||
answer_data = make_answer_data(user, object, Enum.at(options, index)["name"])
|
||||
|
|
@ -150,32 +136,40 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
|
||||
object = Object.get_cached_by_ap_id(object.data["id"])
|
||||
{:ok, answer_activities, object}
|
||||
else
|
||||
{:author, _} -> {:error, dgettext("errors", "Poll's author can't vote")}
|
||||
{:existing_votes, _} -> {:error, dgettext("errors", "Already voted")}
|
||||
{:choice_check, {_, false}} -> {:error, dgettext("errors", "Invalid indices")}
|
||||
{:count_check, false} -> {:error, dgettext("errors", "Too many choices")}
|
||||
end
|
||||
end
|
||||
|
||||
defp get_options_and_max_count(object) do
|
||||
if Map.has_key?(object.data, "anyOf") do
|
||||
{object.data["anyOf"], Enum.count(object.data["anyOf"])}
|
||||
defp validate_not_author(%{data: %{"actor" => ap_id}}, %{ap_id: ap_id}),
|
||||
do: {:error, dgettext("errors", "Poll's author can't vote")}
|
||||
|
||||
defp validate_not_author(_, _), do: :ok
|
||||
|
||||
defp validate_existing_votes(%{ap_id: ap_id}, object) do
|
||||
if Utils.get_existing_votes(ap_id, object) == [] do
|
||||
:ok
|
||||
else
|
||||
{object.data["oneOf"], 1}
|
||||
{:error, dgettext("errors", "Already voted")}
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_and_validate_choice_indices(choices, count) do
|
||||
Enum.map_reduce(choices, true, fn index, valid ->
|
||||
index = if is_binary(index), do: String.to_integer(index), else: index
|
||||
{index, if(valid, do: index < count, else: valid)}
|
||||
end)
|
||||
defp get_options_and_max_count(%{data: %{"anyOf" => any_of}}), do: {any_of, Enum.count(any_of)}
|
||||
defp get_options_and_max_count(%{data: %{"oneOf" => one_of}}), do: {one_of, 1}
|
||||
|
||||
defp normalize_and_validate_choices(choices, object) do
|
||||
choices = Enum.map(choices, fn i -> if is_binary(i), do: String.to_integer(i), else: i end)
|
||||
{options, max_count} = get_options_and_max_count(object)
|
||||
count = Enum.count(options)
|
||||
|
||||
with {_, true} <- {:valid_choice, Enum.all?(choices, &(&1 < count))},
|
||||
{_, true} <- {:count_check, Enum.count(choices) <= max_count} do
|
||||
{:ok, options, choices}
|
||||
else
|
||||
{:valid_choice, _} -> {:error, dgettext("errors", "Invalid indices")}
|
||||
{:count_check, _} -> {:error, dgettext("errors", "Too many choices")}
|
||||
end
|
||||
end
|
||||
|
||||
def get_visibility(_, _, %Participation{}) do
|
||||
{"direct", "direct"}
|
||||
end
|
||||
def get_visibility(_, _, %Participation{}), do: {"direct", "direct"}
|
||||
|
||||
def get_visibility(%{"visibility" => visibility}, in_reply_to, _)
|
||||
when visibility in ~w{public unlisted private direct},
|
||||
|
|
@ -197,13 +191,13 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
|
||||
def get_replied_to_visibility(activity) do
|
||||
with %Object{} = object <- Object.normalize(activity) do
|
||||
Pleroma.Web.ActivityPub.Visibility.get_visibility(object)
|
||||
Visibility.get_visibility(object)
|
||||
end
|
||||
end
|
||||
|
||||
defp check_expiry_date({:ok, nil} = res), do: res
|
||||
def check_expiry_date({:ok, nil} = res), do: res
|
||||
|
||||
defp check_expiry_date({:ok, in_seconds}) do
|
||||
def check_expiry_date({:ok, in_seconds}) do
|
||||
expiry = NaiveDateTime.utc_now() |> NaiveDateTime.add(in_seconds)
|
||||
|
||||
if ActivityExpiration.expires_late_enough?(expiry) do
|
||||
|
|
@ -213,107 +207,36 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
end
|
||||
end
|
||||
|
||||
defp check_expiry_date(expiry_str) do
|
||||
def check_expiry_date(expiry_str) do
|
||||
Ecto.Type.cast(:integer, expiry_str)
|
||||
|> check_expiry_date()
|
||||
end
|
||||
|
||||
def post(user, %{"status" => status} = data) do
|
||||
limit = Pleroma.Config.get([:instance, :limit])
|
||||
|
||||
with status <- String.trim(status),
|
||||
attachments <- attachments_from_ids(data),
|
||||
in_reply_to <- get_replied_to_activity(data["in_reply_to_status_id"]),
|
||||
in_reply_to_conversation <- Participation.get(data["in_reply_to_conversation_id"]),
|
||||
{visibility, in_reply_to_visibility} <-
|
||||
get_visibility(data, in_reply_to, in_reply_to_conversation),
|
||||
{_, false} <-
|
||||
{:private_to_public, in_reply_to_visibility == "direct" && visibility != "direct"},
|
||||
{content_html, mentions, tags} <-
|
||||
make_content_html(
|
||||
status,
|
||||
attachments,
|
||||
data,
|
||||
visibility
|
||||
),
|
||||
mentioned_users <- for({_, mentioned_user} <- mentions, do: mentioned_user.ap_id),
|
||||
addressed_users <- get_addressed_users(mentioned_users, data["to"]),
|
||||
{poll, poll_emoji} <- make_poll_data(data),
|
||||
{to, cc} <-
|
||||
get_to_and_cc(user, addressed_users, in_reply_to, visibility, in_reply_to_conversation),
|
||||
context <- make_context(in_reply_to, in_reply_to_conversation),
|
||||
cw <- data["spoiler_text"] || "",
|
||||
sensitive <- data["sensitive"] || Enum.member?(tags, {"#nsfw", "nsfw"}),
|
||||
{:ok, expires_at} <- check_expiry_date(data["expires_in"]),
|
||||
full_payload <- String.trim(status <> cw),
|
||||
:ok <- validate_character_limit(full_payload, attachments, limit),
|
||||
object <-
|
||||
make_note_data(
|
||||
user.ap_id,
|
||||
to,
|
||||
context,
|
||||
content_html,
|
||||
attachments,
|
||||
in_reply_to,
|
||||
tags,
|
||||
cw,
|
||||
cc,
|
||||
sensitive,
|
||||
poll
|
||||
),
|
||||
object <- put_emoji(object, full_payload, poll_emoji) do
|
||||
preview? = Pleroma.Web.ControllerHelper.truthy_param?(data["preview"]) || false
|
||||
direct? = visibility == "direct"
|
||||
|
||||
result =
|
||||
%{
|
||||
to: to,
|
||||
actor: user,
|
||||
context: context,
|
||||
object: object,
|
||||
additional: %{"cc" => cc, "directMessage" => direct?}
|
||||
}
|
||||
|> maybe_add_list_data(user, visibility)
|
||||
|> ActivityPub.create(preview?)
|
||||
|
||||
if expires_at do
|
||||
with {:ok, activity} <- result do
|
||||
{:ok, _} = ActivityExpiration.create(activity, expires_at)
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
else
|
||||
{:private_to_public, true} ->
|
||||
{:error, dgettext("errors", "The message visibility must be direct")}
|
||||
|
||||
{:error, _} = e ->
|
||||
e
|
||||
|
||||
e ->
|
||||
{:error, e}
|
||||
def post(user, %{"status" => _} = data) do
|
||||
with {:ok, draft} <- Pleroma.Web.CommonAPI.ActivityDraft.create(user, data) do
|
||||
draft.changes
|
||||
|> ActivityPub.create(draft.preview?)
|
||||
|> maybe_create_activity_expiration(draft.expires_at)
|
||||
end
|
||||
end
|
||||
|
||||
# parse and put emoji to object data
|
||||
defp put_emoji(map, text, emojis) do
|
||||
Map.put(
|
||||
map,
|
||||
"emoji",
|
||||
Map.merge(Emoji.Formatter.get_emoji_map(text), emojis)
|
||||
)
|
||||
defp maybe_create_activity_expiration({:ok, activity}, %NaiveDateTime{} = expires_at) do
|
||||
with {:ok, _} <- ActivityExpiration.create(activity, expires_at) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_create_activity_expiration(result, _), do: result
|
||||
|
||||
# Updates the emojis for a user based on their profile
|
||||
def update(user) do
|
||||
emoji = emoji_from_profile(user)
|
||||
source_data = user.info |> Map.get(:source_data, {}) |> Map.put("tag", emoji)
|
||||
source_data = user.info |> Map.get(:source_data, %{}) |> Map.put("tag", emoji)
|
||||
|
||||
user =
|
||||
with {:ok, user} <- User.update_info(user, &User.Info.set_source_data(&1, source_data)) do
|
||||
user
|
||||
else
|
||||
_e -> user
|
||||
case User.update_info(user, &User.Info.set_source_data(&1, source_data)) do
|
||||
{:ok, user} -> user
|
||||
_ -> user
|
||||
end
|
||||
|
||||
ActivityPub.update(%{
|
||||
|
|
@ -328,14 +251,8 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do
|
||||
with %Activity{
|
||||
actor: ^user_ap_id,
|
||||
data: %{
|
||||
"type" => "Create"
|
||||
},
|
||||
object: %Object{
|
||||
data: %{
|
||||
"type" => "Note"
|
||||
}
|
||||
}
|
||||
data: %{"type" => "Create"},
|
||||
object: %Object{data: %{"type" => "Note"}}
|
||||
} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||
true <- Visibility.is_public?(activity),
|
||||
{:ok, _user} <- User.update_info(user, &User.Info.add_pinnned_activity(&1, activity)) do
|
||||
|
|
@ -372,51 +289,46 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
def thread_muted?(%{id: nil} = _user, _activity), do: false
|
||||
|
||||
def thread_muted?(user, activity) do
|
||||
with [] <- ThreadMute.check_muted(user.id, activity.data["context"]) do
|
||||
false
|
||||
else
|
||||
_ -> true
|
||||
ThreadMute.check_muted(user.id, activity.data["context"]) != []
|
||||
end
|
||||
|
||||
def report(user, %{"account_id" => account_id} = data) do
|
||||
with {:ok, account} <- get_reported_account(account_id),
|
||||
{:ok, {content_html, _, _}} <- make_report_content_html(data["comment"]),
|
||||
{:ok, statuses} <- get_report_statuses(account, data) do
|
||||
ActivityPub.flag(%{
|
||||
context: Utils.generate_context_id(),
|
||||
actor: user,
|
||||
account: account,
|
||||
statuses: statuses,
|
||||
content: content_html,
|
||||
forward: data["forward"] || false
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def report(user, data) do
|
||||
with {:account_id, %{"account_id" => account_id}} <- {:account_id, data},
|
||||
{:account, %User{} = account} <- {:account, User.get_cached_by_id(account_id)},
|
||||
{:ok, {content_html, _, _}} <- make_report_content_html(data["comment"]),
|
||||
{:ok, statuses} <- get_report_statuses(account, data),
|
||||
{:ok, activity} <-
|
||||
ActivityPub.flag(%{
|
||||
context: Utils.generate_context_id(),
|
||||
actor: user,
|
||||
account: account,
|
||||
statuses: statuses,
|
||||
content: content_html,
|
||||
forward: data["forward"] || false
|
||||
}) do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:error, err} -> {:error, err}
|
||||
{:account_id, %{}} -> {:error, dgettext("errors", "Valid `account_id` required")}
|
||||
{:account, nil} -> {:error, dgettext("errors", "Account not found")}
|
||||
def report(_user, _params), do: {:error, dgettext("errors", "Valid `account_id` required")}
|
||||
|
||||
defp get_reported_account(account_id) do
|
||||
case User.get_cached_by_id(account_id) do
|
||||
%User{} = account -> {:ok, account}
|
||||
_ -> {:error, dgettext("errors", "Account not found")}
|
||||
end
|
||||
end
|
||||
|
||||
def update_report_state(activity_id, state) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(activity_id),
|
||||
{:ok, activity} <- Utils.update_report_state(activity, state) do
|
||||
{:ok, activity}
|
||||
with %Activity{} = activity <- Activity.get_by_id(activity_id) do
|
||||
Utils.update_report_state(activity, state)
|
||||
else
|
||||
nil -> {:error, :not_found}
|
||||
{:error, reason} -> {:error, reason}
|
||||
_ -> {:error, dgettext("errors", "Could not update state")}
|
||||
end
|
||||
end
|
||||
|
||||
def update_activity_scope(activity_id, opts \\ %{}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
|
||||
{:ok, activity} <- toggle_sensitive(activity, opts),
|
||||
{:ok, activity} <- set_visibility(activity, opts) do
|
||||
{:ok, activity}
|
||||
{:ok, activity} <- toggle_sensitive(activity, opts) do
|
||||
set_visibility(activity, opts)
|
||||
else
|
||||
nil -> {:error, :not_found}
|
||||
{:error, reason} -> {:error, reason}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.CommonAPI.Utils do
|
||||
import Pleroma.Web.Gettext
|
||||
import Pleroma.Web.ControllerHelper, only: [truthy_param?: 1]
|
||||
|
||||
alias Calendar.Strftime
|
||||
alias Pleroma.Activity
|
||||
|
|
@ -41,14 +42,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
end
|
||||
end
|
||||
|
||||
def get_replied_to_activity(""), do: nil
|
||||
|
||||
def get_replied_to_activity(id) when not is_nil(id) do
|
||||
Activity.get_by_id(id)
|
||||
end
|
||||
|
||||
def get_replied_to_activity(_), do: nil
|
||||
|
||||
def attachments_from_ids(%{"media_ids" => ids, "descriptions" => desc} = _) do
|
||||
attachments_from_ids_descs(ids, desc)
|
||||
end
|
||||
|
|
@ -159,70 +152,74 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
|
||||
def maybe_add_list_data(activity_params, _, _), do: activity_params
|
||||
|
||||
def make_poll_data(%{"poll" => %{"expires_in" => expires_in}} = data)
|
||||
when is_binary(expires_in) do
|
||||
# In some cases mastofe sends out strings instead of integers
|
||||
data
|
||||
|> put_in(["poll", "expires_in"], String.to_integer(expires_in))
|
||||
|> make_poll_data()
|
||||
end
|
||||
|
||||
def make_poll_data(%{"poll" => %{"options" => options, "expires_in" => expires_in}} = data)
|
||||
when is_list(options) do
|
||||
%{max_expiration: max_expiration, min_expiration: min_expiration} =
|
||||
limits = Pleroma.Config.get([:instance, :poll_limits])
|
||||
limits = Pleroma.Config.get([:instance, :poll_limits])
|
||||
|
||||
# XXX: There is probably a cleaner way of doing this
|
||||
try do
|
||||
# In some cases mastofe sends out strings instead of integers
|
||||
expires_in = if is_binary(expires_in), do: String.to_integer(expires_in), else: expires_in
|
||||
|
||||
if Enum.count(options) > limits.max_options do
|
||||
raise ArgumentError, message: "Poll can't contain more than #{limits.max_options} options"
|
||||
end
|
||||
|
||||
{poll, emoji} =
|
||||
with :ok <- validate_poll_expiration(expires_in, limits),
|
||||
:ok <- validate_poll_options_amount(options, limits),
|
||||
:ok <- validate_poll_options_length(options, limits) do
|
||||
{option_notes, emoji} =
|
||||
Enum.map_reduce(options, %{}, fn option, emoji ->
|
||||
if String.length(option) > limits.max_option_chars do
|
||||
raise ArgumentError,
|
||||
message:
|
||||
"Poll options cannot be longer than #{limits.max_option_chars} characters each"
|
||||
end
|
||||
note = %{
|
||||
"name" => option,
|
||||
"type" => "Note",
|
||||
"replies" => %{"type" => "Collection", "totalItems" => 0}
|
||||
}
|
||||
|
||||
{%{
|
||||
"name" => option,
|
||||
"type" => "Note",
|
||||
"replies" => %{"type" => "Collection", "totalItems" => 0}
|
||||
}, Map.merge(emoji, Emoji.Formatter.get_emoji_map(option))}
|
||||
{note, Map.merge(emoji, Emoji.Formatter.get_emoji_map(option))}
|
||||
end)
|
||||
|
||||
case expires_in do
|
||||
expires_in when expires_in > max_expiration ->
|
||||
raise ArgumentError, message: "Expiration date is too far in the future"
|
||||
|
||||
expires_in when expires_in < min_expiration ->
|
||||
raise ArgumentError, message: "Expiration date is too soon"
|
||||
|
||||
_ ->
|
||||
:noop
|
||||
end
|
||||
|
||||
end_time =
|
||||
NaiveDateTime.utc_now()
|
||||
|> NaiveDateTime.add(expires_in)
|
||||
|> NaiveDateTime.to_iso8601()
|
||||
|
||||
poll =
|
||||
if Pleroma.Web.ControllerHelper.truthy_param?(data["poll"]["multiple"]) do
|
||||
%{"type" => "Question", "anyOf" => poll, "closed" => end_time}
|
||||
else
|
||||
%{"type" => "Question", "oneOf" => poll, "closed" => end_time}
|
||||
end
|
||||
key = if truthy_param?(data["poll"]["multiple"]), do: "anyOf", else: "oneOf"
|
||||
poll = %{"type" => "Question", key => option_notes, "closed" => end_time}
|
||||
|
||||
{poll, emoji}
|
||||
rescue
|
||||
e in ArgumentError -> e.message
|
||||
{:ok, {poll, emoji}}
|
||||
end
|
||||
end
|
||||
|
||||
def make_poll_data(%{"poll" => poll}) when is_map(poll) do
|
||||
"Invalid poll"
|
||||
{:error, "Invalid poll"}
|
||||
end
|
||||
|
||||
def make_poll_data(_data) do
|
||||
{%{}, %{}}
|
||||
{:ok, {%{}, %{}}}
|
||||
end
|
||||
|
||||
defp validate_poll_options_amount(options, %{max_options: max_options}) do
|
||||
if Enum.count(options) > max_options do
|
||||
{:error, "Poll can't contain more than #{max_options} options"}
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_poll_options_length(options, %{max_option_chars: max_option_chars}) do
|
||||
if Enum.any?(options, &(String.length(&1) > max_option_chars)) do
|
||||
{:error, "Poll options cannot be longer than #{max_option_chars} characters each"}
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_poll_expiration(expires_in, %{min_expiration: min, max_expiration: max}) do
|
||||
cond do
|
||||
expires_in > max -> {:error, "Expiration date is too far in the future"}
|
||||
expires_in < min -> {:error, "Expiration date is too soon"}
|
||||
true -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
def make_content_html(
|
||||
|
|
@ -234,7 +231,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
no_attachment_links =
|
||||
data
|
||||
|> Map.get("no_attachment_links", Config.get([:instance, :no_attachment_links]))
|
||||
|> Kernel.in([true, "true"])
|
||||
|> truthy_param?()
|
||||
|
||||
content_type = get_content_type(data["content_type"])
|
||||
|
||||
|
|
@ -347,25 +344,25 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
attachments,
|
||||
in_reply_to,
|
||||
tags,
|
||||
cw \\ nil,
|
||||
summary \\ nil,
|
||||
cc \\ [],
|
||||
sensitive \\ false,
|
||||
merge \\ %{}
|
||||
extra_params \\ %{}
|
||||
) do
|
||||
%{
|
||||
"type" => "Note",
|
||||
"to" => to,
|
||||
"cc" => cc,
|
||||
"content" => content_html,
|
||||
"summary" => cw,
|
||||
"sensitive" => !Enum.member?(["false", "False", "0", false], sensitive),
|
||||
"summary" => summary,
|
||||
"sensitive" => truthy_param?(sensitive),
|
||||
"context" => context,
|
||||
"attachment" => attachments,
|
||||
"actor" => actor,
|
||||
"tag" => Keyword.values(tags) |> Enum.uniq()
|
||||
}
|
||||
|> add_in_reply_to(in_reply_to)
|
||||
|> Map.merge(merge)
|
||||
|> Map.merge(extra_params)
|
||||
end
|
||||
|
||||
defp add_in_reply_to(object, nil), do: object
|
||||
|
|
@ -434,12 +431,14 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
end
|
||||
end
|
||||
|
||||
def emoji_from_profile(%{info: _info} = user) do
|
||||
(Emoji.Formatter.get_emoji(user.bio) ++ Emoji.Formatter.get_emoji(user.name))
|
||||
|> Enum.map(fn {shortcode, %Emoji{file: url}} ->
|
||||
def emoji_from_profile(%User{bio: bio, name: name}) do
|
||||
[bio, name]
|
||||
|> Enum.map(&Emoji.Formatter.get_emoji/1)
|
||||
|> Enum.concat()
|
||||
|> Enum.map(fn {shortcode, %Emoji{file: path}} ->
|
||||
%{
|
||||
"type" => "Emoji",
|
||||
"icon" => %{"type" => "Image", "url" => "#{Endpoint.url()}#{url}"},
|
||||
"icon" => %{"type" => "Image", "url" => "#{Endpoint.url()}#{path}"},
|
||||
"name" => ":#{shortcode}:"
|
||||
}
|
||||
end)
|
||||
|
|
@ -571,15 +570,16 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
}
|
||||
end
|
||||
|
||||
def validate_character_limit(full_payload, attachments, limit) do
|
||||
def validate_character_limit("" = _full_payload, [] = _attachments) do
|
||||
{:error, dgettext("errors", "Cannot post an empty status without attachments")}
|
||||
end
|
||||
|
||||
def validate_character_limit(full_payload, _attachments) do
|
||||
limit = Pleroma.Config.get([:instance, :limit])
|
||||
length = String.length(full_payload)
|
||||
|
||||
if length < limit do
|
||||
if length > 0 or Enum.count(attachments) > 0 do
|
||||
:ok
|
||||
else
|
||||
{:error, dgettext("errors", "Cannot post an empty status without attachments")}
|
||||
end
|
||||
:ok
|
||||
else
|
||||
{:error, dgettext("errors", "The status is over the character limit")}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.Web.ControllerHelper do
|
|||
use Pleroma.Web, :controller
|
||||
|
||||
# As in MastoAPI, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
|
||||
@falsy_param_values [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"]
|
||||
@falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]
|
||||
def truthy_param?(blank_value) when blank_value in [nil, ""], do: nil
|
||||
def truthy_param?(value), do: value not in @falsy_param_values
|
||||
|
||||
|
|
|
|||
|
|
@ -97,10 +97,7 @@ defmodule Pleroma.Web.Endpoint do
|
|||
extra: extra
|
||||
)
|
||||
|
||||
# Note: the plug and its configuration is compile-time this can't be upstreamed yet
|
||||
if proxies = Pleroma.Config.get([__MODULE__, :reverse_proxies]) do
|
||||
plug(RemoteIp, proxies: proxies)
|
||||
end
|
||||
plug(Pleroma.Plugs.RemoteIp)
|
||||
|
||||
defmodule Instrumenter do
|
||||
use Prometheus.PhoenixInstrumenter
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.DomainBlockController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.User
|
||||
|
||||
@doc "GET /api/v1/domain_blocks"
|
||||
def index(%{assigns: %{user: %{info: info}}} = conn, _) do
|
||||
json(conn, Map.get(info, :domain_blocks, []))
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/domain_blocks"
|
||||
def create(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do
|
||||
User.block_domain(blocker, domain)
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
@doc "DELETE /api/v1/domain_blocks"
|
||||
def delete(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do
|
||||
User.unblock_domain(blocker, domain)
|
||||
json(conn, %{})
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.FilterController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Filter
|
||||
|
||||
@doc "GET /api/v1/filters"
|
||||
def index(%{assigns: %{user: user}} = conn, _) do
|
||||
filters = Filter.get_filters(user)
|
||||
|
||||
render(conn, "filters.json", filters: filters)
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/filters"
|
||||
def create(
|
||||
%{assigns: %{user: user}} = conn,
|
||||
%{"phrase" => phrase, "context" => context} = params
|
||||
) do
|
||||
query = %Filter{
|
||||
user_id: user.id,
|
||||
phrase: phrase,
|
||||
context: context,
|
||||
hide: Map.get(params, "irreversible", false),
|
||||
whole_word: Map.get(params, "boolean", true)
|
||||
# expires_at
|
||||
}
|
||||
|
||||
{:ok, response} = Filter.create(query)
|
||||
|
||||
render(conn, "filter.json", filter: response)
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/filters/:id"
|
||||
def show(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
|
||||
filter = Filter.get(filter_id, user)
|
||||
|
||||
render(conn, "filter.json", filter: filter)
|
||||
end
|
||||
|
||||
@doc "PUT /api/v1/filters/:id"
|
||||
def update(
|
||||
%{assigns: %{user: user}} = conn,
|
||||
%{"phrase" => phrase, "context" => context, "id" => filter_id} = params
|
||||
) do
|
||||
query = %Filter{
|
||||
user_id: user.id,
|
||||
filter_id: filter_id,
|
||||
phrase: phrase,
|
||||
context: context,
|
||||
hide: Map.get(params, "irreversible", nil),
|
||||
whole_word: Map.get(params, "boolean", true)
|
||||
# expires_at
|
||||
}
|
||||
|
||||
{:ok, response} = Filter.update(query)
|
||||
render(conn, "filter.json", filter: response)
|
||||
end
|
||||
|
||||
@doc "DELETE /api/v1/filters/:id"
|
||||
def delete(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
|
||||
query = %Filter{
|
||||
user_id: user.id,
|
||||
filter_id: filter_id
|
||||
}
|
||||
|
||||
{:ok, _} = Filter.delete(query)
|
||||
json(conn, %{})
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
plug(:put_view, Pleroma.Web.MastodonAPI.AccountView)
|
||||
plug(:assign_follower when action != :index)
|
||||
|
||||
action_fallback(:errors)
|
||||
|
||||
@doc "GET /api/v1/follow_requests"
|
||||
def index(%{assigns: %{user: followed}} = conn, _params) do
|
||||
follow_requests = User.get_follow_requests(followed)
|
||||
|
||||
render(conn, "accounts.json", for: followed, users: follow_requests, as: :user)
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/follow_requests/:id/authorize"
|
||||
def authorize(%{assigns: %{user: followed, follower: follower}} = conn, _params) do
|
||||
with {:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do
|
||||
render(conn, "relationship.json", user: followed, target: follower)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/follow_requests/:id/reject"
|
||||
def reject(%{assigns: %{user: followed, follower: follower}} = conn, _params) do
|
||||
with {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
|
||||
render(conn, "relationship.json", user: followed, target: follower)
|
||||
end
|
||||
end
|
||||
|
||||
defp assign_follower(%{params: %{"id" => id}} = conn, _) do
|
||||
case User.get_cached_by_id(id) do
|
||||
%User{} = follower -> assign(conn, :follower, follower)
|
||||
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
||||
end
|
||||
end
|
||||
|
||||
defp errors(conn, {:error, message}) do
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
end
|
||||
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper,
|
||||
only: [json_response: 3, add_link_headers: 2, add_link_headers: 3]
|
||||
only: [json_response: 3, add_link_headers: 2, truthy_param?: 1]
|
||||
|
||||
alias Ecto.Changeset
|
||||
alias Pleroma.Activity
|
||||
|
|
@ -14,13 +14,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
alias Pleroma.Config
|
||||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.Filter
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.Plugs.RateLimiter
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.ScheduledActivity
|
||||
alias Pleroma.Stats
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web
|
||||
|
|
@ -30,51 +28,29 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.AppView
|
||||
alias Pleroma.Web.MastodonAPI.ConversationView
|
||||
alias Pleroma.Web.MastodonAPI.FilterView
|
||||
alias Pleroma.Web.MastodonAPI.ListView
|
||||
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
||||
alias Pleroma.Web.MastodonAPI.MastodonView
|
||||
alias Pleroma.Web.MastodonAPI.ReportView
|
||||
alias Pleroma.Web.MastodonAPI.ScheduledActivityView
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.MediaProxy
|
||||
alias Pleroma.Web.OAuth.App
|
||||
alias Pleroma.Web.OAuth.Authorization
|
||||
alias Pleroma.Web.OAuth.Scopes
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.RichMedia
|
||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
|
||||
alias Pleroma.Web.ControllerHelper
|
||||
import Ecto.Query
|
||||
|
||||
require Logger
|
||||
require Pleroma.Constants
|
||||
|
||||
@rate_limited_relations_actions ~w(follow unfollow)a
|
||||
|
||||
@rate_limited_status_actions ~w(reblog_status unreblog_status fav_status unfav_status
|
||||
post_status delete_status)a
|
||||
|
||||
plug(
|
||||
RateLimiter,
|
||||
{:status_id_action, bucket_name: "status_id_action:reblog_unreblog", params: ["id"]}
|
||||
when action in ~w(reblog_status unreblog_status)a
|
||||
)
|
||||
|
||||
plug(
|
||||
RateLimiter,
|
||||
{:status_id_action, bucket_name: "status_id_action:fav_unfav", params: ["id"]}
|
||||
when action in ~w(fav_status unfav_status)a
|
||||
)
|
||||
|
||||
plug(
|
||||
RateLimiter,
|
||||
{:relations_id_action, params: ["id", "uri"]} when action in @rate_limited_relations_actions
|
||||
)
|
||||
|
||||
plug(RateLimiter, :relations_actions when action in @rate_limited_relations_actions)
|
||||
plug(RateLimiter, :statuses_actions when action in @rate_limited_status_actions)
|
||||
plug(RateLimiter, :app_account_creation when action == :account_register)
|
||||
plug(RateLimiter, :search when action in [:search, :search2, :account_search])
|
||||
plug(RateLimiter, :password_reset when action == :password_reset)
|
||||
|
|
@ -157,7 +133,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
]
|
||||
|> Enum.reduce(%{}, fn key, acc ->
|
||||
add_if_present(acc, params, to_string(key), key, fn value ->
|
||||
{:ok, ControllerHelper.truthy_param?(value)}
|
||||
{:ok, truthy_param?(value)}
|
||||
end)
|
||||
end)
|
||||
|> add_if_present(params, "default_scope", :default_scope)
|
||||
|
|
@ -345,43 +321,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
json(conn, mastodon_emoji)
|
||||
end
|
||||
|
||||
def home_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("user", user)
|
||||
|
||||
activities =
|
||||
[user.ap_id | user.following]
|
||||
|> ActivityPub.fetch_activities(params)
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities)
|
||||
|> put_view(StatusView)
|
||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||
end
|
||||
|
||||
def public_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||
local_only = params["local"] in [true, "True", "true", "1"]
|
||||
|
||||
activities =
|
||||
params
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("local_only", local_only)
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities, %{"local" => local_only})
|
||||
|> put_view(StatusView)
|
||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||
end
|
||||
|
||||
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user) do
|
||||
params =
|
||||
|
|
@ -401,80 +340,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def dm_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", "Create")
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> Map.put(:visibility, "direct")
|
||||
|
||||
activities =
|
||||
[user.ap_id]
|
||||
|> ActivityPub.fetch_activities_query(params)
|
||||
|> Pagination.fetch_paginated(params)
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities)
|
||||
|> put_view(StatusView)
|
||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||
end
|
||||
|
||||
def get_statuses(%{assigns: %{user: user}} = conn, %{"ids" => ids}) do
|
||||
limit = 100
|
||||
|
||||
activities =
|
||||
ids
|
||||
|> Enum.take(limit)
|
||||
|> Activity.all_by_ids_with_object()
|
||||
|> Enum.filter(&Visibility.visible_for_user?(&1, user))
|
||||
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> render("index.json", activities: activities, for: user, as: :activity)
|
||||
end
|
||||
|
||||
def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user})
|
||||
end
|
||||
end
|
||||
|
||||
def get_context(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||
activities <-
|
||||
ActivityPub.fetch_activities_for_context(activity.data["context"], %{
|
||||
"blocking_user" => user,
|
||||
"user" => user,
|
||||
"exclude_id" => activity.id
|
||||
}),
|
||||
grouped_activities <- Enum.group_by(activities, fn %{id: id} -> id < activity.id end) do
|
||||
result = %{
|
||||
ancestors:
|
||||
StatusView.render("index.json",
|
||||
for: user,
|
||||
activities: grouped_activities[true] || [],
|
||||
as: :activity
|
||||
)
|
||||
|> Enum.reverse(),
|
||||
# credo:disable-for-previous-line Credo.Check.Refactor.PipeChainStart
|
||||
descendants:
|
||||
StatusView.render("index.json",
|
||||
for: user,
|
||||
activities: grouped_activities[false] || [],
|
||||
as: :activity
|
||||
)
|
||||
|> Enum.reverse()
|
||||
# credo:disable-for-previous-line Credo.Check.Refactor.PipeChainStart
|
||||
}
|
||||
|
||||
json(conn, result)
|
||||
end
|
||||
end
|
||||
|
||||
def get_poll(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
|
|
@ -525,193 +390,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def scheduled_statuses(%{assigns: %{user: user}} = conn, params) do
|
||||
with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do
|
||||
conn
|
||||
|> add_link_headers(scheduled_activities)
|
||||
|> put_view(ScheduledActivityView)
|
||||
|> render("index.json", %{scheduled_activities: scheduled_activities})
|
||||
end
|
||||
end
|
||||
|
||||
def show_scheduled_status(%{assigns: %{user: user}} = conn, %{"id" => scheduled_activity_id}) do
|
||||
with %ScheduledActivity{} = scheduled_activity <-
|
||||
ScheduledActivity.get(user, scheduled_activity_id) do
|
||||
conn
|
||||
|> put_view(ScheduledActivityView)
|
||||
|> render("show.json", %{scheduled_activity: scheduled_activity})
|
||||
else
|
||||
_ -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def update_scheduled_status(
|
||||
%{assigns: %{user: user}} = conn,
|
||||
%{"id" => scheduled_activity_id} = params
|
||||
) do
|
||||
with %ScheduledActivity{} = scheduled_activity <-
|
||||
ScheduledActivity.get(user, scheduled_activity_id),
|
||||
{:ok, scheduled_activity} <- ScheduledActivity.update(scheduled_activity, params) do
|
||||
conn
|
||||
|> put_view(ScheduledActivityView)
|
||||
|> render("show.json", %{scheduled_activity: scheduled_activity})
|
||||
else
|
||||
nil -> {:error, :not_found}
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
|
||||
def delete_scheduled_status(%{assigns: %{user: user}} = conn, %{"id" => scheduled_activity_id}) do
|
||||
with %ScheduledActivity{} = scheduled_activity <-
|
||||
ScheduledActivity.get(user, scheduled_activity_id),
|
||||
{:ok, scheduled_activity} <- ScheduledActivity.delete(scheduled_activity) do
|
||||
conn
|
||||
|> put_view(ScheduledActivityView)
|
||||
|> render("show.json", %{scheduled_activity: scheduled_activity})
|
||||
else
|
||||
nil -> {:error, :not_found}
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
|
||||
def post_status(%{assigns: %{user: user}} = conn, %{"status" => _} = params) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("in_reply_to_status_id", params["in_reply_to_id"])
|
||||
|
||||
scheduled_at = params["scheduled_at"]
|
||||
|
||||
if scheduled_at && ScheduledActivity.far_enough?(scheduled_at) do
|
||||
with {:ok, scheduled_activity} <-
|
||||
ScheduledActivity.create(user, %{"params" => params, "scheduled_at" => scheduled_at}) do
|
||||
conn
|
||||
|> put_view(ScheduledActivityView)
|
||||
|> render("show.json", %{scheduled_activity: scheduled_activity})
|
||||
end
|
||||
else
|
||||
params = Map.drop(params, ["scheduled_at"])
|
||||
|
||||
case CommonAPI.post(user, params) do
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{error: message})
|
||||
|
||||
{:ok, activity} ->
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{
|
||||
activity: activity,
|
||||
for: user,
|
||||
as: :activity,
|
||||
with_direct_conversation_id: true
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
|
||||
json(conn, %{})
|
||||
else
|
||||
_e -> render_error(conn, :forbidden, "Can't delete this post")
|
||||
end
|
||||
end
|
||||
|
||||
def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user),
|
||||
%Activity{} = announce <- Activity.normalize(announce.data) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: announce, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
||||
def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
||||
def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
||||
def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, _, _, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
||||
def pin_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, activity} <- CommonAPI.pin(ap_id_or_id, user) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
||||
def unpin_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, activity} <- CommonAPI.unpin(ap_id_or_id, user) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
||||
def bookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
||||
true <- Visibility.visible_for_user?(activity, user),
|
||||
{:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
||||
def unbookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
||||
true <- Visibility.visible_for_user?(activity, user),
|
||||
{:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
||||
def mute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
activity = Activity.get_by_id(id)
|
||||
|
||||
with {:ok, activity} <- CommonAPI.add_mute(user, activity) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
||||
def unmute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
activity = Activity.get_by_id(id)
|
||||
|
||||
with {:ok, activity} <- CommonAPI.remove_mute(user, activity) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
||||
def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
targets = User.get_all_by_ids(List.wrap(id))
|
||||
|
||||
|
|
@ -778,83 +456,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
json(conn, mascot)
|
||||
end
|
||||
|
||||
def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
{:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
|
||||
%Object{data: %{"likes" => likes}} <- Object.normalize(activity) do
|
||||
q = from(u in User, where: u.ap_id in ^likes)
|
||||
|
||||
users =
|
||||
Repo.all(q)
|
||||
|> Enum.filter(&(not User.blocks?(user, &1)))
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("accounts.json", %{for: user, users: users, as: :user})
|
||||
else
|
||||
{:visible, false} -> {:error, :not_found}
|
||||
_ -> json(conn, [])
|
||||
end
|
||||
end
|
||||
|
||||
def reblogged_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
{:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
|
||||
%Object{data: %{"announcements" => announces}} <- Object.normalize(activity) do
|
||||
q = from(u in User, where: u.ap_id in ^announces)
|
||||
|
||||
users =
|
||||
Repo.all(q)
|
||||
|> Enum.filter(&(not User.blocks?(user, &1)))
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("accounts.json", %{for: user, users: users, as: :user})
|
||||
else
|
||||
{:visible, false} -> {:error, :not_found}
|
||||
_ -> json(conn, [])
|
||||
end
|
||||
end
|
||||
|
||||
def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||
local_only = params["local"] in [true, "True", "true", "1"]
|
||||
|
||||
tags =
|
||||
[params["tag"], params["any"]]
|
||||
|> List.flatten()
|
||||
|> Enum.uniq()
|
||||
|> Enum.filter(& &1)
|
||||
|> Enum.map(&String.downcase(&1))
|
||||
|
||||
tag_all =
|
||||
params["all"] ||
|
||||
[]
|
||||
|> Enum.map(&String.downcase(&1))
|
||||
|
||||
tag_reject =
|
||||
params["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)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities, %{"local" => local_only})
|
||||
|> put_view(StatusView)
|
||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||
end
|
||||
|
||||
def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
|
||||
with %User{} = user <- User.get_cached_by_id(id),
|
||||
followers <- MastodonAPI.get_followers(user, params) do
|
||||
|
|
@ -889,42 +490,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def follow_requests(%{assigns: %{user: followed}} = conn, _params) do
|
||||
follow_requests = User.get_follow_requests(followed)
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("accounts.json", %{for: followed, users: follow_requests, as: :user})
|
||||
end
|
||||
|
||||
def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
||||
with %User{} = follower <- User.get_cached_by_id(id),
|
||||
{:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("relationship.json", %{user: followed, target: follower})
|
||||
else
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
||||
with %User{} = follower <- User.get_cached_by_id(id),
|
||||
{:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("relationship.json", %{user: followed, target: follower})
|
||||
else
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
||||
with {_, %User{} = followed} <- {:followed, User.get_cached_by_id(id)},
|
||||
{_, true} <- {:followed, follower.id != followed.id},
|
||||
|
|
@ -1054,20 +619,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def domain_blocks(%{assigns: %{user: %{info: info}}} = conn, _) do
|
||||
json(conn, info.domain_blocks || [])
|
||||
end
|
||||
|
||||
def block_domain(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do
|
||||
User.block_domain(blocker, domain)
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
def unblock_domain(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do
|
||||
User.unblock_domain(blocker, domain)
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
def subscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %User{} = subscription_target <- User.get_cached_by_id(id),
|
||||
{:ok, subscription_target} = User.subscribe(user, subscription_target) do
|
||||
|
|
@ -1165,31 +716,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|> render("index.json", %{lists: lists})
|
||||
end
|
||||
|
||||
def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do
|
||||
with %Pleroma.List{title: _title, following: following} <- Pleroma.List.get(id, user) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", "Create")
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|
||||
# we must filter the following list for the user to avoid leaking statuses the user
|
||||
# does not actually have permission to see (for more info, peruse security issue #270).
|
||||
activities =
|
||||
following
|
||||
|> Enum.filter(fn x -> x in user.following end)
|
||||
|> ActivityPub.fetch_activities_bounded(following, params)
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||
else
|
||||
_e -> render_error(conn, :forbidden, "Error.")
|
||||
end
|
||||
end
|
||||
|
||||
def index(%{assigns: %{user: user}} = conn, _params) do
|
||||
token = get_session(conn, :oauth_token)
|
||||
|
||||
|
|
@ -1368,62 +894,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
json(conn, [])
|
||||
end
|
||||
|
||||
def get_filters(%{assigns: %{user: user}} = conn, _) do
|
||||
filters = Filter.get_filters(user)
|
||||
res = FilterView.render("filters.json", filters: filters)
|
||||
json(conn, res)
|
||||
end
|
||||
|
||||
def create_filter(
|
||||
%{assigns: %{user: user}} = conn,
|
||||
%{"phrase" => phrase, "context" => context} = params
|
||||
) do
|
||||
query = %Filter{
|
||||
user_id: user.id,
|
||||
phrase: phrase,
|
||||
context: context,
|
||||
hide: Map.get(params, "irreversible", false),
|
||||
whole_word: Map.get(params, "boolean", true)
|
||||
# expires_at
|
||||
}
|
||||
|
||||
{:ok, response} = Filter.create(query)
|
||||
res = FilterView.render("filter.json", filter: response)
|
||||
json(conn, res)
|
||||
end
|
||||
|
||||
def get_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
|
||||
filter = Filter.get(filter_id, user)
|
||||
res = FilterView.render("filter.json", filter: filter)
|
||||
json(conn, res)
|
||||
end
|
||||
|
||||
def update_filter(
|
||||
%{assigns: %{user: user}} = conn,
|
||||
%{"phrase" => phrase, "context" => context, "id" => filter_id} = params
|
||||
) do
|
||||
query = %Filter{
|
||||
user_id: user.id,
|
||||
filter_id: filter_id,
|
||||
phrase: phrase,
|
||||
context: context,
|
||||
hide: Map.get(params, "irreversible", nil),
|
||||
whole_word: Map.get(params, "boolean", true)
|
||||
# expires_at
|
||||
}
|
||||
|
||||
{:ok, response} = Filter.update(query)
|
||||
res = FilterView.render("filter.json", filter: response)
|
||||
json(conn, res)
|
||||
end
|
||||
|
||||
def delete_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
|
||||
query = %Filter{
|
||||
user_id: user.id,
|
||||
filter_id: filter_id
|
||||
}
|
||||
|
||||
{:ok, _} = Filter.delete(query)
|
||||
def empty_object(conn, _) do
|
||||
Logger.debug("Unimplemented, returning an empty object")
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
|
|
@ -1474,21 +946,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@deprecated "https://github.com/tootsuite/mastodon/pull/11213"
|
||||
def status_card(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
data = RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> render("card.json", data)
|
||||
else
|
||||
_e -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def reports(%{assigns: %{user: user}} = conn, params) do
|
||||
case CommonAPI.report(user, params) do
|
||||
{:ok, activity} ->
|
||||
|
|
@ -1597,15 +1054,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
defp try_render(conn, target, params)
|
||||
when is_binary(target) do
|
||||
def try_render(conn, target, params)
|
||||
when is_binary(target) do
|
||||
case render(conn, target, params) do
|
||||
nil -> render_error(conn, :not_implemented, "Can't display this activity")
|
||||
res -> res
|
||||
end
|
||||
end
|
||||
|
||||
defp try_render(conn, _, _) do
|
||||
def try_render(conn, _, _) do
|
||||
render_error(conn, :not_implemented, "Can't display this activity")
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
|
||||
|
||||
alias Pleroma.ScheduledActivity
|
||||
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
||||
|
||||
plug(:assign_scheduled_activity when action != :index)
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
@doc "GET /api/v1/scheduled_statuses"
|
||||
def index(%{assigns: %{user: user}} = conn, params) do
|
||||
with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do
|
||||
conn
|
||||
|> add_link_headers(scheduled_activities)
|
||||
|> render("index.json", scheduled_activities: scheduled_activities)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/scheduled_statuses/:id"
|
||||
def show(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, _params) do
|
||||
render(conn, "show.json", scheduled_activity: scheduled_activity)
|
||||
end
|
||||
|
||||
@doc "PUT /api/v1/scheduled_statuses/:id"
|
||||
def update(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, params) do
|
||||
with {:ok, scheduled_activity} <- ScheduledActivity.update(scheduled_activity, params) do
|
||||
render(conn, "show.json", scheduled_activity: scheduled_activity)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "DELETE /api/v1/scheduled_statuses/:id"
|
||||
def delete(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, _params) do
|
||||
with {:ok, scheduled_activity} <- ScheduledActivity.delete(scheduled_activity) do
|
||||
render(conn, "show.json", scheduled_activity: scheduled_activity)
|
||||
end
|
||||
end
|
||||
|
||||
defp assign_scheduled_activity(%{assigns: %{user: user}, params: %{"id" => id}} = conn, _) do
|
||||
case ScheduledActivity.get(user, id) do
|
||||
%ScheduledActivity{} = activity -> assign(conn, :scheduled_activity, activity)
|
||||
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
||||
end
|
||||
end
|
||||
end
|
||||
274
lib/pleroma/web/mastodon_api/controllers/status_controller.ex
Normal file
274
lib/pleroma/web/mastodon_api/controllers/status_controller.ex
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.MastodonAPI.MastodonAPIController, only: [try_render: 3]
|
||||
|
||||
require Ecto.Query
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Bookmark
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Plugs.RateLimiter
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.ScheduledActivity
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.ScheduledActivityView
|
||||
|
||||
@rate_limited_status_actions ~w(reblog unreblog favourite unfavourite create delete)a
|
||||
|
||||
plug(
|
||||
RateLimiter,
|
||||
{:status_id_action, bucket_name: "status_id_action:reblog_unreblog", params: ["id"]}
|
||||
when action in ~w(reblog unreblog)a
|
||||
)
|
||||
|
||||
plug(
|
||||
RateLimiter,
|
||||
{:status_id_action, bucket_name: "status_id_action:fav_unfav", params: ["id"]}
|
||||
when action in ~w(favourite unfavourite)a
|
||||
)
|
||||
|
||||
plug(RateLimiter, :statuses_actions when action in @rate_limited_status_actions)
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
@doc """
|
||||
GET `/api/v1/statuses?ids[]=1&ids[]=2`
|
||||
|
||||
`ids` query param is required
|
||||
"""
|
||||
def index(%{assigns: %{user: user}} = conn, %{"ids" => ids}) do
|
||||
limit = 100
|
||||
|
||||
activities =
|
||||
ids
|
||||
|> Enum.take(limit)
|
||||
|> Activity.all_by_ids_with_object()
|
||||
|> Enum.filter(&Visibility.visible_for_user?(&1, user))
|
||||
|
||||
render(conn, "index.json", activities: activities, for: user, as: :activity)
|
||||
end
|
||||
|
||||
@doc """
|
||||
POST /api/v1/statuses
|
||||
|
||||
Creates a scheduled status when `scheduled_at` param is present and it's far enough
|
||||
"""
|
||||
def create(
|
||||
%{assigns: %{user: user}} = conn,
|
||||
%{"status" => _, "scheduled_at" => scheduled_at} = params
|
||||
) do
|
||||
params = Map.put(params, "in_reply_to_status_id", params["in_reply_to_id"])
|
||||
|
||||
if ScheduledActivity.far_enough?(scheduled_at) do
|
||||
with {:ok, scheduled_activity} <-
|
||||
ScheduledActivity.create(user, %{"params" => params, "scheduled_at" => scheduled_at}) do
|
||||
conn
|
||||
|> put_view(ScheduledActivityView)
|
||||
|> render("show.json", scheduled_activity: scheduled_activity)
|
||||
end
|
||||
else
|
||||
create(conn, Map.drop(params, ["scheduled_at"]))
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
POST /api/v1/statuses
|
||||
|
||||
Creates a regular status
|
||||
"""
|
||||
def create(%{assigns: %{user: user}} = conn, %{"status" => _} = params) do
|
||||
params = Map.put(params, "in_reply_to_status_id", params["in_reply_to_id"])
|
||||
|
||||
with {:ok, activity} <- CommonAPI.post(user, params) do
|
||||
try_render(conn, "show.json",
|
||||
activity: activity,
|
||||
for: user,
|
||||
as: :activity,
|
||||
with_direct_conversation_id: true
|
||||
)
|
||||
else
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
def create(%{assigns: %{user: _user}} = conn, %{"media_ids" => _} = params) do
|
||||
create(conn, Map.put(params, "status", ""))
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/statuses/:id"
|
||||
def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
try_render(conn, "show.json", activity: activity, for: user)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "DELETE /api/v1/statuses/:id"
|
||||
def delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
|
||||
json(conn, %{})
|
||||
else
|
||||
_e -> render_error(conn, :forbidden, "Can't delete this post")
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/statuses/:id/reblog"
|
||||
def reblog(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user),
|
||||
%Activity{} = announce <- Activity.normalize(announce.data) do
|
||||
try_render(conn, "show.json", %{activity: announce, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/statuses/:id/unreblog"
|
||||
def unreblog(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do
|
||||
try_render(conn, "show.json", %{activity: activity, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/statuses/:id/favourite"
|
||||
def favourite(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
|
||||
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/statuses/:id/unfavourite"
|
||||
def unfavourite(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, _, _, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
|
||||
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/statuses/:id/pin"
|
||||
def pin(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, activity} <- CommonAPI.pin(ap_id_or_id, user) do
|
||||
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/statuses/:id/unpin"
|
||||
def unpin(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, activity} <- CommonAPI.unpin(ap_id_or_id, user) do
|
||||
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/statuses/:id/bookmark"
|
||||
def bookmark(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
||||
true <- Visibility.visible_for_user?(activity, user),
|
||||
{:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do
|
||||
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/statuses/:id/unbookmark"
|
||||
def unbookmark(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
||||
true <- Visibility.visible_for_user?(activity, user),
|
||||
{:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do
|
||||
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/statuses/:id/mute"
|
||||
def mute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||
{:ok, activity} <- CommonAPI.add_mute(user, activity) do
|
||||
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/statuses/:id/unmute"
|
||||
def unmute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||
{:ok, activity} <- CommonAPI.remove_mute(user, activity) do
|
||||
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/statuses/:id/card"
|
||||
@deprecated "https://github.com/tootsuite/mastodon/pull/11213"
|
||||
def card(%{assigns: %{user: user}} = conn, %{"id" => status_id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(status_id),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
data = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||
render(conn, "card.json", data)
|
||||
else
|
||||
_ -> render_error(conn, :not_found, "Record not found")
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/statuses/:id/favourited_by"
|
||||
def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
{:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
|
||||
%Object{data: %{"likes" => likes}} <- Object.normalize(activity) do
|
||||
users =
|
||||
User
|
||||
|> Ecto.Query.where([u], u.ap_id in ^likes)
|
||||
|> Repo.all()
|
||||
|> Enum.filter(&(not User.blocks?(user, &1)))
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("accounts.json", for: user, users: users, as: :user)
|
||||
else
|
||||
{:visible, false} -> {:error, :not_found}
|
||||
_ -> json(conn, [])
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/statuses/:id/reblogged_by"
|
||||
def reblogged_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
{:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
|
||||
%Object{data: %{"announcements" => announces}} <- Object.normalize(activity) do
|
||||
users =
|
||||
User
|
||||
|> Ecto.Query.where([u], u.ap_id in ^announces)
|
||||
|> Repo.all()
|
||||
|> Enum.filter(&(not User.blocks?(user, &1)))
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("accounts.json", for: user, users: users, as: :user)
|
||||
else
|
||||
{:visible, false} -> {:error, :not_found}
|
||||
_ -> json(conn, [])
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/statuses/:id/context"
|
||||
def context(%{assigns: %{user: user}} = conn, %{"id" => id}) 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
|
||||
})
|
||||
|
||||
render(conn, "context.json", activity: activity, activities: activities, user: user)
|
||||
end
|
||||
end
|
||||
end
|
||||
136
lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex
Normal file
136
lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper,
|
||||
only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1]
|
||||
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
||||
plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
|
||||
|
||||
# GET /api/v1/timelines/home
|
||||
def home(%{assigns: %{user: user}} = conn, params) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("user", user)
|
||||
|
||||
recipients = [user.ap_id | user.following]
|
||||
|
||||
activities =
|
||||
recipients
|
||||
|> ActivityPub.fetch_activities(params)
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities)
|
||||
|> render("index.json", activities: activities, for: user, as: :activity)
|
||||
end
|
||||
|
||||
# GET /api/v1/timelines/direct
|
||||
def direct(%{assigns: %{user: user}} = conn, params) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", "Create")
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> Map.put(:visibility, "direct")
|
||||
|
||||
activities =
|
||||
[user.ap_id]
|
||||
|> ActivityPub.fetch_activities_query(params)
|
||||
|> Pagination.fetch_paginated(params)
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities)
|
||||
|> render("index.json", activities: activities, for: user, as: :activity)
|
||||
end
|
||||
|
||||
# GET /api/v1/timelines/public
|
||||
def public(%{assigns: %{user: user}} = conn, params) do
|
||||
local_only = truthy_param?(params["local"])
|
||||
|
||||
activities =
|
||||
params
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("local_only", local_only)
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities, %{"local" => local_only})
|
||||
|> render("index.json", activities: activities, for: user, as: :activity)
|
||||
end
|
||||
|
||||
# GET /api/v1/timelines/tag/:tag
|
||||
def hashtag(%{assigns: %{user: user}} = conn, params) do
|
||||
local_only = truthy_param?(params["local"])
|
||||
|
||||
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)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities, %{"local" => local_only})
|
||||
|> render("index.json", activities: activities, for: user, as: :activity)
|
||||
end
|
||||
|
||||
# GET /api/v1/timelines/list/:list_id
|
||||
def list(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do
|
||||
with %Pleroma.List{title: _title, following: following} <- Pleroma.List.get(id, user) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", "Create")
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|
||||
# we must filter the following list for the user to avoid leaking statuses the user
|
||||
# does not actually have permission to see (for more info, peruse security issue #270).
|
||||
activities =
|
||||
following
|
||||
|> Enum.filter(fn x -> x in user.following end)
|
||||
|> ActivityPub.fetch_activities_bounded(following, params)
|
||||
|> Enum.reverse()
|
||||
|
||||
render(conn, "index.json", activities: activities, for: user, as: :activity)
|
||||
else
|
||||
_e -> render_error(conn, :forbidden, "Error.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -24,7 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
|
|||
|
||||
activity = Activity.get_by_id_with_object(last_activity_id)
|
||||
|
||||
last_status = StatusView.render("status.json", %{activity: activity, for: user})
|
||||
last_status = StatusView.render("show.json", %{activity: activity, for: user})
|
||||
|
||||
# Conversations return all users except the current user.
|
||||
users =
|
||||
|
|
|
|||
|
|
@ -39,19 +39,19 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||
"mention" ->
|
||||
response
|
||||
|> Map.merge(%{
|
||||
status: StatusView.render("status.json", %{activity: activity, for: user})
|
||||
status: StatusView.render("show.json", %{activity: activity, for: user})
|
||||
})
|
||||
|
||||
"favourite" ->
|
||||
response
|
||||
|> Map.merge(%{
|
||||
status: StatusView.render("status.json", %{activity: parent_activity, for: user})
|
||||
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
||||
})
|
||||
|
||||
"reblog" ->
|
||||
response
|
||||
|> Map.merge(%{
|
||||
status: StatusView.render("status.json", %{activity: parent_activity, for: user})
|
||||
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
||||
})
|
||||
|
||||
"follow" ->
|
||||
|
|
|
|||
|
|
@ -7,11 +7,10 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do
|
|||
|
||||
alias Pleroma.ScheduledActivity
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.ScheduledActivityView
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
|
||||
def render("index.json", %{scheduled_activities: scheduled_activities}) do
|
||||
render_many(scheduled_activities, ScheduledActivityView, "show.json")
|
||||
render_many(scheduled_activities, __MODULE__, "show.json")
|
||||
end
|
||||
|
||||
def render("show.json", %{scheduled_activity: %ScheduledActivity{} = scheduled_activity}) do
|
||||
|
|
@ -24,12 +23,8 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do
|
|||
end
|
||||
|
||||
defp with_media_attachments(data, %{params: %{"media_attachments" => media_attachments}}) do
|
||||
try do
|
||||
attachments = render_many(media_attachments, StatusView, "attachment.json", as: :attachment)
|
||||
Map.put(data, :media_attachments, attachments)
|
||||
rescue
|
||||
_ -> data
|
||||
end
|
||||
attachments = render_many(media_attachments, StatusView, "attachment.json", as: :attachment)
|
||||
Map.put(data, :media_attachments, attachments)
|
||||
end
|
||||
|
||||
defp with_media_attachments(data, _), do: data
|
||||
|
|
@ -45,13 +40,9 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do
|
|||
in_reply_to_id: params["in_reply_to_id"]
|
||||
}
|
||||
|
||||
data =
|
||||
if media_ids = params["media_ids"] do
|
||||
Map.put(data, :media_ids, media_ids)
|
||||
else
|
||||
data
|
||||
end
|
||||
|
||||
data
|
||||
case params["media_ids"] do
|
||||
nil -> data
|
||||
media_ids -> Map.put(data, :media_ids, media_ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -73,17 +73,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
|
||||
def render("index.json", opts) do
|
||||
replied_to_activities = get_replied_to_activities(opts.activities)
|
||||
opts = Map.put(opts, :replied_to_activities, replied_to_activities)
|
||||
|
||||
opts.activities
|
||||
|> safe_render_many(
|
||||
StatusView,
|
||||
"status.json",
|
||||
Map.put(opts, :replied_to_activities, replied_to_activities)
|
||||
)
|
||||
safe_render_many(opts.activities, StatusView, "show.json", opts)
|
||||
end
|
||||
|
||||
def render(
|
||||
"status.json",
|
||||
"show.json",
|
||||
%{activity: %{data: %{"type" => "Announce", "object" => _object}} = activity} = opts
|
||||
) do
|
||||
user = get_user(activity.data["actor"])
|
||||
|
|
@ -96,7 +92,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
|> Activity.with_set_thread_muted_field(opts[:for])
|
||||
|> Repo.one()
|
||||
|
||||
reblogged = render("status.json", Map.put(opts, :activity, reblogged_activity))
|
||||
reblogged = render("show.json", Map.put(opts, :activity, reblogged_activity))
|
||||
|
||||
favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || [])
|
||||
|
||||
|
|
@ -144,7 +140,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
}
|
||||
end
|
||||
|
||||
def render("status.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
||||
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
||||
object = Object.normalize(activity)
|
||||
|
||||
user = get_user(activity.data["actor"])
|
||||
|
|
@ -303,7 +299,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
}
|
||||
end
|
||||
|
||||
def render("status.json", _) do
|
||||
def render("show.json", _) do
|
||||
nil
|
||||
end
|
||||
|
||||
|
|
@ -441,6 +437,20 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
end
|
||||
end
|
||||
|
||||
def render("context.json", %{activity: activity, activities: activities, user: user}) do
|
||||
%{ancestors: ancestors, descendants: descendants} =
|
||||
activities
|
||||
|> Enum.reverse()
|
||||
|> Enum.group_by(fn %{id: id} -> if id < activity.id, do: :ancestors, else: :descendants end)
|
||||
|> Map.put_new(:ancestors, [])
|
||||
|> Map.put_new(:descendants, [])
|
||||
|
||||
%{
|
||||
ancestors: render("index.json", for: user, activities: ancestors, as: :activity),
|
||||
descendants: render("index.json", for: user, activities: descendants, as: :activity)
|
||||
}
|
||||
end
|
||||
|
||||
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
|
||||
object = Object.normalize(activity)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.SubscriptionNotificationController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.SubscriptionNotification
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.PleromaAPI.PleromaAPI
|
||||
|
||||
def index(%{assigns: %{user: user}} = conn, params) do
|
||||
notifications =
|
||||
user
|
||||
|> PleromaAPI.get_subscription_notifications(params)
|
||||
|> Enum.map(&build_notification_data/1)
|
||||
|
||||
conn
|
||||
|> add_link_headers(notifications)
|
||||
|> render("index.json", %{notifications: notifications, for: user})
|
||||
end
|
||||
|
||||
def show(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do
|
||||
with {:ok, notification} <- SubscriptionNotification.get(user, id) do
|
||||
render(conn, "show.json", %{
|
||||
subscription_notification: build_notification_data(notification),
|
||||
for: user
|
||||
})
|
||||
else
|
||||
{:error, reason} ->
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json(%{"error" => reason})
|
||||
end
|
||||
end
|
||||
|
||||
def clear(%{assigns: %{user: user}} = conn, _params) do
|
||||
SubscriptionNotification.clear(user)
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
def dismiss(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do
|
||||
with {:ok, _notif} <- SubscriptionNotification.dismiss(user, id) do
|
||||
json(conn, %{})
|
||||
else
|
||||
{:error, reason} ->
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json(%{"error" => reason})
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_multiple(
|
||||
%{assigns: %{user: user}} = conn,
|
||||
%{"ids" => ids} = _params
|
||||
) do
|
||||
SubscriptionNotification.destroy_multiple(user, ids)
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
defp build_notification_data(%{activity: %{data: data}} = notification) do
|
||||
%{
|
||||
notification: notification,
|
||||
actor: User.get_cached_by_ap_id(data["actor"]),
|
||||
parent_activity: Activity.get_create_by_object_ap_id(data["object"])
|
||||
}
|
||||
end
|
||||
end
|
||||
40
lib/pleroma/web/pleroma_api/pleroma_api.ex
Normal file
40
lib/pleroma/web/pleroma_api/pleroma_api.ex
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
defmodule Pleroma.Web.PleromaAPI.PleromaAPI do
|
||||
import Ecto.Query
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.SubscriptionNotification
|
||||
|
||||
def get_subscription_notifications(user, params \\ %{}) do
|
||||
options = cast_params(params)
|
||||
|
||||
user
|
||||
|> SubscriptionNotification.for_user_query(options)
|
||||
|> restrict(:exclude_types, options)
|
||||
|> Pagination.fetch_paginated(params)
|
||||
end
|
||||
|
||||
defp cast_params(params) do
|
||||
param_types = %{
|
||||
exclude_types: {:array, :string},
|
||||
reblogs: :boolean,
|
||||
with_muted: :boolean
|
||||
}
|
||||
|
||||
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
||||
changeset.changes
|
||||
end
|
||||
|
||||
defp restrict(query, :exclude_types, %{exclude_types: mastodon_types = [_ | _]}) do
|
||||
ap_types =
|
||||
mastodon_types
|
||||
|> Enum.map(&Activity.from_mastodon_notification_type/1)
|
||||
|> Enum.filter(& &1)
|
||||
|
||||
query
|
||||
|> where([q, a], not fragment("? @> ARRAY[?->>'type']::varchar[]", ^ap_types, a.data))
|
||||
end
|
||||
|
||||
defp restrict(query, _, _), do: query
|
||||
end
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.SubscriptionNotificationView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.PleromaAPI.SubscriptionNotificationView
|
||||
|
||||
def render("index.json", %{notifications: notifications, for: user}) do
|
||||
safe_render_many(notifications, SubscriptionNotificationView, "show.json", %{for: user})
|
||||
end
|
||||
|
||||
def render("show.json", %{
|
||||
subscription_notification: %{
|
||||
notification: %{activity: activity} = notification,
|
||||
actor: actor,
|
||||
parent_activity: parent_activity
|
||||
},
|
||||
for: user
|
||||
}) do
|
||||
mastodon_type = Activity.mastodon_notification_type(activity)
|
||||
|
||||
response = %{
|
||||
id: to_string(notification.id),
|
||||
type: mastodon_type,
|
||||
created_at: CommonAPI.Utils.to_masto_date(notification.inserted_at),
|
||||
account: AccountView.render("account.json", %{user: actor, for: user})
|
||||
}
|
||||
|
||||
case mastodon_type do
|
||||
"mention" ->
|
||||
response
|
||||
|> Map.merge(%{
|
||||
status: StatusView.render("show.json", %{activity: activity, for: user})
|
||||
})
|
||||
|
||||
"favourite" ->
|
||||
response
|
||||
|> Map.merge(%{
|
||||
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
||||
})
|
||||
|
||||
"reblog" ->
|
||||
response
|
||||
|> Map.merge(%{
|
||||
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
||||
})
|
||||
|
||||
"follow" ->
|
||||
response
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.Push.Impl do
|
|||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.SubscriptionNotification
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Metadata.Utils
|
||||
alias Pleroma.Web.Push.Subscription
|
||||
|
|
@ -19,7 +20,7 @@ defmodule Pleroma.Web.Push.Impl do
|
|||
@types ["Create", "Follow", "Announce", "Like"]
|
||||
|
||||
@doc "Performs sending notifications for user subscriptions"
|
||||
@spec perform(Notification.t()) :: list(any) | :error
|
||||
@spec perform(Notification.t() | SubscriptionNotification.t()) :: list(any) | :error
|
||||
def perform(
|
||||
%{
|
||||
activity: %{data: %{"type" => activity_type}, id: activity_id} = activity,
|
||||
|
|
|
|||
|
|
@ -293,6 +293,14 @@ defmodule Pleroma.Web.Router do
|
|||
pipe_through(:oauth_read)
|
||||
get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses)
|
||||
get("/conversations/:id", PleromaAPIController, :conversation)
|
||||
|
||||
scope "/subscription_notifications" do
|
||||
post("/clear", SubscriptionNotificationController, :clear)
|
||||
post("/dismiss", SubscriptionNotificationController, :dismiss)
|
||||
delete("/destroy_multiple", SubscriptionNotificationController, :destroy_multiple)
|
||||
get("/", SubscriptionNotificationController, :index)
|
||||
get("/:id", SubscriptionNotificationController, :show)
|
||||
end
|
||||
end
|
||||
|
||||
scope [] do
|
||||
|
|
@ -315,12 +323,12 @@ defmodule Pleroma.Web.Router do
|
|||
get("/accounts/:id/lists", MastodonAPIController, :account_lists)
|
||||
get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array)
|
||||
|
||||
get("/follow_requests", MastodonAPIController, :follow_requests)
|
||||
get("/follow_requests", FollowRequestController, :index)
|
||||
get("/blocks", MastodonAPIController, :blocks)
|
||||
get("/mutes", MastodonAPIController, :mutes)
|
||||
|
||||
get("/timelines/home", MastodonAPIController, :home_timeline)
|
||||
get("/timelines/direct", MastodonAPIController, :dm_timeline)
|
||||
get("/timelines/home", TimelineController, :home)
|
||||
get("/timelines/direct", TimelineController, :direct)
|
||||
|
||||
get("/favourites", MastodonAPIController, :favourites)
|
||||
get("/bookmarks", MastodonAPIController, :bookmarks)
|
||||
|
|
@ -331,16 +339,16 @@ defmodule Pleroma.Web.Router do
|
|||
post("/notifications/dismiss", NotificationController, :dismiss)
|
||||
delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple)
|
||||
|
||||
get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses)
|
||||
get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status)
|
||||
get("/scheduled_statuses", ScheduledActivityController, :index)
|
||||
get("/scheduled_statuses/:id", ScheduledActivityController, :show)
|
||||
|
||||
get("/lists", ListController, :index)
|
||||
get("/lists/:id", ListController, :show)
|
||||
get("/lists/:id/accounts", ListController, :list_accounts)
|
||||
|
||||
get("/domain_blocks", MastodonAPIController, :domain_blocks)
|
||||
get("/domain_blocks", DomainBlockController, :index)
|
||||
|
||||
get("/filters", MastodonAPIController, :get_filters)
|
||||
get("/filters", FilterController, :index)
|
||||
|
||||
get("/suggestions", MastodonAPIController, :suggestions)
|
||||
|
||||
|
|
@ -355,22 +363,22 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
patch("/accounts/update_credentials", MastodonAPIController, :update_credentials)
|
||||
|
||||
post("/statuses", MastodonAPIController, :post_status)
|
||||
delete("/statuses/:id", MastodonAPIController, :delete_status)
|
||||
post("/statuses", StatusController, :create)
|
||||
delete("/statuses/:id", StatusController, :delete)
|
||||
|
||||
post("/statuses/:id/reblog", MastodonAPIController, :reblog_status)
|
||||
post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status)
|
||||
post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
|
||||
post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
|
||||
post("/statuses/:id/pin", MastodonAPIController, :pin_status)
|
||||
post("/statuses/:id/unpin", MastodonAPIController, :unpin_status)
|
||||
post("/statuses/:id/bookmark", MastodonAPIController, :bookmark_status)
|
||||
post("/statuses/:id/unbookmark", MastodonAPIController, :unbookmark_status)
|
||||
post("/statuses/:id/mute", MastodonAPIController, :mute_conversation)
|
||||
post("/statuses/:id/unmute", MastodonAPIController, :unmute_conversation)
|
||||
post("/statuses/:id/reblog", StatusController, :reblog)
|
||||
post("/statuses/:id/unreblog", StatusController, :unreblog)
|
||||
post("/statuses/:id/favourite", StatusController, :favourite)
|
||||
post("/statuses/:id/unfavourite", StatusController, :unfavourite)
|
||||
post("/statuses/:id/pin", StatusController, :pin)
|
||||
post("/statuses/:id/unpin", StatusController, :unpin)
|
||||
post("/statuses/:id/bookmark", StatusController, :bookmark)
|
||||
post("/statuses/:id/unbookmark", StatusController, :unbookmark)
|
||||
post("/statuses/:id/mute", StatusController, :mute_conversation)
|
||||
post("/statuses/:id/unmute", StatusController, :unmute_conversation)
|
||||
|
||||
put("/scheduled_statuses/:id", MastodonAPIController, :update_scheduled_status)
|
||||
delete("/scheduled_statuses/:id", MastodonAPIController, :delete_scheduled_status)
|
||||
put("/scheduled_statuses/:id", ScheduledActivityController, :update)
|
||||
delete("/scheduled_statuses/:id", ScheduledActivityController, :delete)
|
||||
|
||||
post("/polls/:id/votes", MastodonAPIController, :poll_vote)
|
||||
|
||||
|
|
@ -384,10 +392,10 @@ defmodule Pleroma.Web.Router do
|
|||
post("/lists/:id/accounts", ListController, :add_to_list)
|
||||
delete("/lists/:id/accounts", ListController, :remove_from_list)
|
||||
|
||||
post("/filters", MastodonAPIController, :create_filter)
|
||||
get("/filters/:id", MastodonAPIController, :get_filter)
|
||||
put("/filters/:id", MastodonAPIController, :update_filter)
|
||||
delete("/filters/:id", MastodonAPIController, :delete_filter)
|
||||
post("/filters", FilterController, :create)
|
||||
get("/filters/:id", FilterController, :show)
|
||||
put("/filters/:id", FilterController, :update)
|
||||
delete("/filters/:id", FilterController, :delete)
|
||||
|
||||
patch("/pleroma/accounts/update_avatar", MastodonAPIController, :update_avatar)
|
||||
patch("/pleroma/accounts/update_banner", MastodonAPIController, :update_banner)
|
||||
|
|
@ -411,11 +419,11 @@ defmodule Pleroma.Web.Router do
|
|||
post("/accounts/:id/mute", MastodonAPIController, :mute)
|
||||
post("/accounts/:id/unmute", MastodonAPIController, :unmute)
|
||||
|
||||
post("/follow_requests/:id/authorize", MastodonAPIController, :authorize_follow_request)
|
||||
post("/follow_requests/:id/reject", MastodonAPIController, :reject_follow_request)
|
||||
post("/follow_requests/:id/authorize", FollowRequestController, :authorize)
|
||||
post("/follow_requests/:id/reject", FollowRequestController, :reject)
|
||||
|
||||
post("/domain_blocks", MastodonAPIController, :block_domain)
|
||||
delete("/domain_blocks", MastodonAPIController, :unblock_domain)
|
||||
post("/domain_blocks", DomainBlockController, :create)
|
||||
delete("/domain_blocks", DomainBlockController, :delete)
|
||||
|
||||
post("/pleroma/accounts/:id/subscribe", MastodonAPIController, :subscribe)
|
||||
post("/pleroma/accounts/:id/unsubscribe", MastodonAPIController, :unsubscribe)
|
||||
|
|
@ -448,10 +456,10 @@ defmodule Pleroma.Web.Router do
|
|||
get("/apps/verify_credentials", MastodonAPIController, :verify_app_credentials)
|
||||
get("/custom_emojis", MastodonAPIController, :custom_emojis)
|
||||
|
||||
get("/statuses/:id/card", MastodonAPIController, :status_card)
|
||||
get("/statuses/:id/card", StatusController, :card)
|
||||
|
||||
get("/statuses/:id/favourited_by", MastodonAPIController, :favourited_by)
|
||||
get("/statuses/:id/reblogged_by", MastodonAPIController, :reblogged_by)
|
||||
get("/statuses/:id/favourited_by", StatusController, :favourited_by)
|
||||
get("/statuses/:id/reblogged_by", StatusController, :reblogged_by)
|
||||
|
||||
get("/trends", MastodonAPIController, :empty_array)
|
||||
|
||||
|
|
@ -466,13 +474,13 @@ defmodule Pleroma.Web.Router do
|
|||
scope [] do
|
||||
pipe_through(:oauth_read_or_public)
|
||||
|
||||
get("/timelines/public", MastodonAPIController, :public_timeline)
|
||||
get("/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline)
|
||||
get("/timelines/list/:list_id", MastodonAPIController, :list_timeline)
|
||||
get("/timelines/public", TimelineController, :public)
|
||||
get("/timelines/tag/:tag", TimelineController, :hashtag)
|
||||
get("/timelines/list/:list_id", TimelineController, :list)
|
||||
|
||||
get("/statuses", MastodonAPIController, :get_statuses)
|
||||
get("/statuses/:id", MastodonAPIController, :get_status)
|
||||
get("/statuses/:id/context", MastodonAPIController, :get_context)
|
||||
get("/statuses", StatusController, :index)
|
||||
get("/statuses/:id", StatusController, :show)
|
||||
get("/statuses/:id/context", StatusController, :context)
|
||||
|
||||
get("/polls/:id", MastodonAPIController, :get_poll)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ defmodule Pleroma.Web.StreamerView do
|
|||
event: "update",
|
||||
payload:
|
||||
Pleroma.Web.MastodonAPI.StatusView.render(
|
||||
"status.json",
|
||||
"show.json",
|
||||
activity: activity,
|
||||
for: user
|
||||
)
|
||||
|
|
@ -43,7 +43,7 @@ defmodule Pleroma.Web.StreamerView do
|
|||
event: "update",
|
||||
payload:
|
||||
Pleroma.Web.MastodonAPI.StatusView.render(
|
||||
"status.json",
|
||||
"show.json",
|
||||
activity: activity
|
||||
)
|
||||
|> Jason.encode!()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue