Merge branch 'develop' into issue/1276
This commit is contained in:
commit
dfd2c74184
259 changed files with 4072 additions and 2048 deletions
|
|
@ -503,8 +503,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
defp do_follow(follower, followed, activity_id, local) do
|
||||
with data <- make_follow_data(follower, followed, activity_id),
|
||||
{:ok, activity} <- insert(data, local),
|
||||
:ok <- maybe_federate(activity),
|
||||
_ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:error, error} -> Repo.rollback(error)
|
||||
|
|
@ -584,6 +583,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
defp do_delete(%Object{data: %{"type" => "Tombstone", "id" => ap_id}}, _) do
|
||||
activity =
|
||||
ap_id
|
||||
|> Activity.Queries.by_object_id()
|
||||
|> Activity.Queries.by_type("Delete")
|
||||
|> Repo.one()
|
||||
|
||||
{:ok, activity}
|
||||
end
|
||||
|
||||
@spec block(User.t(), User.t(), String.t() | nil, boolean()) ::
|
||||
{:ok, Activity.t()} | {:error, any()}
|
||||
def block(blocker, blocked, activity_id \\ nil, local \\ true) do
|
||||
|
|
@ -1230,17 +1239,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp fetch_activities_query_ap_ids_ops(opts) do
|
||||
source_user = opts["muting_user"]
|
||||
ap_id_relations = if source_user, do: [:mute, :reblog_mute], else: []
|
||||
ap_id_relationships = if source_user, do: [:mute, :reblog_mute], else: []
|
||||
|
||||
ap_id_relations =
|
||||
ap_id_relations ++
|
||||
ap_id_relationships =
|
||||
ap_id_relationships ++
|
||||
if opts["blocking_user"] && opts["blocking_user"] == source_user do
|
||||
[:block]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
preloaded_ap_ids = User.outgoing_relations_ap_ids(source_user, ap_id_relations)
|
||||
preloaded_ap_ids = User.outgoing_relationships_ap_ids(source_user, ap_id_relationships)
|
||||
|
||||
restrict_blocked_opts = Map.merge(%{"blocked_users_ap_ids" => preloaded_ap_ids[:block]}, opts)
|
||||
restrict_muted_opts = Map.merge(%{"muted_users_ap_ids" => preloaded_ap_ids[:mute]}, opts)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
alias Pleroma.Delivery
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Object.Fetcher
|
||||
alias Pleroma.Plugs.EnsureAuthenticatedPlug
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
||||
|
|
@ -18,23 +19,37 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
alias Pleroma.Web.ActivityPub.UserView
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.FederatingPlug
|
||||
alias Pleroma.Web.Federator
|
||||
|
||||
require Logger
|
||||
|
||||
action_fallback(:errors)
|
||||
|
||||
@federating_only_actions [:internal_fetch, :relay, :relay_following, :relay_followers]
|
||||
|
||||
plug(FederatingPlug when action in @federating_only_actions)
|
||||
|
||||
plug(
|
||||
EnsureAuthenticatedPlug,
|
||||
[unless_func: &FederatingPlug.federating?/0] when action not in @federating_only_actions
|
||||
)
|
||||
|
||||
plug(
|
||||
EnsureAuthenticatedPlug
|
||||
when action in [:read_inbox, :update_outbox, :whoami, :upload_media, :following, :followers]
|
||||
)
|
||||
|
||||
plug(
|
||||
Pleroma.Plugs.Cache,
|
||||
[query_params: false, tracking_fun: &__MODULE__.track_object_fetch/2]
|
||||
when action in [:activity, :object]
|
||||
)
|
||||
|
||||
plug(Pleroma.Web.FederatingPlug when action in [:inbox, :relay])
|
||||
plug(:set_requester_reachable when action in [:inbox])
|
||||
plug(:relay_active? when action in [:relay])
|
||||
|
||||
def relay_active?(conn, _) do
|
||||
defp relay_active?(conn, _) do
|
||||
if Pleroma.Config.get([:instance, :allow_relay]) do
|
||||
conn
|
||||
else
|
||||
|
|
@ -127,11 +142,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
end
|
||||
|
||||
# GET /relay/following
|
||||
def following(%{assigns: %{relay: true}} = conn, _params) do
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> put_view(UserView)
|
||||
|> render("following.json", %{user: Relay.get_actor()})
|
||||
def relay_following(conn, _params) do
|
||||
with %{halted: false} = conn <- FederatingPlug.call(conn, []) do
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> put_view(UserView)
|
||||
|> render("following.json", %{user: Relay.get_actor()})
|
||||
end
|
||||
end
|
||||
|
||||
def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
|
||||
|
|
@ -164,11 +181,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
end
|
||||
|
||||
# GET /relay/followers
|
||||
def followers(%{assigns: %{relay: true}} = conn, _params) do
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> put_view(UserView)
|
||||
|> render("followers.json", %{user: Relay.get_actor()})
|
||||
def relay_followers(conn, _params) do
|
||||
with %{halted: false} = conn <- FederatingPlug.call(conn, []) do
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> put_view(UserView)
|
||||
|> render("followers.json", %{user: Relay.get_actor()})
|
||||
end
|
||||
end
|
||||
|
||||
def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
|
||||
|
|
@ -200,13 +219,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
end
|
||||
end
|
||||
|
||||
def outbox(conn, %{"nickname" => nickname, "page" => page?} = params)
|
||||
def outbox(
|
||||
%{assigns: %{user: for_user}} = conn,
|
||||
%{"nickname" => nickname, "page" => page?} = params
|
||||
)
|
||||
when page? in [true, "true"] do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, user} <- User.ensure_keys_present(user) do
|
||||
activities =
|
||||
if params["max_id"] do
|
||||
ActivityPub.fetch_user_activities(user, nil, %{
|
||||
ActivityPub.fetch_user_activities(user, for_user, %{
|
||||
"max_id" => params["max_id"],
|
||||
# This is a hack because postgres generates inefficient queries when filtering by
|
||||
# 'Answer', poll votes will be hidden by the visibility filter in this case anyway
|
||||
|
|
@ -214,7 +236,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
"limit" => 10
|
||||
})
|
||||
else
|
||||
ActivityPub.fetch_user_activities(user, nil, %{
|
||||
ActivityPub.fetch_user_activities(user, for_user, %{
|
||||
"limit" => 10,
|
||||
"include_poll_votes" => true
|
||||
})
|
||||
|
|
@ -255,8 +277,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
json(conn, "ok")
|
||||
end
|
||||
|
||||
# only accept relayed Creates
|
||||
def inbox(conn, %{"type" => "Create"} = params) do
|
||||
# POST /relay/inbox -or- POST /internal/fetch/inbox
|
||||
def inbox(conn, params) do
|
||||
if params["type"] == "Create" && FederatingPlug.federating?() do
|
||||
post_inbox_relayed_create(conn, params)
|
||||
else
|
||||
post_inbox_fallback(conn, params)
|
||||
end
|
||||
end
|
||||
|
||||
defp post_inbox_relayed_create(conn, params) do
|
||||
Logger.debug(
|
||||
"Signature missing or not from author, relayed Create message, fetching object from source"
|
||||
)
|
||||
|
|
@ -266,10 +296,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
json(conn, "ok")
|
||||
end
|
||||
|
||||
def inbox(conn, params) do
|
||||
defp post_inbox_fallback(conn, params) do
|
||||
headers = Enum.into(conn.req_headers, %{})
|
||||
|
||||
if String.contains?(headers["signature"], params["actor"]) do
|
||||
if headers["signature"] && params["actor"] &&
|
||||
String.contains?(headers["signature"], params["actor"]) do
|
||||
Logger.debug(
|
||||
"Signature validation error for: #{params["actor"]}, make sure you are forwarding the HTTP Host header!"
|
||||
)
|
||||
|
|
@ -277,7 +308,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
Logger.debug(inspect(conn.req_headers))
|
||||
end
|
||||
|
||||
json(conn, dgettext("errors", "error"))
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(dgettext("errors", "error"))
|
||||
end
|
||||
|
||||
defp represent_service_actor(%User{} = user, conn) do
|
||||
|
|
@ -311,10 +344,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|> render("user.json", %{user: user})
|
||||
end
|
||||
|
||||
def whoami(_conn, _params), do: {:error, :not_found}
|
||||
|
||||
def read_inbox(
|
||||
%{assigns: %{user: %{nickname: nickname} = user}} = conn,
|
||||
%{assigns: %{user: %User{nickname: nickname} = user}} = conn,
|
||||
%{"nickname" => nickname, "page" => page?} = params
|
||||
)
|
||||
when page? in [true, "true"] do
|
||||
|
|
@ -337,7 +368,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
})
|
||||
end
|
||||
|
||||
def read_inbox(%{assigns: %{user: %{nickname: nickname} = user}} = conn, %{
|
||||
def read_inbox(%{assigns: %{user: %User{nickname: nickname} = user}} = conn, %{
|
||||
"nickname" => nickname
|
||||
}) do
|
||||
with {:ok, user} <- User.ensure_keys_present(user) do
|
||||
|
|
@ -348,15 +379,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
end
|
||||
end
|
||||
|
||||
def read_inbox(%{assigns: %{user: nil}} = conn, %{"nickname" => nickname}) do
|
||||
err = dgettext("errors", "can't read inbox of %{nickname}", nickname: nickname)
|
||||
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json(err)
|
||||
end
|
||||
|
||||
def read_inbox(%{assigns: %{user: %{nickname: as_nickname}}} = conn, %{
|
||||
def read_inbox(%{assigns: %{user: %User{nickname: as_nickname}}} = conn, %{
|
||||
"nickname" => nickname
|
||||
}) do
|
||||
err =
|
||||
|
|
@ -370,7 +393,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|> json(err)
|
||||
end
|
||||
|
||||
def handle_user_activity(user, %{"type" => "Create"} = params) do
|
||||
defp handle_user_activity(%User{} = user, %{"type" => "Create"} = params) do
|
||||
object =
|
||||
params["object"]
|
||||
|> Map.merge(Map.take(params, ["to", "cc"]))
|
||||
|
|
@ -386,7 +409,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
})
|
||||
end
|
||||
|
||||
def handle_user_activity(user, %{"type" => "Delete"} = params) do
|
||||
defp handle_user_activity(%User{} = user, %{"type" => "Delete"} = params) do
|
||||
with %Object{} = object <- Object.normalize(params["object"]),
|
||||
true <- user.is_moderator || user.ap_id == object.data["actor"],
|
||||
{:ok, delete} <- ActivityPub.delete(object) do
|
||||
|
|
@ -396,7 +419,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_user_activity(user, %{"type" => "Like"} = params) do
|
||||
defp handle_user_activity(%User{} = user, %{"type" => "Like"} = params) do
|
||||
with %Object{} = object <- Object.normalize(params["object"]),
|
||||
{:ok, activity, _object} <- ActivityPub.like(user, object) do
|
||||
{:ok, activity}
|
||||
|
|
@ -405,7 +428,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_user_activity(_, _) do
|
||||
defp handle_user_activity(_, _) do
|
||||
{:error, dgettext("errors", "Unhandled activity type")}
|
||||
end
|
||||
|
||||
|
|
@ -434,7 +457,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
end
|
||||
end
|
||||
|
||||
def update_outbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = _) do
|
||||
def update_outbox(%{assigns: %{user: %User{} = user}} = conn, %{"nickname" => nickname}) do
|
||||
err =
|
||||
dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",
|
||||
nickname: nickname,
|
||||
|
|
@ -446,13 +469,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|> json(err)
|
||||
end
|
||||
|
||||
def errors(conn, {:error, :not_found}) do
|
||||
defp errors(conn, {:error, :not_found}) do
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(dgettext("errors", "Not found"))
|
||||
end
|
||||
|
||||
def errors(conn, _e) do
|
||||
defp errors(conn, _e) do
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|> json(dgettext("errors", "error"))
|
||||
|
|
@ -492,7 +515,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
- HTTP Code: 201 Created
|
||||
- HTTP Body: ActivityPub object to be inserted into another's `attachment` field
|
||||
"""
|
||||
def upload_media(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
|
||||
def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do
|
||||
with {:ok, object} <-
|
||||
ActivityPub.upload(
|
||||
file,
|
||||
|
|
|
|||
|
|
@ -60,15 +60,28 @@ defmodule Pleroma.Web.ActivityPub.Relay do
|
|||
|
||||
def publish(_), do: {:error, "Not implemented"}
|
||||
|
||||
@spec list() :: {:ok, [String.t()]} | {:error, any()}
|
||||
def list do
|
||||
@spec list(boolean()) :: {:ok, [String.t()]} | {:error, any()}
|
||||
def list(with_not_accepted \\ false) do
|
||||
with %User{} = user <- get_actor() do
|
||||
list =
|
||||
accepted =
|
||||
user
|
||||
|> User.following()
|
||||
|> Enum.map(fn entry -> URI.parse(entry).host end)
|
||||
|> Enum.uniq()
|
||||
|
||||
list =
|
||||
if with_not_accepted do
|
||||
without_accept =
|
||||
user
|
||||
|> Pleroma.Activity.following_requests_for_actor()
|
||||
|> Enum.map(fn a -> URI.parse(a.data["object"]).host <> " (no Accept received)" end)
|
||||
|> Enum.uniq()
|
||||
|
||||
accepted ++ without_accept
|
||||
else
|
||||
accepted
|
||||
end
|
||||
|
||||
{:ok, list}
|
||||
else
|
||||
error -> format_error(error)
|
||||
|
|
|
|||
|
|
@ -1108,13 +1108,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
end
|
||||
|
||||
def add_mention_tags(object) do
|
||||
mentions =
|
||||
object
|
||||
|> Utils.get_notified_from_object()
|
||||
|> Enum.map(&build_mention_tag/1)
|
||||
{enabled_receivers, disabled_receivers} = Utils.get_notified_from_object(object)
|
||||
potential_receivers = enabled_receivers ++ disabled_receivers
|
||||
mentions = Enum.map(potential_receivers, &build_mention_tag/1)
|
||||
|
||||
tags = object["tag"] || []
|
||||
|
||||
Map.put(object, "tag", tags ++ mentions)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -440,22 +440,19 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
|> update(set: [data: fragment("jsonb_set(data, '{state}', ?)", ^state)])
|
||||
|> Repo.update_all([])
|
||||
|
||||
User.set_follow_state_cache(actor, object, state)
|
||||
|
||||
activity = Activity.get_by_id(activity.id)
|
||||
|
||||
{:ok, activity}
|
||||
end
|
||||
|
||||
def update_follow_state(
|
||||
%Activity{data: %{"actor" => actor, "object" => object}} = activity,
|
||||
%Activity{} = activity,
|
||||
state
|
||||
) do
|
||||
new_data = Map.put(activity.data, "state", state)
|
||||
changeset = Changeset.change(activity, data: new_data)
|
||||
|
||||
with {:ok, activity} <- Repo.update(changeset) do
|
||||
User.set_follow_state_cache(actor, object, state)
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
|
@ -784,45 +781,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
|
||||
defp build_flag_object(_), do: []
|
||||
|
||||
@doc """
|
||||
Fetches the OrderedCollection/OrderedCollectionPage from `from`, limiting the amount of pages fetched after
|
||||
the first one to `pages_left` pages.
|
||||
If the amount of pages is higher than the collection has, it returns whatever was there.
|
||||
"""
|
||||
def fetch_ordered_collection(from, pages_left, acc \\ []) do
|
||||
with {:ok, response} <- Tesla.get(from),
|
||||
{:ok, collection} <- Jason.decode(response.body) do
|
||||
case collection["type"] do
|
||||
"OrderedCollection" ->
|
||||
# If we've encountered the OrderedCollection and not the page,
|
||||
# just call the same function on the page address
|
||||
fetch_ordered_collection(collection["first"], pages_left)
|
||||
|
||||
"OrderedCollectionPage" ->
|
||||
if pages_left > 0 do
|
||||
# There are still more pages
|
||||
if Map.has_key?(collection, "next") do
|
||||
# There are still more pages, go deeper saving what we have into the accumulator
|
||||
fetch_ordered_collection(
|
||||
collection["next"],
|
||||
pages_left - 1,
|
||||
acc ++ collection["orderedItems"]
|
||||
)
|
||||
else
|
||||
# No more pages left, just return whatever we already have
|
||||
acc ++ collection["orderedItems"]
|
||||
end
|
||||
else
|
||||
# Got the amount of pages needed, add them all to the accumulator
|
||||
acc ++ collection["orderedItems"]
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:error, "Not an OrderedCollection or OrderedCollectionPage"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#### Report-related helpers
|
||||
def get_reports(params, page, page_size) do
|
||||
params =
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||
public_key = :public_key.pem_encode([public_key])
|
||||
user = User.sanitize_html(user)
|
||||
|
||||
endpoints = render("endpoints.json", %{user: user})
|
||||
|
||||
|
|
@ -81,12 +82,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
fields =
|
||||
user
|
||||
|> User.fields()
|
||||
|> Enum.map(fn %{"name" => name, "value" => value} ->
|
||||
%{
|
||||
"name" => Pleroma.HTML.strip_tags(name),
|
||||
"value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
|
||||
}
|
||||
end)
|
||||
|> Enum.map(&Map.put(&1, "type", "PropertyValue"))
|
||||
|
||||
%{
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
|||
def is_list?(%{data: %{"listMessage" => _}}), do: true
|
||||
def is_list?(_), do: false
|
||||
|
||||
@spec visible_for_user?(Activity.t(), User.t() | nil) :: boolean()
|
||||
def visible_for_user?(%{actor: ap_id}, %User{ap_id: ap_id}), do: true
|
||||
|
||||
def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{} = user) do
|
||||
|
|
@ -55,14 +56,21 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
|||
|
||||
def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false
|
||||
|
||||
def visible_for_user?(activity, nil) do
|
||||
is_public?(activity)
|
||||
def visible_for_user?(%{local: local} = activity, nil) do
|
||||
cfg_key =
|
||||
if local,
|
||||
do: :local,
|
||||
else: :remote
|
||||
|
||||
if Pleroma.Config.get([:restrict_unauthenticated, :activities, cfg_key]),
|
||||
do: false,
|
||||
else: is_public?(activity)
|
||||
end
|
||||
|
||||
def visible_for_user?(activity, user) do
|
||||
x = [user.ap_id | User.following(user)]
|
||||
y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || [])
|
||||
visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y))
|
||||
is_public?(activity) || Enum.any?(x, &(&1 in y))
|
||||
end
|
||||
|
||||
def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:accounts"], admin: true}
|
||||
when action in [:list_users, :user_show, :right_get]
|
||||
when action in [:list_users, :user_show, :right_get, :show_user_credentials]
|
||||
)
|
||||
|
||||
plug(
|
||||
|
|
@ -54,7 +54,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
:tag_users,
|
||||
:untag_users,
|
||||
:right_add,
|
||||
:right_delete
|
||||
:right_delete,
|
||||
:update_user_credentials
|
||||
]
|
||||
)
|
||||
|
||||
|
|
@ -658,6 +659,52 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
json_response(conn, :no_content, "")
|
||||
end
|
||||
|
||||
@doc "Show a given user's credentials"
|
||||
def show_user_credentials(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("credentials.json", %{user: user, for: admin})
|
||||
else
|
||||
_ -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
@doc "Updates a given user"
|
||||
def update_user_credentials(
|
||||
%{assigns: %{user: admin}} = conn,
|
||||
%{"nickname" => nickname} = params
|
||||
) do
|
||||
with {_, user} <- {:user, User.get_cached_by_nickname(nickname)},
|
||||
{:ok, _user} <-
|
||||
User.update_as_admin(user, params) do
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: [user],
|
||||
action: "updated_users"
|
||||
})
|
||||
|
||||
if params["password"] do
|
||||
User.force_password_reset_async(user)
|
||||
end
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subject: [user],
|
||||
action: "force_password_reset"
|
||||
})
|
||||
|
||||
json(conn, %{status: "success"})
|
||||
else
|
||||
{:error, changeset} ->
|
||||
{_, {error, _}} = Enum.at(changeset.errors, 0)
|
||||
json(conn, %{error: "New password #{error}."})
|
||||
|
||||
_ ->
|
||||
json(conn, %{error: "Unable to change password."})
|
||||
end
|
||||
end
|
||||
|
||||
def list_reports(conn, params) do
|
||||
{page, page_size} = page_params(params)
|
||||
|
||||
|
|
@ -745,14 +792,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def list_statuses(%{assigns: %{user: admin}} = conn, params) do
|
||||
def list_statuses(%{assigns: %{user: _admin}} = conn, params) do
|
||||
godmode = params["godmode"] == "true" || params["godmode"] == true
|
||||
local_only = params["local_only"] == "true" || params["local_only"] == true
|
||||
with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
|
||||
{page, page_size} = page_params(params)
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_statuses(admin, %{
|
||||
ActivityPub.fetch_statuses(nil, %{
|
||||
"godmode" => godmode,
|
||||
"local_only" => local_only,
|
||||
"limit" => page_size,
|
||||
|
|
@ -834,7 +881,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
configs = ConfigDB.get_all_as_keyword()
|
||||
|
||||
merged =
|
||||
Config.Holder.config()
|
||||
Config.Holder.default_config()
|
||||
|> ConfigDB.merge(configs)
|
||||
|> Enum.map(fn {group, values} ->
|
||||
Enum.map(values, fn {key, value} ->
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
defmodule Pleroma.Web.AdminAPI.AccountView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.AdminAPI.AccountView
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
|
@ -24,9 +23,47 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
|||
}
|
||||
end
|
||||
|
||||
def render("credentials.json", %{user: user, for: for_user}) do
|
||||
user = User.sanitize_html(user, User.html_filter_policy(for_user))
|
||||
avatar = User.avatar_url(user) |> MediaProxy.url()
|
||||
banner = User.banner_url(user) |> MediaProxy.url()
|
||||
background = image_url(user.background) |> MediaProxy.url()
|
||||
|
||||
user
|
||||
|> Map.take([
|
||||
:id,
|
||||
:bio,
|
||||
:email,
|
||||
:fields,
|
||||
:name,
|
||||
:nickname,
|
||||
:locked,
|
||||
:no_rich_text,
|
||||
:default_scope,
|
||||
:hide_follows,
|
||||
:hide_followers_count,
|
||||
:hide_follows_count,
|
||||
:hide_followers,
|
||||
:hide_favorites,
|
||||
:allow_following_move,
|
||||
:show_role,
|
||||
:skip_thread_containment,
|
||||
:pleroma_settings_store,
|
||||
:raw_fields,
|
||||
:discoverable,
|
||||
:actor_type
|
||||
])
|
||||
|> Map.merge(%{
|
||||
"avatar" => avatar,
|
||||
"banner" => banner,
|
||||
"background" => background
|
||||
})
|
||||
end
|
||||
|
||||
def render("show.json", %{user: user}) do
|
||||
avatar = User.avatar_url(user) |> MediaProxy.url()
|
||||
display_name = HTML.strip_tags(user.name || user.nickname)
|
||||
display_name = Pleroma.HTML.strip_tags(user.name || user.nickname)
|
||||
user = User.sanitize_html(user, FastSanitize.Sanitizer.StripTags)
|
||||
|
||||
%{
|
||||
"id" => user.id,
|
||||
|
|
@ -104,4 +141,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
|||
""
|
||||
end
|
||||
end
|
||||
|
||||
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
|
||||
defp image_url(_), do: nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -358,7 +358,7 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
def thread_muted?(%{id: nil} = _user, _activity), do: false
|
||||
|
||||
def thread_muted?(user, activity) do
|
||||
ThreadMute.check_muted(user.id, activity.data["context"]) != []
|
||||
ThreadMute.exists?(user.id, activity.data["context"])
|
||||
end
|
||||
|
||||
def report(user, %{"account_id" => account_id} = data) do
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
def format_input(text, "text/markdown", options) do
|
||||
text
|
||||
|> Formatter.mentions_escape(options)
|
||||
|> Earmark.as_html!()
|
||||
|> Earmark.as_html!(%Earmark.Options{renderer: Pleroma.EarmarkRenderer})
|
||||
|> Formatter.linkify(options)
|
||||
|> Formatter.html_escape("text/html")
|
||||
end
|
||||
|
|
@ -591,7 +591,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
limit = Pleroma.Config.get([:instance, :limit])
|
||||
length = String.length(full_payload)
|
||||
|
||||
if length < limit do
|
||||
if length <= limit do
|
||||
:ok
|
||||
else
|
||||
{:error, dgettext("errors", "The status is over the character limit")}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,12 @@ defmodule Pleroma.Web.ControllerHelper do
|
|||
|
||||
defp param_to_integer(_, default), do: default
|
||||
|
||||
def add_link_headers(conn, activities, extra_params \\ %{}) do
|
||||
def add_link_headers(conn, activities, extra_params \\ %{})
|
||||
|
||||
def add_link_headers(%{assigns: %{skip_link_headers: true}} = conn, _activities, _extra_params),
|
||||
do: conn
|
||||
|
||||
def add_link_headers(conn, activities, extra_params) do
|
||||
case List.last(activities) do
|
||||
%{id: max_id} ->
|
||||
params =
|
||||
|
|
@ -87,7 +92,8 @@ defmodule Pleroma.Web.ControllerHelper do
|
|||
render_error(conn, :not_implemented, "Can't display this activity")
|
||||
end
|
||||
|
||||
@spec put_in_if_exist(map(), atom() | String.t(), any) :: map()
|
||||
def put_in_if_exist(map, _key, nil), do: map
|
||||
def put_in_if_exist(map, key, value), do: put_in(map, key, value)
|
||||
@spec put_if_exist(map(), atom() | String.t(), any) :: map()
|
||||
def put_if_exist(map, _key, nil), do: map
|
||||
|
||||
def put_if_exist(map, key, value), do: Map.put(map, key, value)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ defmodule Pleroma.Web.Endpoint do
|
|||
plug(Pleroma.Plugs.HTTPSecurityPlug)
|
||||
plug(Pleroma.Plugs.UploadedMedia)
|
||||
|
||||
@static_cache_control "public max-age=86400 must-revalidate"
|
||||
@static_cache_control "public, no-cache"
|
||||
|
||||
# InstanceStatic needs to be before Plug.Static to be able to override shipped-static files
|
||||
# If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well
|
||||
|
|
|
|||
|
|
@ -9,18 +9,18 @@ defmodule Pleroma.Web.Feed.TagController do
|
|||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.Feed.FeedView
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [put_in_if_exist: 3]
|
||||
import Pleroma.Web.ControllerHelper, only: [put_if_exist: 3]
|
||||
|
||||
def feed(conn, %{"tag" => raw_tag} = params) do
|
||||
{format, tag} = parse_tag(raw_tag)
|
||||
|
||||
activities =
|
||||
%{"type" => ["Create"], "tag" => tag}
|
||||
|> put_in_if_exist("max_id", params["max_id"])
|
||||
|> put_if_exist("max_id", params["max_id"])
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("application/atom+xml")
|
||||
|> put_resp_content_type("application/#{format}+xml")
|
||||
|> put_view(FeedView)
|
||||
|> render("tag.#{format}",
|
||||
activities: activities,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ defmodule Pleroma.Web.Feed.UserController do
|
|||
alias Pleroma.Web.ActivityPub.ActivityPubController
|
||||
alias Pleroma.Web.Feed.FeedView
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [put_in_if_exist: 3]
|
||||
import Pleroma.Web.ControllerHelper, only: [put_if_exist: 3]
|
||||
|
||||
plug(Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect])
|
||||
|
||||
|
|
@ -25,7 +25,12 @@ defmodule Pleroma.Web.Feed.UserController do
|
|||
|
||||
def feed_redirect(%{assigns: %{format: format}} = conn, _params)
|
||||
when format in ["json", "activity+json"] do
|
||||
ActivityPubController.call(conn, :user)
|
||||
with %{halted: false} = conn <-
|
||||
Pleroma.Plugs.EnsureAuthenticatedPlug.call(conn,
|
||||
unless_func: &Pleroma.Web.FederatingPlug.federating?/0
|
||||
) do
|
||||
ActivityPubController.call(conn, :user)
|
||||
end
|
||||
end
|
||||
|
||||
def feed_redirect(conn, %{"nickname" => nickname}) do
|
||||
|
|
@ -35,19 +40,28 @@ defmodule Pleroma.Web.Feed.UserController do
|
|||
end
|
||||
|
||||
def feed(conn, %{"nickname" => nickname} = params) do
|
||||
format = get_format(conn)
|
||||
|
||||
format =
|
||||
if format in ["rss", "atom"] do
|
||||
format
|
||||
else
|
||||
"atom"
|
||||
end
|
||||
|
||||
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
|
||||
activities =
|
||||
%{
|
||||
"type" => ["Create"],
|
||||
"actor_id" => user.ap_id
|
||||
}
|
||||
|> put_in_if_exist("max_id", params["max_id"])
|
||||
|> put_if_exist("max_id", params["max_id"])
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("application/atom+xml")
|
||||
|> put_resp_content_type("application/#{format}+xml")
|
||||
|> put_view(FeedView)
|
||||
|> render("user.xml",
|
||||
|> render("user.#{format}",
|
||||
user: user,
|
||||
activities: activities,
|
||||
feed_config: Pleroma.Config.get([:feed])
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
import Pleroma.Web.ControllerHelper,
|
||||
only: [add_link_headers: 2, truthy_param?: 1, assign_account_by_id: 2, json_response: 3]
|
||||
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Plugs.RateLimiter
|
||||
alias Pleroma.User
|
||||
|
|
@ -60,14 +59,18 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
|
||||
plug(
|
||||
Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
when action != :create
|
||||
when action not in [:create, :show, :statuses]
|
||||
)
|
||||
|
||||
@relations [:follow, :unfollow]
|
||||
@relationship_actions [:follow, :unfollow]
|
||||
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock)a
|
||||
|
||||
plug(RateLimiter, [name: :relations_id_action, params: ["id", "uri"]] when action in @relations)
|
||||
plug(RateLimiter, [name: :relations_actions] when action in @relations)
|
||||
plug(
|
||||
RateLimiter,
|
||||
[name: :relation_id_action, params: ["id", "uri"]] when action in @relationship_actions
|
||||
)
|
||||
|
||||
plug(RateLimiter, [name: :relations_actions] when action in @relationship_actions)
|
||||
plug(RateLimiter, [name: :app_account_creation] when action == :create)
|
||||
plug(:assign_account_by_id when action in @needs_account)
|
||||
|
||||
|
|
@ -76,7 +79,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
@doc "POST /api/v1/accounts"
|
||||
def create(
|
||||
%{assigns: %{app: app}} = conn,
|
||||
%{"username" => nickname, "email" => _, "password" => _, "agreement" => true} = params
|
||||
%{"username" => nickname, "password" => _, "agreement" => true} = params
|
||||
) do
|
||||
params =
|
||||
params
|
||||
|
|
@ -93,7 +96,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
|> Map.put("bio", params["bio"] || "")
|
||||
|> Map.put("confirm", params["password"])
|
||||
|
||||
with {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
|
||||
with :ok <- validate_email_param(params),
|
||||
{:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
|
||||
{:ok, token} <- Token.create_token(app, user, %{scopes: app.scopes}) do
|
||||
json(conn, %{
|
||||
token_type: "Bearer",
|
||||
|
|
@ -114,6 +118,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
render_error(conn, :forbidden, "Invalid credentials")
|
||||
end
|
||||
|
||||
defp validate_email_param(%{"email" => _}), do: :ok
|
||||
|
||||
defp validate_email_param(_) do
|
||||
case Pleroma.Config.get([:instance, :account_activation_required]) do
|
||||
true -> {:error, %{"error" => "Missing parameters"}}
|
||||
_ -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/accounts/verify_credentials"
|
||||
def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
||||
chat_token = Phoenix.Token.sign(conn, "user socket", user.id)
|
||||
|
|
@ -130,17 +143,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
|
||||
user = original_user
|
||||
|
||||
params =
|
||||
if Map.has_key?(params, "fields_attributes") do
|
||||
Map.update!(params, "fields_attributes", fn fields ->
|
||||
fields
|
||||
|> normalize_fields_attributes()
|
||||
|> Enum.filter(fn %{"name" => n} -> n != "" end)
|
||||
end)
|
||||
else
|
||||
params
|
||||
end
|
||||
|
||||
user_params =
|
||||
[
|
||||
:no_rich_text,
|
||||
|
|
@ -159,46 +161,20 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)})
|
||||
end)
|
||||
|> add_if_present(params, "display_name", :name)
|
||||
|> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value, user)} end)
|
||||
|> add_if_present(params, "avatar", :avatar, fn value ->
|
||||
with %Plug.Upload{} <- value,
|
||||
{:ok, object} <- ActivityPub.upload(value, type: :avatar) do
|
||||
{:ok, object.data}
|
||||
end
|
||||
end)
|
||||
|> add_if_present(params, "header", :banner, fn value ->
|
||||
with %Plug.Upload{} <- value,
|
||||
{:ok, object} <- ActivityPub.upload(value, type: :banner) do
|
||||
{:ok, object.data}
|
||||
end
|
||||
end)
|
||||
|> add_if_present(params, "pleroma_background_image", :background, fn value ->
|
||||
with %Plug.Upload{} <- value,
|
||||
{:ok, object} <- ActivityPub.upload(value, type: :background) do
|
||||
{:ok, object.data}
|
||||
end
|
||||
end)
|
||||
|> add_if_present(params, "fields_attributes", :fields, fn fields ->
|
||||
fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end)
|
||||
|
||||
{:ok, fields}
|
||||
end)
|
||||
|> add_if_present(params, "fields_attributes", :raw_fields)
|
||||
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value ->
|
||||
{:ok, Map.merge(user.pleroma_settings_store, value)}
|
||||
end)
|
||||
|> add_if_present(params, "note", :bio)
|
||||
|> add_if_present(params, "avatar", :avatar)
|
||||
|> add_if_present(params, "header", :banner)
|
||||
|> add_if_present(params, "pleroma_background_image", :background)
|
||||
|> add_if_present(
|
||||
params,
|
||||
"fields_attributes",
|
||||
:raw_fields,
|
||||
&{:ok, normalize_fields_attributes(&1)}
|
||||
)
|
||||
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store)
|
||||
|> add_if_present(params, "default_scope", :default_scope)
|
||||
|> add_if_present(params, "actor_type", :actor_type)
|
||||
|
||||
emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "")
|
||||
|
||||
user_emojis =
|
||||
user
|
||||
|> Map.get(:emoji, [])
|
||||
|> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text))
|
||||
|> Enum.dedup()
|
||||
|
||||
user_params = Map.put(user_params, :emoji, user_emojis)
|
||||
changeset = User.update_changeset(user, user_params)
|
||||
|
||||
with {:ok, user} <- User.update_and_set_cache(changeset) do
|
||||
|
|
@ -249,7 +225,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
|
||||
@doc "GET /api/v1/accounts/:id/statuses"
|
||||
def statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user) do
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user),
|
||||
true <- User.visible_for?(user, reading_user) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("tag", params["tagged"])
|
||||
|
|
@ -261,6 +238,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
|> add_link_headers(activities)
|
||||
|> put_view(StatusView)
|
||||
|> render("index.json", activities: activities, for: reading_user, as: :activity)
|
||||
else
|
||||
_e -> render_error(conn, :not_found, "Can't find user")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -86,6 +86,6 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
|||
@spec get_or_make_app() :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
||||
defp get_or_make_app do
|
||||
%{client_name: @local_mastodon_name, redirect_uris: "."}
|
||||
|> App.get_or_make(["read", "write", "follow", "push"])
|
||||
|> App.get_or_make(["read", "write", "follow", "push", "admin"])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
%{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark]
|
||||
)
|
||||
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action not in [:index, :show])
|
||||
|
||||
@rate_limited_status_actions ~w(reblog unreblog favourite unfavourite create delete)a
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct])
|
||||
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list)
|
||||
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :public)
|
||||
|
||||
plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
|
||||
|
||||
|
|
@ -75,17 +75,30 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||
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()
|
||||
cfg_key =
|
||||
if local_only do
|
||||
:local
|
||||
else
|
||||
:federated
|
||||
end
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities, %{"local" => local_only})
|
||||
|> render("index.json", activities: activities, for: user, as: :activity)
|
||||
restrict? = Pleroma.Config.get([:restrict_unauthenticated, :timelines, cfg_key])
|
||||
|
||||
if not (restrict? and is_nil(user)) do
|
||||
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()
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities, %{"local" => local_only})
|
||||
|> render("index.json", activities: activities, for: user, as: :activity)
|
||||
else
|
||||
render_error(conn, :unauthorized, "authorization required for timeline view")
|
||||
end
|
||||
end
|
||||
|
||||
def hashtag_fetching(params, user, local_only) do
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
|||
|
||||
user
|
||||
|> Notification.for_user_query(options)
|
||||
|> restrict(:include_types, options)
|
||||
|> restrict(:exclude_types, options)
|
||||
|> restrict(:account_ap_id, options)
|
||||
|> Pagination.fetch_paginated(params)
|
||||
|
|
@ -69,10 +70,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
|||
defp cast_params(params) do
|
||||
param_types = %{
|
||||
exclude_types: {:array, :string},
|
||||
include_types: {:array, :string},
|
||||
exclude_visibilities: {:array, :string},
|
||||
reblogs: :boolean,
|
||||
with_muted: :boolean,
|
||||
with_move: :boolean,
|
||||
account_ap_id: :string
|
||||
}
|
||||
|
||||
|
|
@ -80,14 +81,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
|||
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)
|
||||
defp restrict(query, :include_types, %{include_types: mastodon_types = [_ | _]}) do
|
||||
ap_types = convert_and_filter_mastodon_types(mastodon_types)
|
||||
|
||||
query
|
||||
|> where([q, a], not fragment("? @> ARRAY[?->>'type']::varchar[]", ^ap_types, a.data))
|
||||
where(query, [q, a], fragment("? @> ARRAY[?->>'type']::varchar[]", ^ap_types, a.data))
|
||||
end
|
||||
|
||||
defp restrict(query, :exclude_types, %{exclude_types: mastodon_types = [_ | _]}) do
|
||||
ap_types = convert_and_filter_mastodon_types(mastodon_types)
|
||||
|
||||
where(query, [q, a], not fragment("? @> ARRAY[?->>'type']::varchar[]", ^ap_types, a.data))
|
||||
end
|
||||
|
||||
defp restrict(query, :account_ap_id, %{account_ap_id: account_ap_id}) do
|
||||
|
|
@ -95,4 +98,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
|||
end
|
||||
|
||||
defp restrict(query, _, _), do: query
|
||||
|
||||
defp convert_and_filter_mastodon_types(types) do
|
||||
types
|
||||
|> Enum.map(&Activity.from_mastodon_notification_type/1)
|
||||
|> Enum.filter(& &1)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,13 +5,28 @@
|
|||
defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.FollowingRelationship
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
def render("index.json", %{users: users} = opts) do
|
||||
relationships_opt =
|
||||
cond do
|
||||
Map.has_key?(opts, :relationships) ->
|
||||
opts[:relationships]
|
||||
|
||||
is_nil(opts[:for]) ->
|
||||
UserRelationship.view_relationships_option(nil, [])
|
||||
|
||||
true ->
|
||||
UserRelationship.view_relationships_option(opts[:for], users)
|
||||
end
|
||||
|
||||
opts = Map.put(opts, :relationships, relationships_opt)
|
||||
|
||||
users
|
||||
|> render_many(AccountView, "show.json", opts)
|
||||
|> Enum.filter(&Enum.any?/1)
|
||||
|
|
@ -36,37 +51,111 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
%{}
|
||||
end
|
||||
|
||||
def render("relationship.json", %{user: %User{} = user, target: %User{} = target}) do
|
||||
follow_state = User.get_cached_follow_state(user, target)
|
||||
def render(
|
||||
"relationship.json",
|
||||
%{user: %User{} = reading_user, target: %User{} = target} = opts
|
||||
) do
|
||||
user_relationships = get_in(opts, [:relationships, :user_relationships])
|
||||
following_relationships = get_in(opts, [:relationships, :following_relationships])
|
||||
|
||||
requested =
|
||||
if follow_state && !User.following?(user, target) do
|
||||
follow_state == "pending"
|
||||
follow_state =
|
||||
if following_relationships do
|
||||
user_to_target_following_relation =
|
||||
FollowingRelationship.find(following_relationships, reading_user, target)
|
||||
|
||||
User.get_follow_state(reading_user, target, user_to_target_following_relation)
|
||||
else
|
||||
false
|
||||
User.get_follow_state(reading_user, target)
|
||||
end
|
||||
|
||||
followed_by =
|
||||
if following_relationships do
|
||||
case FollowingRelationship.find(following_relationships, target, reading_user) do
|
||||
%{state: "accept"} -> true
|
||||
_ -> false
|
||||
end
|
||||
else
|
||||
User.following?(target, reading_user)
|
||||
end
|
||||
|
||||
# NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags
|
||||
%{
|
||||
id: to_string(target.id),
|
||||
following: User.following?(user, target),
|
||||
followed_by: User.following?(target, user),
|
||||
blocking: User.blocks_user?(user, target),
|
||||
blocked_by: User.blocks_user?(target, user),
|
||||
muting: User.mutes?(user, target),
|
||||
muting_notifications: User.muted_notifications?(user, target),
|
||||
subscribing: User.subscribed_to?(user, target),
|
||||
requested: requested,
|
||||
domain_blocking: User.blocks_domain?(user, target),
|
||||
showing_reblogs: User.showing_reblogs?(user, target),
|
||||
following: follow_state == "accept",
|
||||
followed_by: followed_by,
|
||||
blocking:
|
||||
UserRelationship.exists?(
|
||||
user_relationships,
|
||||
:block,
|
||||
reading_user,
|
||||
target,
|
||||
&User.blocks_user?(&1, &2)
|
||||
),
|
||||
blocked_by:
|
||||
UserRelationship.exists?(
|
||||
user_relationships,
|
||||
:block,
|
||||
target,
|
||||
reading_user,
|
||||
&User.blocks_user?(&1, &2)
|
||||
),
|
||||
muting:
|
||||
UserRelationship.exists?(
|
||||
user_relationships,
|
||||
:mute,
|
||||
reading_user,
|
||||
target,
|
||||
&User.mutes?(&1, &2)
|
||||
),
|
||||
muting_notifications:
|
||||
UserRelationship.exists?(
|
||||
user_relationships,
|
||||
:notification_mute,
|
||||
reading_user,
|
||||
target,
|
||||
&User.muted_notifications?(&1, &2)
|
||||
),
|
||||
subscribing:
|
||||
UserRelationship.exists?(
|
||||
user_relationships,
|
||||
:inverse_subscription,
|
||||
target,
|
||||
reading_user,
|
||||
&User.subscribed_to?(&2, &1)
|
||||
),
|
||||
requested: follow_state == "pending",
|
||||
domain_blocking: User.blocks_domain?(reading_user, target),
|
||||
showing_reblogs:
|
||||
not UserRelationship.exists?(
|
||||
user_relationships,
|
||||
:reblog_mute,
|
||||
reading_user,
|
||||
target,
|
||||
&User.muting_reblogs?(&1, &2)
|
||||
),
|
||||
endorsed: false
|
||||
}
|
||||
end
|
||||
|
||||
def render("relationships.json", %{user: user, targets: targets}) do
|
||||
render_many(targets, AccountView, "relationship.json", user: user, as: :target)
|
||||
def render("relationships.json", %{user: user, targets: targets} = opts) do
|
||||
relationships_opt =
|
||||
cond do
|
||||
Map.has_key?(opts, :relationships) ->
|
||||
opts[:relationships]
|
||||
|
||||
is_nil(opts[:for]) ->
|
||||
UserRelationship.view_relationships_option(nil, [])
|
||||
|
||||
true ->
|
||||
UserRelationship.view_relationships_option(user, targets)
|
||||
end
|
||||
|
||||
render_opts = %{as: :target, user: user, relationships: relationships_opt}
|
||||
render_many(targets, AccountView, "relationship.json", render_opts)
|
||||
end
|
||||
|
||||
defp do_render("show.json", %{user: user} = opts) do
|
||||
user = User.sanitize_html(user, User.html_filter_policy(opts[:for]))
|
||||
display_name = user.name || user.nickname
|
||||
|
||||
image = User.avatar_url(user) |> MediaProxy.url()
|
||||
|
|
@ -100,18 +189,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
}
|
||||
end)
|
||||
|
||||
fields =
|
||||
user
|
||||
|> User.fields()
|
||||
|> Enum.map(fn %{"name" => name, "value" => value} ->
|
||||
%{
|
||||
"name" => name,
|
||||
"value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
|
||||
}
|
||||
end)
|
||||
|
||||
bio = HTML.filter_tags(user.bio, User.html_filter_policy(opts[:for]))
|
||||
relationship = render("relationship.json", %{user: opts[:for], target: user})
|
||||
relationship =
|
||||
render("relationship.json", %{
|
||||
user: opts[:for],
|
||||
target: user,
|
||||
relationships: opts[:relationships]
|
||||
})
|
||||
|
||||
%{
|
||||
id: to_string(user.id),
|
||||
|
|
@ -123,17 +206,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
followers_count: followers_count,
|
||||
following_count: following_count,
|
||||
statuses_count: user.note_count,
|
||||
note: bio || "",
|
||||
note: user.bio || "",
|
||||
url: User.profile_url(user),
|
||||
avatar: image,
|
||||
avatar_static: image,
|
||||
header: header,
|
||||
header_static: header,
|
||||
emojis: emojis,
|
||||
fields: fields,
|
||||
fields: user.fields,
|
||||
bot: bot,
|
||||
source: %{
|
||||
note: HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
|
||||
note: (user.bio || "") |> String.replace(~r(<br */?>), "\n") |> Pleroma.HTML.strip_tags(),
|
||||
sensitive: false,
|
||||
fields: user.raw_fields,
|
||||
pleroma: %{
|
||||
|
|
|
|||
|
|
@ -8,24 +8,86 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||
alias Pleroma.Activity
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.NotificationView
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
|
||||
def render("index.json", %{notifications: notifications, for: user}) do
|
||||
safe_render_many(notifications, NotificationView, "show.json", %{for: user})
|
||||
def render("index.json", %{notifications: notifications, for: reading_user} = opts) do
|
||||
activities = Enum.map(notifications, & &1.activity)
|
||||
|
||||
parent_activities =
|
||||
activities
|
||||
|> Enum.filter(
|
||||
&(Activity.mastodon_notification_type(&1) in [
|
||||
"favourite",
|
||||
"reblog",
|
||||
"pleroma:emoji_reaction"
|
||||
])
|
||||
)
|
||||
|> Enum.map(& &1.data["object"])
|
||||
|> Activity.create_by_object_ap_id()
|
||||
|> Activity.with_preloaded_object(:left)
|
||||
|> Pleroma.Repo.all()
|
||||
|
||||
relationships_opt =
|
||||
cond do
|
||||
Map.has_key?(opts, :relationships) ->
|
||||
opts[:relationships]
|
||||
|
||||
is_nil(opts[:for]) ->
|
||||
UserRelationship.view_relationships_option(nil, [])
|
||||
|
||||
true ->
|
||||
move_activities_targets =
|
||||
activities
|
||||
|> Enum.filter(&(Activity.mastodon_notification_type(&1) == "move"))
|
||||
|> Enum.map(&User.get_cached_by_ap_id(&1.data["target"]))
|
||||
|
||||
actors =
|
||||
activities
|
||||
|> Enum.map(fn a -> User.get_cached_by_ap_id(a.data["actor"]) end)
|
||||
|> Enum.filter(& &1)
|
||||
|> Kernel.++(move_activities_targets)
|
||||
|
||||
UserRelationship.view_relationships_option(reading_user, actors)
|
||||
end
|
||||
|
||||
opts = %{
|
||||
for: reading_user,
|
||||
parent_activities: parent_activities,
|
||||
relationships: relationships_opt
|
||||
}
|
||||
|
||||
safe_render_many(notifications, NotificationView, "show.json", opts)
|
||||
end
|
||||
|
||||
def render("show.json", %{
|
||||
notification: %Notification{activity: activity} = notification,
|
||||
for: user
|
||||
}) do
|
||||
def render(
|
||||
"show.json",
|
||||
%{
|
||||
notification: %Notification{activity: activity} = notification,
|
||||
for: reading_user
|
||||
} = opts
|
||||
) do
|
||||
actor = User.get_cached_by_ap_id(activity.data["actor"])
|
||||
parent_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||
|
||||
parent_activity_fn = fn ->
|
||||
if opts[:parent_activities] do
|
||||
Activity.Queries.find_by_object_ap_id(opts[:parent_activities], activity.data["object"])
|
||||
else
|
||||
Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||
end
|
||||
end
|
||||
|
||||
mastodon_type = Activity.mastodon_notification_type(activity)
|
||||
|
||||
with %{id: _} = account <- AccountView.render("show.json", %{user: actor, for: user}) do
|
||||
with %{id: _} = account <-
|
||||
AccountView.render("show.json", %{
|
||||
user: actor,
|
||||
for: reading_user,
|
||||
relationships: opts[:relationships]
|
||||
}) do
|
||||
response = %{
|
||||
id: to_string(notification.id),
|
||||
type: mastodon_type,
|
||||
|
|
@ -36,24 +98,28 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||
}
|
||||
}
|
||||
|
||||
render_opts = %{relationships: opts[:relationships]}
|
||||
|
||||
case mastodon_type do
|
||||
"mention" ->
|
||||
put_status(response, activity, user)
|
||||
put_status(response, activity, reading_user, render_opts)
|
||||
|
||||
"favourite" ->
|
||||
put_status(response, parent_activity, user)
|
||||
put_status(response, parent_activity_fn.(), reading_user, render_opts)
|
||||
|
||||
"reblog" ->
|
||||
put_status(response, parent_activity, user)
|
||||
put_status(response, parent_activity_fn.(), reading_user, render_opts)
|
||||
|
||||
"move" ->
|
||||
put_target(response, activity, user)
|
||||
put_target(response, activity, reading_user, render_opts)
|
||||
|
||||
"follow" ->
|
||||
response
|
||||
|
||||
"pleroma:emoji_reaction" ->
|
||||
put_status(response, parent_activity, user) |> put_emoji(activity)
|
||||
response
|
||||
|> put_status(parent_activity_fn.(), reading_user, render_opts)
|
||||
|> put_emoji(activity)
|
||||
|
||||
_ ->
|
||||
nil
|
||||
|
|
@ -64,16 +130,21 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||
end
|
||||
|
||||
defp put_emoji(response, activity) do
|
||||
response
|
||||
|> Map.put(:emoji, activity.data["content"])
|
||||
Map.put(response, :emoji, activity.data["content"])
|
||||
end
|
||||
|
||||
defp put_status(response, activity, user) do
|
||||
Map.put(response, :status, StatusView.render("show.json", %{activity: activity, for: user}))
|
||||
defp put_status(response, activity, reading_user, opts) do
|
||||
status_render_opts = Map.merge(opts, %{activity: activity, for: reading_user})
|
||||
status_render = StatusView.render("show.json", status_render_opts)
|
||||
|
||||
Map.put(response, :status, status_render)
|
||||
end
|
||||
|
||||
defp put_target(response, activity, user) do
|
||||
target = User.get_cached_by_ap_id(activity.data["target"])
|
||||
Map.put(response, :target, AccountView.render("show.json", %{user: target, for: user}))
|
||||
defp put_target(response, activity, reading_user, opts) do
|
||||
target_user = User.get_cached_by_ap_id(activity.data["target"])
|
||||
target_render_opts = Map.merge(opts, %{user: target_user, for: reading_user})
|
||||
target_render = AccountView.render("show.json", target_render_opts)
|
||||
|
||||
Map.put(response, :target, target_render)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
|
|
@ -71,10 +72,41 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
end
|
||||
|
||||
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)
|
||||
# To do: check AdminAPIControllerTest on the reasons behind nil activities in the list
|
||||
activities = Enum.filter(opts.activities, & &1)
|
||||
replied_to_activities = get_replied_to_activities(activities)
|
||||
|
||||
safe_render_many(opts.activities, StatusView, "show.json", opts)
|
||||
parent_activities =
|
||||
activities
|
||||
|> Enum.filter(&(&1.data["type"] == "Announce" && &1.data["object"]))
|
||||
|> Enum.map(&Object.normalize(&1).data["id"])
|
||||
|> Activity.create_by_object_ap_id()
|
||||
|> Activity.with_preloaded_object(:left)
|
||||
|> Activity.with_preloaded_bookmark(opts[:for])
|
||||
|> Activity.with_set_thread_muted_field(opts[:for])
|
||||
|> Repo.all()
|
||||
|
||||
relationships_opt =
|
||||
cond do
|
||||
Map.has_key?(opts, :relationships) ->
|
||||
opts[:relationships]
|
||||
|
||||
is_nil(opts[:for]) ->
|
||||
UserRelationship.view_relationships_option(nil, [])
|
||||
|
||||
true ->
|
||||
actors = Enum.map(activities ++ parent_activities, &get_user(&1.data["actor"]))
|
||||
|
||||
UserRelationship.view_relationships_option(opts[:for], actors)
|
||||
end
|
||||
|
||||
opts =
|
||||
opts
|
||||
|> Map.put(:replied_to_activities, replied_to_activities)
|
||||
|> Map.put(:parent_activities, parent_activities)
|
||||
|> Map.put(:relationships, relationships_opt)
|
||||
|
||||
safe_render_many(activities, StatusView, "show.json", opts)
|
||||
end
|
||||
|
||||
def render(
|
||||
|
|
@ -85,17 +117,25 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
created_at = Utils.to_masto_date(activity.data["published"])
|
||||
activity_object = Object.normalize(activity)
|
||||
|
||||
reblogged_activity =
|
||||
Activity.create_by_object_ap_id(activity_object.data["id"])
|
||||
|> Activity.with_preloaded_bookmark(opts[:for])
|
||||
|> Activity.with_set_thread_muted_field(opts[:for])
|
||||
|> Repo.one()
|
||||
reblogged_parent_activity =
|
||||
if opts[:parent_activities] do
|
||||
Activity.Queries.find_by_object_ap_id(
|
||||
opts[:parent_activities],
|
||||
activity_object.data["id"]
|
||||
)
|
||||
else
|
||||
Activity.create_by_object_ap_id(activity_object.data["id"])
|
||||
|> Activity.with_preloaded_bookmark(opts[:for])
|
||||
|> Activity.with_set_thread_muted_field(opts[:for])
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
reblogged = render("show.json", Map.put(opts, :activity, reblogged_activity))
|
||||
reblog_rendering_opts = Map.put(opts, :activity, reblogged_parent_activity)
|
||||
reblogged = render("show.json", reblog_rendering_opts)
|
||||
|
||||
favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || [])
|
||||
|
||||
bookmarked = Activity.get_bookmark(reblogged_activity, opts[:for]) != nil
|
||||
bookmarked = Activity.get_bookmark(reblogged_parent_activity, opts[:for]) != nil
|
||||
|
||||
mentions =
|
||||
activity.recipients
|
||||
|
|
@ -107,7 +147,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
id: to_string(activity.id),
|
||||
uri: activity_object.data["id"],
|
||||
url: activity_object.data["id"],
|
||||
account: AccountView.render("show.json", %{user: user, for: opts[:for]}),
|
||||
account:
|
||||
AccountView.render("show.json", %{
|
||||
user: user,
|
||||
for: opts[:for],
|
||||
relationships: opts[:relationships]
|
||||
}),
|
||||
in_reply_to_id: nil,
|
||||
in_reply_to_account_id: nil,
|
||||
reblog: reblogged,
|
||||
|
|
@ -116,7 +161,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
reblogs_count: 0,
|
||||
replies_count: 0,
|
||||
favourites_count: 0,
|
||||
reblogged: reblogged?(reblogged_activity, opts[:for]),
|
||||
reblogged: reblogged?(reblogged_parent_activity, opts[:for]),
|
||||
favourited: present?(favorited),
|
||||
bookmarked: present?(bookmarked),
|
||||
muted: false,
|
||||
|
|
@ -183,9 +228,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
end
|
||||
|
||||
thread_muted? =
|
||||
case activity.thread_muted? do
|
||||
thread_muted? when is_boolean(thread_muted?) -> thread_muted?
|
||||
nil -> (opts[:for] && CommonAPI.thread_muted?(opts[:for], activity)) || false
|
||||
cond do
|
||||
is_nil(opts[:for]) -> false
|
||||
is_boolean(activity.thread_muted?) -> activity.thread_muted?
|
||||
true -> CommonAPI.thread_muted?(opts[:for], activity)
|
||||
end
|
||||
|
||||
attachment_data = object.data["attachment"] || []
|
||||
|
|
@ -253,11 +299,26 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
_ -> []
|
||||
end
|
||||
|
||||
muted =
|
||||
thread_muted? ||
|
||||
UserRelationship.exists?(
|
||||
get_in(opts, [:relationships, :user_relationships]),
|
||||
:mute,
|
||||
opts[:for],
|
||||
user,
|
||||
fn for_user, user -> User.mutes?(for_user, user) end
|
||||
)
|
||||
|
||||
%{
|
||||
id: to_string(activity.id),
|
||||
uri: object.data["id"],
|
||||
url: url,
|
||||
account: AccountView.render("show.json", %{user: user, for: opts[:for]}),
|
||||
account:
|
||||
AccountView.render("show.json", %{
|
||||
user: user,
|
||||
for: opts[:for],
|
||||
relationships: opts[:relationships]
|
||||
}),
|
||||
in_reply_to_id: reply_to && to_string(reply_to.id),
|
||||
in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
|
||||
reblog: nil,
|
||||
|
|
@ -270,7 +331,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
reblogged: reblogged?(activity, opts[:for]),
|
||||
favourited: present?(favorited),
|
||||
bookmarked: present?(bookmarked),
|
||||
muted: thread_muted? || User.mutes?(opts[:for], user),
|
||||
muted: muted,
|
||||
pinned: pinned?(activity, user),
|
||||
sensitive: sensitive,
|
||||
spoiler_text: summary,
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|
|||
"pleroma_explicit_addressing",
|
||||
"shareable_emoji_packs",
|
||||
"multifetch",
|
||||
"pleroma:api/v1/notifications:include_types_filter",
|
||||
if Config.get([:media_proxy, :enabled]) do
|
||||
"media_proxy"
|
||||
end,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
alias Pleroma.Web.Metadata.PlayerView
|
||||
alias Pleroma.Web.Router
|
||||
|
||||
plug(Pleroma.Plugs.EnsureAuthenticatedPlug,
|
||||
unless_func: &Pleroma.Web.FederatingPlug.federating?/0
|
||||
)
|
||||
|
||||
plug(
|
||||
RateLimiter,
|
||||
[name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
|
||||
|
|
@ -135,13 +139,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
end
|
||||
end
|
||||
|
||||
def errors(conn, {:error, :not_found}) do
|
||||
defp errors(conn, {:error, :not_found}) do
|
||||
render_error(conn, :not_found, "Not found")
|
||||
end
|
||||
|
||||
def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
|
||||
defp errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
|
||||
|
||||
def errors(conn, _) do
|
||||
defp errors(conn, _) do
|
||||
render_error(conn, :internal_server_error, "Something went wrong")
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -101,6 +101,11 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
|||
conn
|
||||
|> put_view(ConversationView)
|
||||
|> render("participation.json", %{participation: participation, for: user})
|
||||
else
|
||||
_error ->
|
||||
conn
|
||||
|> put_status(404)
|
||||
|> json(%{"error" => "Unknown conversation id"})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -108,9 +113,9 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
|||
%{assigns: %{user: user}} = conn,
|
||||
%{"id" => participation_id} = params
|
||||
) do
|
||||
participation = Participation.get(participation_id, preload: [:conversation])
|
||||
|
||||
if user.id == participation.user_id do
|
||||
with %Participation{} = participation <-
|
||||
Participation.get(participation_id, preload: [:conversation]),
|
||||
true <- user.id == participation.user_id do
|
||||
params =
|
||||
params
|
||||
|> Map.put("blocking_user", user)
|
||||
|
|
@ -126,6 +131,11 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
|||
|> add_link_headers(activities)
|
||||
|> put_view(StatusView)
|
||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||
else
|
||||
_error ->
|
||||
conn
|
||||
|> put_status(404)
|
||||
|> json(%{"error" => "Unknown conversation id"})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -133,15 +143,22 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
|||
%{assigns: %{user: user}} = conn,
|
||||
%{"id" => participation_id, "recipients" => recipients}
|
||||
) do
|
||||
participation =
|
||||
participation_id
|
||||
|> Participation.get()
|
||||
|
||||
with true <- user.id == participation.user_id,
|
||||
with %Participation{} = participation <- Participation.get(participation_id),
|
||||
true <- user.id == participation.user_id,
|
||||
{:ok, participation} <- Participation.set_recipients(participation, recipients) do
|
||||
conn
|
||||
|> put_view(ConversationView)
|
||||
|> render("participation.json", %{participation: participation, for: user})
|
||||
else
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{"error" => message})
|
||||
|
||||
_error ->
|
||||
conn
|
||||
|> put_status(404)
|
||||
|> json(%{"error" => "Unknown conversation id"})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -173,6 +173,8 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
|
||||
patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
|
||||
get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
|
||||
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
|
||||
|
||||
get("/users", AdminAPIController, :list_users)
|
||||
get("/users/:nickname", AdminAPIController, :user_show)
|
||||
|
|
@ -513,7 +515,7 @@ defmodule Pleroma.Web.Router do
|
|||
end
|
||||
|
||||
pipeline :ostatus do
|
||||
plug(:accepts, ["html", "xml", "atom", "activity+json", "json"])
|
||||
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
|
||||
plug(Pleroma.Plugs.StaticFEPlug)
|
||||
end
|
||||
|
||||
|
|
@ -541,6 +543,7 @@ defmodule Pleroma.Web.Router do
|
|||
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
||||
end
|
||||
|
||||
# Server to Server (S2S) AP interactions
|
||||
pipeline :activitypub do
|
||||
plug(:accepts, ["activity+json", "json"])
|
||||
plug(Pleroma.Web.Plugs.HTTPSignaturePlug)
|
||||
|
|
@ -554,6 +557,7 @@ defmodule Pleroma.Web.Router do
|
|||
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
||||
end
|
||||
|
||||
# Client to Server (C2S) AP interactions
|
||||
pipeline :activitypub_client do
|
||||
plug(:accepts, ["activity+json", "json"])
|
||||
plug(:fetch_session)
|
||||
|
|
@ -597,8 +601,8 @@ defmodule Pleroma.Web.Router do
|
|||
post("/inbox", ActivityPubController, :inbox)
|
||||
end
|
||||
|
||||
get("/following", ActivityPubController, :following, assigns: %{relay: true})
|
||||
get("/followers", ActivityPubController, :followers, assigns: %{relay: true})
|
||||
get("/following", ActivityPubController, :relay_following)
|
||||
get("/followers", ActivityPubController, :relay_followers)
|
||||
end
|
||||
|
||||
scope "/internal/fetch", Pleroma.Web.ActivityPub do
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
|||
plug(:put_view, Pleroma.Web.StaticFE.StaticFEView)
|
||||
plug(:assign_id)
|
||||
|
||||
plug(Pleroma.Plugs.EnsureAuthenticatedPlug,
|
||||
unless_func: &Pleroma.Web.FederatingPlug.federating?/0
|
||||
)
|
||||
|
||||
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
|
||||
|
||||
defp get_title(%Object{data: %{"name" => name}}) when is_binary(name),
|
||||
|
|
@ -33,7 +37,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
|||
|> render("error.html", %{message: message, meta: ""})
|
||||
end
|
||||
|
||||
def get_counts(%Activity{} = activity) do
|
||||
defp get_counts(%Activity{} = activity) do
|
||||
%Object{data: data} = Object.normalize(activity)
|
||||
|
||||
%{
|
||||
|
|
@ -43,9 +47,9 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
|||
}
|
||||
end
|
||||
|
||||
def represent(%Activity{} = activity), do: represent(activity, false)
|
||||
defp represent(%Activity{} = activity), do: represent(activity, false)
|
||||
|
||||
def represent(%Activity{object: %Object{data: data}} = activity, selected) do
|
||||
defp represent(%Activity{object: %Object{data: data}} = activity, selected) do
|
||||
{:ok, user} = User.get_or_fetch(activity.object.data["actor"])
|
||||
|
||||
link =
|
||||
|
|
@ -54,10 +58,19 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
|||
_ -> data["url"] || data["external_url"] || data["id"]
|
||||
end
|
||||
|
||||
content =
|
||||
if data["content"] do
|
||||
data["content"]
|
||||
|> Pleroma.HTML.filter_tags()
|
||||
|> Pleroma.Emoji.Formatter.emojify(Map.get(data, "emoji", %{}))
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
%{
|
||||
user: user,
|
||||
user: User.sanitize_html(user),
|
||||
title: get_title(activity.object),
|
||||
content: data["content"] || nil,
|
||||
content: content,
|
||||
attachment: data["attachment"],
|
||||
link: link,
|
||||
published: data["published"],
|
||||
|
|
@ -109,7 +122,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
|||
next_page_id = List.last(timeline) && List.last(timeline).id
|
||||
|
||||
render(conn, "profile.html", %{
|
||||
user: user,
|
||||
user: User.sanitize_html(user),
|
||||
timeline: timeline,
|
||||
prev_page_id: prev_page_id,
|
||||
next_page_id: next_page_id,
|
||||
|
|
@ -147,17 +160,17 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
|||
end
|
||||
end
|
||||
|
||||
def assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
|
||||
defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
|
||||
do: assign(conn, :notice_id, notice_id)
|
||||
|
||||
def assign_id(%{path_info: ["users", user_id]} = conn, _opts),
|
||||
defp assign_id(%{path_info: ["users", user_id]} = conn, _opts),
|
||||
do: assign(conn, :username_or_id, user_id)
|
||||
|
||||
def assign_id(%{path_info: ["objects", object_id]} = conn, _opts),
|
||||
defp assign_id(%{path_info: ["objects", object_id]} = conn, _opts),
|
||||
do: assign(conn, :object_id, object_id)
|
||||
|
||||
def assign_id(%{path_info: ["activities", activity_id]} = conn, _opts),
|
||||
defp assign_id(%{path_info: ["activities", activity_id]} = conn, _opts),
|
||||
do: assign(conn, :activity_id, activity_id)
|
||||
|
||||
def assign_id(conn, _opts), do: conn
|
||||
defp assign_id(conn, _opts), do: conn
|
||||
end
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ defmodule Pleroma.Web.Streamer.Worker do
|
|||
|
||||
defp should_send?(%User{} = user, %Activity{} = item) do
|
||||
%{block: blocked_ap_ids, mute: muted_ap_ids, reblog_mute: reblog_muted_ap_ids} =
|
||||
User.outgoing_relations_ap_ids(user, [:block, :mute, :reblog_mute])
|
||||
User.outgoing_relationships_ap_ids(user, [:block, :mute, :reblog_mute])
|
||||
|
||||
recipient_blocks = MapSet.new(blocked_ap_ids ++ muted_ap_ids)
|
||||
recipients = MapSet.new(item.recipients)
|
||||
|
|
|
|||
49
lib/pleroma/web/templates/feed/feed/_activity.rss.eex
Normal file
49
lib/pleroma/web/templates/feed/feed/_activity.rss.eex
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<item>
|
||||
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
|
||||
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
|
||||
<guid><%= @data["id"] %></guid>
|
||||
<title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
||||
<description><%= activity_content(@object) %></description>
|
||||
<pubDate><%= @data["published"] %></pubDate>
|
||||
<updated><%= @data["published"] %></updated>
|
||||
<ostatus:conversation ref="<%= activity_context(@activity) %>">
|
||||
<%= activity_context(@activity) %>
|
||||
</ostatus:conversation>
|
||||
<link rel="ostatus:conversation"><%= activity_context(@activity) %></link>
|
||||
|
||||
<%= if @data["summary"] do %>
|
||||
<description><%= @data["summary"] %></description>
|
||||
<% end %>
|
||||
|
||||
<%= if @activity.local do %>
|
||||
<link><%= @data["id"] %></link>
|
||||
<% else %>
|
||||
<link><%= @data["external_url"] %></link>
|
||||
<% end %>
|
||||
|
||||
<%= for tag <- @data["tag"] || [] do %>
|
||||
<category term="<%= tag %>"></category>
|
||||
<% end %>
|
||||
|
||||
<%= for attachment <- @data["attachment"] || [] do %>
|
||||
<link type="<%= attachment_type(attachment) %>"><%= attachment_href(attachment) %></link>
|
||||
<% end %>
|
||||
|
||||
<%= if @data["inReplyTo"] do %>
|
||||
<thr:in-reply-to ref='<%= @data["inReplyTo"] %>' href='<%= get_href(@data["inReplyTo"]) %>'/>
|
||||
<% end %>
|
||||
|
||||
<%= for id <- @activity.recipients do %>
|
||||
<%= if id == Pleroma.Constants.as_public() do %>
|
||||
<link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection">http://activityschema.org/collection/public</link>
|
||||
<% else %>
|
||||
<%= unless Regex.match?(~r/^#{Pleroma.Web.base_url()}.+followers$/, id) do %>
|
||||
<link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person"><%= id %></link>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= for {emoji, file} <- @data["emoji"] || %{} do %>
|
||||
<link name="<%= emoji %>" rel="emoji"><%= file %></link>
|
||||
<% end %>
|
||||
</item>
|
||||
17
lib/pleroma/web/templates/feed/feed/_author.rss.eex
Normal file
17
lib/pleroma/web/templates/feed/feed/_author.rss.eex
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<managingEditor>
|
||||
<guid><%= @user.ap_id %></guid>
|
||||
<activity:object>http://activitystrea.ms/schema/1.0/person</activity:object>
|
||||
<uri><%= @user.ap_id %></uri>
|
||||
<poco:preferredUsername><%= @user.nickname %></poco:preferredUsername>
|
||||
<poco:displayName><%= @user.name %></poco:displayName>
|
||||
<poco:note><%= escape(@user.bio) %></poco:note>
|
||||
<description><%= escape(@user.bio) %></description>
|
||||
<name><%= @user.nickname %></name>
|
||||
<link rel="avatar"><%= User.avatar_url(@user) %></link>
|
||||
<%= if User.banner_url(@user) do %>
|
||||
<link rel="header"><%= User.banner_url(@user) %></link>
|
||||
<% end %>
|
||||
<%= if @user.local do %>
|
||||
<ap_enabled>true</ap_enabled>
|
||||
<% end %>
|
||||
</managingEditor>
|
||||
|
|
@ -12,13 +12,13 @@
|
|||
<logo><%= logo(@user) %></logo>
|
||||
<link rel="self" href="<%= '#{user_feed_url(@conn, :feed, @user.nickname)}.atom' %>" type="application/atom+xml"/>
|
||||
|
||||
<%= render @view_module, "_author.xml", assigns %>
|
||||
<%= render @view_module, "_author.atom", assigns %>
|
||||
|
||||
<%= if last_activity(@activities) do %>
|
||||
<link rel="next" href="<%= '#{user_feed_url(@conn, :feed, @user.nickname)}.atom?max_id=#{last_activity(@activities).id}' %>" type="application/atom+xml"/>
|
||||
<% end %>
|
||||
|
||||
<%= for activity <- @activities do %>
|
||||
<%= render @view_module, "_activity.xml", Map.merge(assigns, prepare_activity(activity)) %>
|
||||
<%= render @view_module, "_activity.atom", Map.merge(assigns, prepare_activity(activity)) %>
|
||||
<% end %>
|
||||
</feed>
|
||||
20
lib/pleroma/web/templates/feed/feed/user.rss.eex
Normal file
20
lib/pleroma/web/templates/feed/feed/user.rss.eex
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<guid><%= user_feed_url(@conn, :feed, @user.nickname) <> ".rss" %></guid>
|
||||
<title><%= @user.nickname <> "'s timeline" %></title>
|
||||
<updated><%= most_recent_update(@activities, @user) %></updated>
|
||||
<image><%= logo(@user) %></image>
|
||||
<link><%= '#{user_feed_url(@conn, :feed, @user.nickname)}.rss' %></link>
|
||||
|
||||
<%= render @view_module, "_author.rss", assigns %>
|
||||
|
||||
<%= if last_activity(@activities) do %>
|
||||
<link rel="next"><%= '#{user_feed_url(@conn, :feed, @user.nickname)}.rss?max_id=#{last_activity(@activities).id}' %></link>
|
||||
<% end %>
|
||||
|
||||
<%= for activity <- @activities do %>
|
||||
<%= render @view_module, "_activity.rss", Map.merge(assigns, prepare_activity(activity)) %>
|
||||
<% end %>
|
||||
</channel>
|
||||
</rss>
|
||||
|
|
@ -16,6 +16,8 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
|||
|
||||
@status_types ["Article", "Event", "Note", "Video", "Page", "Question"]
|
||||
|
||||
plug(Pleroma.Web.FederatingPlug)
|
||||
|
||||
# Note: follower can submit the form (with password auth) not being signed in (having no token)
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.WebFinger
|
||||
|
||||
plug(Pleroma.Web.FederatingPlug when action == :remote_subscribe)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["follow", "write:follows"]}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue