Merge remote-tracking branch 'origin/develop' into activity-pub-use-atoms-as-keys
This commit is contained in:
commit
b02df1803e
20 changed files with 173 additions and 213 deletions
|
@ -17,14 +17,6 @@ def append_uri_params(uri, appended_params) do
|
||||||
|> URI.to_string()
|
|> URI.to_string()
|
||||||
end
|
end
|
||||||
|
|
||||||
def append_param_if_present(%{} = params, param_name, param_value) do
|
|
||||||
if param_value do
|
|
||||||
Map.put(params, param_name, param_value)
|
|
||||||
else
|
|
||||||
params
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def maybe_add_base("/" <> uri, base), do: Path.join([base, uri])
|
def maybe_add_base("/" <> uri, base), do: Path.join([base, uri])
|
||||||
def maybe_add_base(uri, _base), do: uri
|
def maybe_add_base(uri, _base), do: uri
|
||||||
end
|
end
|
||||||
|
|
15
lib/pleroma/maps.ex
Normal file
15
lib/pleroma/maps.ex
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Maps do
|
||||||
|
def put_if_present(map, key, value, value_function \\ &{:ok, &1}) when is_map(map) do
|
||||||
|
with false <- is_nil(key),
|
||||||
|
false <- is_nil(value),
|
||||||
|
{:ok, new_value} <- value_function.(value) do
|
||||||
|
Map.put(map, key, new_value)
|
||||||
|
else
|
||||||
|
_ -> map
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
alias Pleroma.Constants
|
alias Pleroma.Constants
|
||||||
alias Pleroma.Conversation
|
alias Pleroma.Conversation
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Object.Containment
|
alias Pleroma.Object.Containment
|
||||||
|
@ -19,7 +20,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.MRF
|
alias Pleroma.Web.ActivityPub.MRF
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
|
||||||
alias Pleroma.Web.Streamer
|
alias Pleroma.Web.Streamer
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
alias Pleroma.Workers.BackgroundWorker
|
alias Pleroma.Workers.BackgroundWorker
|
||||||
|
@ -147,12 +147,7 @@ def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when
|
||||||
})
|
})
|
||||||
|
|
||||||
# Splice in the child object if we have one.
|
# Splice in the child object if we have one.
|
||||||
activity =
|
activity = Maps.put_if_present(activity, :object, object)
|
||||||
if not is_nil(object) do
|
|
||||||
Map.put(activity, :object, object)
|
|
||||||
else
|
|
||||||
activity
|
|
||||||
end
|
|
||||||
|
|
||||||
BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
|
BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
|
||||||
|
|
||||||
|
@ -318,7 +313,7 @@ defp accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
|
||||||
|
|
||||||
data =
|
data =
|
||||||
%{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
|
%{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
|
||||||
|> Utils.maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
|
|
||||||
with {:ok, activity} <- insert(data, local),
|
with {:ok, activity} <- insert(data, local),
|
||||||
_ <- notify_and_stream(activity),
|
_ <- notify_and_stream(activity),
|
||||||
|
@ -340,7 +335,7 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
||||||
"actor" => actor,
|
"actor" => actor,
|
||||||
"object" => object
|
"object" => object
|
||||||
}
|
}
|
||||||
|> Utils.maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
|
|
||||||
with {:ok, activity} <- insert(data, local),
|
with {:ok, activity} <- insert(data, local),
|
||||||
_ <- notify_and_stream(activity),
|
_ <- notify_and_stream(activity),
|
||||||
|
@ -1192,12 +1187,7 @@ def fetch_activities_bounded(
|
||||||
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
|
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
|
||||||
def upload(file, opts \\ []) do
|
def upload(file, opts \\ []) do
|
||||||
with {:ok, data} <- Upload.store(file, opts) do
|
with {:ok, data} <- Upload.store(file, opts) do
|
||||||
obj_data =
|
obj_data = Maps.put_if_present(data, "actor", opts[:actor])
|
||||||
if opts[:actor] do
|
|
||||||
Map.put(data, "actor", opts[:actor])
|
|
||||||
else
|
|
||||||
data
|
|
||||||
end
|
|
||||||
|
|
||||||
Repo.insert(%Object{data: obj_data})
|
Repo.insert(%Object{data: obj_data})
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,6 +21,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
alias Pleroma.Web.ActivityPub.UserView
|
alias Pleroma.Web.ActivityPub.UserView
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
alias Pleroma.Web.ControllerHelper
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.FederatingPlug
|
alias Pleroma.Web.FederatingPlug
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
|
@ -230,27 +231,22 @@ def outbox(
|
||||||
when page? in [true, "true"] do
|
when page? in [true, "true"] do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||||
{:ok, user} <- User.ensure_keys_present(user) do
|
{:ok, user} <- User.ensure_keys_present(user) do
|
||||||
activities =
|
# "include_poll_votes" is a hack because postgres generates inefficient
|
||||||
if params["max_id"] do
|
# queries when filtering by 'Answer', poll votes will be hidden by the
|
||||||
ActivityPub.fetch_user_activities(user, for_user, %{
|
# visibility filter in this case anyway
|
||||||
max_id: params["max_id"],
|
params =
|
||||||
# This is a hack because postgres generates inefficient queries when filtering by
|
params
|
||||||
# 'Answer', poll votes will be hidden by the visibility filter in this case anyway
|
|> Map.drop(["nickname", "page"])
|
||||||
include_poll_votes: true,
|
|> Map.put("include_poll_votes", true)
|
||||||
limit: 10
|
|
||||||
})
|
activities = ActivityPub.fetch_user_activities(user, for_user, params)
|
||||||
else
|
|
||||||
ActivityPub.fetch_user_activities(user, for_user, %{
|
|
||||||
limit: 10,
|
|
||||||
include_poll_votes: true
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_resp_content_type("application/activity+json")
|
|> put_resp_content_type("application/activity+json")
|
||||||
|> put_view(UserView)
|
|> put_view(UserView)
|
||||||
|> render("activity_collection_page.json", %{
|
|> render("activity_collection_page.json", %{
|
||||||
activities: activities,
|
activities: activities,
|
||||||
|
pagination: ControllerHelper.get_pagination_fields(conn, activities),
|
||||||
iri: "#{user.ap_id}/outbox"
|
iri: "#{user.ap_id}/outbox"
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
@ -353,21 +349,23 @@ def read_inbox(
|
||||||
%{"nickname" => nickname, "page" => page?} = params
|
%{"nickname" => nickname, "page" => page?} = params
|
||||||
)
|
)
|
||||||
when page? in [true, "true"] do
|
when page? in [true, "true"] do
|
||||||
|
params =
|
||||||
|
params
|
||||||
|
|> Map.drop(["nickname", "page"])
|
||||||
|
|> Map.put("blocking_user", user)
|
||||||
|
|> Map.put("user", user)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
if params["max_id"] do
|
[user.ap_id | User.following(user)]
|
||||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], %{
|
|> ActivityPub.fetch_activities(params)
|
||||||
max_id: params["max_id"],
|
|> Enum.reverse()
|
||||||
limit: 10
|
|
||||||
})
|
|
||||||
else
|
|
||||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], %{limit: 10})
|
|
||||||
end
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_resp_content_type("application/activity+json")
|
|> put_resp_content_type("application/activity+json")
|
||||||
|> put_view(UserView)
|
|> put_view(UserView)
|
||||||
|> render("activity_collection_page.json", %{
|
|> render("activity_collection_page.json", %{
|
||||||
activities: activities,
|
activities: activities,
|
||||||
|
pagination: ControllerHelper.get_pagination_fields(conn, activities),
|
||||||
iri: "#{user.ap_id}/inbox"
|
iri: "#{user.ap_id}/inbox"
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.EarmarkRenderer
|
alias Pleroma.EarmarkRenderer
|
||||||
alias Pleroma.FollowingRelationship
|
alias Pleroma.FollowingRelationship
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Object.Containment
|
alias Pleroma.Object.Containment
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -208,12 +209,6 @@ def fix_context(object) do
|
||||||
|> Map.put("conversation", context)
|
|> Map.put("conversation", context)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp add_if_present(map, _key, nil), do: map
|
|
||||||
|
|
||||||
defp add_if_present(map, key, value) do
|
|
||||||
Map.put(map, key, value)
|
|
||||||
end
|
|
||||||
|
|
||||||
def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachment) do
|
def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachment) do
|
||||||
attachments =
|
attachments =
|
||||||
Enum.map(attachment, fn data ->
|
Enum.map(attachment, fn data ->
|
||||||
|
@ -241,13 +236,13 @@ def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachm
|
||||||
|
|
||||||
attachment_url =
|
attachment_url =
|
||||||
%{"href" => href}
|
%{"href" => href}
|
||||||
|> add_if_present("mediaType", media_type)
|
|> Maps.put_if_present("mediaType", media_type)
|
||||||
|> add_if_present("type", Map.get(url || %{}, "type"))
|
|> Maps.put_if_present("type", Map.get(url || %{}, "type"))
|
||||||
|
|
||||||
%{"url" => [attachment_url]}
|
%{"url" => [attachment_url]}
|
||||||
|> add_if_present("mediaType", media_type)
|
|> Maps.put_if_present("mediaType", media_type)
|
||||||
|> add_if_present("type", data["type"])
|
|> Maps.put_if_present("type", data["type"])
|
||||||
|> add_if_present("name", data["name"])
|
|> Maps.put_if_present("name", data["name"])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Map.put(object, "attachment", attachments)
|
Map.put(object, "attachment", attachments)
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
alias Ecto.UUID
|
alias Ecto.UUID
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -307,7 +308,7 @@ def make_like_data(
|
||||||
"cc" => cc,
|
"cc" => cc,
|
||||||
"context" => object.data["context"]
|
"context" => object.data["context"]
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_emoji_reaction_data(user, object, emoji, activity_id) do
|
def make_emoji_reaction_data(user, object, emoji, activity_id) do
|
||||||
|
@ -477,7 +478,7 @@ def make_follow_data(
|
||||||
"object" => followed_id,
|
"object" => followed_id,
|
||||||
"state" => "pending"
|
"state" => "pending"
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
|
def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
|
||||||
|
@ -546,7 +547,7 @@ def make_announce_data(
|
||||||
"cc" => [],
|
"cc" => [],
|
||||||
"context" => object.data["context"]
|
"context" => object.data["context"]
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_announce_data(
|
def make_announce_data(
|
||||||
|
@ -563,7 +564,7 @@ def make_announce_data(
|
||||||
"cc" => [Pleroma.Constants.as_public()],
|
"cc" => [Pleroma.Constants.as_public()],
|
||||||
"context" => object.data["context"]
|
"context" => object.data["context"]
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_undo_data(
|
def make_undo_data(
|
||||||
|
@ -582,7 +583,7 @@ def make_undo_data(
|
||||||
"cc" => [Pleroma.Constants.as_public()],
|
"cc" => [Pleroma.Constants.as_public()],
|
||||||
"context" => context
|
"context" => context
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec add_announce_to_object(Activity.t(), Object.t()) ::
|
@spec add_announce_to_object(Activity.t(), Object.t()) ::
|
||||||
|
@ -627,7 +628,7 @@ def make_unfollow_data(follower, followed, follow_activity, activity_id) do
|
||||||
"to" => [followed.ap_id],
|
"to" => [followed.ap_id],
|
||||||
"object" => follow_activity.data
|
"object" => follow_activity.data
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
#### Block-related helpers
|
#### Block-related helpers
|
||||||
|
@ -650,7 +651,7 @@ def make_block_data(blocker, blocked, activity_id) do
|
||||||
"to" => [blocked.ap_id],
|
"to" => [blocked.ap_id],
|
||||||
"object" => blocked.ap_id
|
"object" => blocked.ap_id
|
||||||
}
|
}
|
||||||
|> maybe_put("id", activity_id)
|
|> Maps.put_if_present("id", activity_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
#### Create-related helpers
|
#### Create-related helpers
|
||||||
|
@ -870,7 +871,4 @@ def get_existing_votes(actor, %{data: %{"id" => id}}) do
|
||||||
|> where([a, object: o], fragment("(?)->>'type' = 'Answer'", o.data))
|
|> where([a, object: o], fragment("(?)->>'type' = 'Answer'", o.data))
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
def maybe_put(map, _key, nil), do: map
|
|
||||||
def maybe_put(map, key, value), do: Map.put(map, key, value)
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -213,34 +213,24 @@ def render("activity_collection.json", %{iri: iri}) do
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("activity_collection_page.json", %{activities: activities, iri: iri}) do
|
def render("activity_collection_page.json", %{
|
||||||
# this is sorted chronologically, so first activity is the newest (max)
|
activities: activities,
|
||||||
{max_id, min_id, collection} =
|
iri: iri,
|
||||||
if length(activities) > 0 do
|
pagination: pagination
|
||||||
{
|
}) do
|
||||||
Enum.at(activities, 0).id,
|
collection =
|
||||||
Enum.at(Enum.reverse(activities), 0).id,
|
Enum.map(activities, fn activity ->
|
||||||
Enum.map(activities, fn act ->
|
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
||||||
{:ok, data} = Transmogrifier.prepare_outgoing(act.data)
|
|
||||||
data
|
data
|
||||||
end)
|
end)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
[]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"id" => "#{iri}?max_id=#{max_id}&page=true",
|
|
||||||
"type" => "OrderedCollectionPage",
|
"type" => "OrderedCollectionPage",
|
||||||
"partOf" => iri,
|
"partOf" => iri,
|
||||||
"orderedItems" => collection,
|
"orderedItems" => collection
|
||||||
"next" => "#{iri}?max_id=#{min_id}&page=true"
|
|
||||||
}
|
}
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
|
|> Map.merge(pagination)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_put_total_items(map, false, _total), do: map
|
defp maybe_put_total_items(map, false, _total), do: map
|
||||||
|
|
|
@ -61,13 +61,12 @@ def show(conn, _params) do
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
|
|
||||||
setting = %{
|
%{
|
||||||
group: ConfigDB.convert(group),
|
group: ConfigDB.convert(group),
|
||||||
key: ConfigDB.convert(key),
|
key: ConfigDB.convert(key),
|
||||||
value: ConfigDB.convert(merged_value)
|
value: ConfigDB.convert(merged_value)
|
||||||
}
|
}
|
||||||
|
|> Pleroma.Maps.put_if_present(:db, db)
|
||||||
if db, do: Map.put(setting, :db, db), else: setting
|
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|> List.flatten()
|
|> List.flatten()
|
||||||
|
|
|
@ -42,12 +42,7 @@ def index(conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(%{body_params: params} = conn, _) do
|
def create(%{body_params: params} = conn, _) do
|
||||||
params =
|
params = Pleroma.Maps.put_if_present(params, :client_name, params[:name])
|
||||||
if params[:name] do
|
|
||||||
Map.put(params, :client_name, params[:name])
|
|
||||||
else
|
|
||||||
params
|
|
||||||
end
|
|
||||||
|
|
||||||
case App.create(params) do
|
case App.create(params) do
|
||||||
{:ok, app} ->
|
{:ok, app} ->
|
||||||
|
@ -59,12 +54,7 @@ def create(%{body_params: params} = conn, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(%{body_params: params} = conn, %{id: id}) do
|
def update(%{body_params: params} = conn, %{id: id}) do
|
||||||
params =
|
params = Pleroma.Maps.put_if_present(params, :client_name, params[:name])
|
||||||
if params[:name] do
|
|
||||||
Map.put(params, :client_name, params.name)
|
|
||||||
else
|
|
||||||
params
|
|
||||||
end
|
|
||||||
|
|
||||||
with {:ok, app} <- App.update(id, params) do
|
with {:ok, app} <- App.update(id, params) do
|
||||||
render(conn, "show.json", app: app, admin: true)
|
render(conn, "show.json", app: app, admin: true)
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
defmodule Pleroma.Web.ControllerHelper do
|
defmodule Pleroma.Web.ControllerHelper do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Pagination
|
||||||
|
|
||||||
# As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
|
# As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
|
||||||
@falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]
|
@falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]
|
||||||
|
|
||||||
|
@ -46,37 +48,47 @@ def add_link_headers(%{assigns: %{skip_link_headers: true}} = conn, _activities,
|
||||||
do: conn
|
do: conn
|
||||||
|
|
||||||
def add_link_headers(conn, activities, extra_params) do
|
def add_link_headers(conn, activities, extra_params) do
|
||||||
|
case get_pagination_fields(conn, activities, extra_params) do
|
||||||
|
%{"next" => next_url, "prev" => prev_url} ->
|
||||||
|
put_resp_header(conn, "link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"")
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_pagination_fields(conn, activities, extra_params \\ %{}) do
|
||||||
case List.last(activities) do
|
case List.last(activities) do
|
||||||
%{id: max_id} ->
|
%{id: max_id} ->
|
||||||
params =
|
params =
|
||||||
conn.params
|
conn.params
|
||||||
|> Map.drop(Map.keys(conn.path_params))
|
|> Map.drop(Map.keys(conn.path_params))
|
||||||
|> Map.drop(["since_id", "max_id", "min_id"])
|
|
||||||
|> Map.merge(extra_params)
|
|> Map.merge(extra_params)
|
||||||
|
|> Map.drop(Pagination.page_keys() -- ["limit", "order"])
|
||||||
limit =
|
|
||||||
params
|
|
||||||
|> Map.get("limit", "20")
|
|
||||||
|> String.to_integer()
|
|
||||||
|
|
||||||
min_id =
|
min_id =
|
||||||
if length(activities) <= limit do
|
|
||||||
activities
|
activities
|
||||||
|> List.first()
|
|> List.first()
|
||||||
|> Map.get(:id)
|
|> Map.get(:id)
|
||||||
|
|
||||||
|
fields = %{
|
||||||
|
"next" => current_url(conn, Map.put(params, :max_id, max_id)),
|
||||||
|
"prev" => current_url(conn, Map.put(params, :min_id, min_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generating an `id` without already present pagination keys would
|
||||||
|
# need a query-restriction with an `q.id >= ^id` or `q.id <= ^id`
|
||||||
|
# instead of the `q.id > ^min_id` and `q.id < ^max_id`.
|
||||||
|
# This is because we only have ids present inside of the page, while
|
||||||
|
# `min_id`, `since_id` and `max_id` requires to know one outside of it.
|
||||||
|
if Map.take(conn.params, Pagination.page_keys() -- ["limit", "order"]) != [] do
|
||||||
|
Map.put(fields, "id", current_url(conn, conn.params))
|
||||||
else
|
else
|
||||||
activities
|
fields
|
||||||
|> Enum.at(limit * -1)
|
|
||||||
|> Map.get(:id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
next_url = current_url(conn, Map.merge(params, %{max_id: max_id}))
|
|
||||||
prev_url = current_url(conn, Map.merge(params, %{min_id: min_id}))
|
|
||||||
|
|
||||||
put_resp_header(conn, "link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"")
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
conn
|
%{}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -98,11 +110,6 @@ def try_render(conn, _, _) do
|
||||||
render_error(conn, :not_implemented, "Can't display this activity")
|
render_error(conn, :not_implemented, "Can't display this activity")
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec put_if_exist(map(), atom() | String.t(), any) :: map()
|
|
||||||
def put_if_exist(map, _key, nil), do: map
|
|
||||||
|
|
||||||
def put_if_exist(map, key, value), do: Map.put(map, key, value)
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns true if request specifies to include embedded relationships in account objects.
|
Returns true if request specifies to include embedded relationships in account objects.
|
||||||
May only be used in selected account-related endpoints; has no effect for status- or
|
May only be used in selected account-related endpoints; has no effect for status- or
|
||||||
|
|
|
@ -9,14 +9,12 @@ defmodule Pleroma.Web.Feed.TagController do
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.Feed.FeedView
|
alias Pleroma.Web.Feed.FeedView
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [put_if_exist: 3]
|
|
||||||
|
|
||||||
def feed(conn, %{"tag" => raw_tag} = params) do
|
def feed(conn, %{"tag" => raw_tag} = params) do
|
||||||
{format, tag} = parse_tag(raw_tag)
|
{format, tag} = parse_tag(raw_tag)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
%{type: ["Create"], tag: tag}
|
%{type: ["Create"], tag: tag}
|
||||||
|> put_if_exist(:max_id, params["max_id"])
|
|> Pleroma.Maps.put_if_present(:max_id, params["max_id"])
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -11,8 +11,6 @@ defmodule Pleroma.Web.Feed.UserController do
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPubController
|
alias Pleroma.Web.ActivityPub.ActivityPubController
|
||||||
alias Pleroma.Web.Feed.FeedView
|
alias Pleroma.Web.Feed.FeedView
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [put_if_exist: 3]
|
|
||||||
|
|
||||||
plug(Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect])
|
plug(Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect])
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
@ -55,7 +53,7 @@ def feed(conn, %{"nickname" => nickname} = params) do
|
||||||
type: ["Create"],
|
type: ["Create"],
|
||||||
actor_id: user.ap_id
|
actor_id: user.ap_id
|
||||||
}
|
}
|
||||||
|> put_if_exist(:max_id, params["max_id"])
|
|> Pleroma.Maps.put_if_present(:max_id, params["max_id"])
|
||||||
|> ActivityPub.fetch_public_or_unlisted_activities()
|
|> ActivityPub.fetch_public_or_unlisted_activities()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
json_response: 3
|
json_response: 3
|
||||||
]
|
]
|
||||||
|
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
|
@ -160,23 +161,22 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p
|
||||||
:discoverable
|
:discoverable
|
||||||
]
|
]
|
||||||
|> Enum.reduce(%{}, fn key, acc ->
|
|> Enum.reduce(%{}, fn key, acc ->
|
||||||
add_if_present(acc, params, key, key, &{:ok, truthy_param?(&1)})
|
Maps.put_if_present(acc, key, params[key], &{:ok, truthy_param?(&1)})
|
||||||
end)
|
end)
|
||||||
|> add_if_present(params, :display_name, :name)
|
|> Maps.put_if_present(:name, params[:display_name])
|
||||||
|> add_if_present(params, :note, :bio)
|
|> Maps.put_if_present(:bio, params[:note])
|
||||||
|> add_if_present(params, :avatar, :avatar)
|
|> Maps.put_if_present(:avatar, params[:avatar])
|
||||||
|> add_if_present(params, :header, :banner)
|
|> Maps.put_if_present(:banner, params[:header])
|
||||||
|> add_if_present(params, :pleroma_background_image, :background)
|
|> Maps.put_if_present(:background, params[:pleroma_background_image])
|
||||||
|> add_if_present(
|
|> Maps.put_if_present(
|
||||||
params,
|
|
||||||
:fields_attributes,
|
|
||||||
:raw_fields,
|
:raw_fields,
|
||||||
|
params[:fields_attributes],
|
||||||
&{:ok, normalize_fields_attributes(&1)}
|
&{:ok, normalize_fields_attributes(&1)}
|
||||||
)
|
)
|
||||||
|> add_if_present(params, :pleroma_settings_store, :pleroma_settings_store)
|
|> Maps.put_if_present(:pleroma_settings_store, params[:pleroma_settings_store])
|
||||||
|> add_if_present(params, :default_scope, :default_scope)
|
|> Maps.put_if_present(:default_scope, params[:default_scope])
|
||||||
|> add_if_present(params["source"], "privacy", :default_scope)
|
|> Maps.put_if_present(:default_scope, params["source"]["privacy"])
|
||||||
|> add_if_present(params, :actor_type, :actor_type)
|
|> Maps.put_if_present(:actor_type, params[:actor_type])
|
||||||
|
|
||||||
changeset = User.update_changeset(user, user_params)
|
changeset = User.update_changeset(user, user_params)
|
||||||
|
|
||||||
|
@ -206,16 +206,6 @@ defp build_update_activity_params(user) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp add_if_present(map, params, params_field, map_field, value_function \\ &{:ok, &1}) do
|
|
||||||
with true <- is_map(params),
|
|
||||||
true <- Map.has_key?(params, params_field),
|
|
||||||
{:ok, new_value} <- value_function.(Map.get(params, params_field)) do
|
|
||||||
Map.put(map, map_field, new_value)
|
|
||||||
else
|
|
||||||
_ -> map
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp normalize_fields_attributes(fields) do
|
defp normalize_fields_attributes(fields) do
|
||||||
if Enum.all?(fields, &is_tuple/1) do
|
if Enum.all?(fields, &is_tuple/1) do
|
||||||
Enum.map(fields, fn {_, v} -> v end)
|
Enum.map(fields, fn {_, v} -> v end)
|
||||||
|
|
|
@ -50,10 +50,8 @@ def home(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> Map.put(:reply_filtering_user, user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|> Map.put(:user, user)
|
|> Map.put(:user, user)
|
||||||
|
|
||||||
recipients = [user.ap_id | User.following(user)]
|
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
recipients
|
[user.ap_id | User.following(user)]
|
||||||
|> ActivityPub.fetch_activities(params)
|
|> ActivityPub.fetch_activities(params)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
|
|
|
@ -45,10 +45,6 @@ def render("short.json", %{app: %App{website: webiste, client_name: name}}) do
|
||||||
defp with_vapid_key(data) do
|
defp with_vapid_key(data) do
|
||||||
vapid_key = Application.get_env(:web_push_encryption, :vapid_details, [])[:public_key]
|
vapid_key = Application.get_env(:web_push_encryption, :vapid_details, [])[:public_key]
|
||||||
|
|
||||||
if vapid_key do
|
Pleroma.Maps.put_if_present(data, "vapid_key", vapid_key)
|
||||||
Map.put(data, "vapid_key", vapid_key)
|
|
||||||
else
|
|
||||||
data
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,7 +30,7 @@ defp with_media_attachments(data, %{params: %{"media_attachments" => media_attac
|
||||||
defp with_media_attachments(data, _), do: data
|
defp with_media_attachments(data, _), do: data
|
||||||
|
|
||||||
defp status_params(params) do
|
defp status_params(params) do
|
||||||
data = %{
|
%{
|
||||||
text: params["status"],
|
text: params["status"],
|
||||||
sensitive: params["sensitive"],
|
sensitive: params["sensitive"],
|
||||||
spoiler_text: params["spoiler_text"],
|
spoiler_text: params["spoiler_text"],
|
||||||
|
@ -39,10 +39,6 @@ defp status_params(params) do
|
||||||
poll: params["poll"],
|
poll: params["poll"],
|
||||||
in_reply_to_id: params["in_reply_to_id"]
|
in_reply_to_id: params["in_reply_to_id"]
|
||||||
}
|
}
|
||||||
|
|> Pleroma.Maps.put_if_present(:media_ids, params["media_ids"])
|
||||||
case params["media_ids"] do
|
|
||||||
nil -> data
|
|
||||||
media_ids -> Map.put(data, :media_ids, media_ids)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Helpers.UriHelper
|
alias Pleroma.Helpers.UriHelper
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.MFA
|
alias Pleroma.MFA
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
alias Pleroma.Registration
|
alias Pleroma.Registration
|
||||||
|
@ -108,7 +109,7 @@ defp handle_existing_authorization(
|
||||||
if redirect_uri in String.split(app.redirect_uris) do
|
if redirect_uri in String.split(app.redirect_uris) do
|
||||||
redirect_uri = redirect_uri(conn, redirect_uri)
|
redirect_uri = redirect_uri(conn, redirect_uri)
|
||||||
url_params = %{access_token: token.token}
|
url_params = %{access_token: token.token}
|
||||||
url_params = UriHelper.append_param_if_present(url_params, :state, params["state"])
|
url_params = Maps.put_if_present(url_params, :state, params["state"])
|
||||||
url = UriHelper.append_uri_params(redirect_uri, url_params)
|
url = UriHelper.append_uri_params(redirect_uri, url_params)
|
||||||
redirect(conn, external: url)
|
redirect(conn, external: url)
|
||||||
else
|
else
|
||||||
|
@ -147,7 +148,7 @@ def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{
|
||||||
if redirect_uri in String.split(app.redirect_uris) do
|
if redirect_uri in String.split(app.redirect_uris) do
|
||||||
redirect_uri = redirect_uri(conn, redirect_uri)
|
redirect_uri = redirect_uri(conn, redirect_uri)
|
||||||
url_params = %{code: auth.token}
|
url_params = %{code: auth.token}
|
||||||
url_params = UriHelper.append_param_if_present(url_params, :state, auth_attrs["state"])
|
url_params = Maps.put_if_present(url_params, :state, auth_attrs["state"])
|
||||||
url = UriHelper.append_uri_params(redirect_uri, url_params)
|
url = UriHelper.append_uri_params(redirect_uri, url_params)
|
||||||
redirect(conn, external: url)
|
redirect(conn, external: url)
|
||||||
else
|
else
|
||||||
|
|
|
@ -571,13 +571,6 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web.ActivityPub do
|
|
||||||
# XXX: not really ostatus
|
|
||||||
pipe_through(:ostatus)
|
|
||||||
|
|
||||||
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
|
||||||
end
|
|
||||||
|
|
||||||
pipeline :ap_service_actor do
|
pipeline :ap_service_actor do
|
||||||
plug(:accepts, ["activity+json", "json"])
|
plug(:accepts, ["activity+json", "json"])
|
||||||
end
|
end
|
||||||
|
@ -602,6 +595,7 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/api/ap/whoami", ActivityPubController, :whoami)
|
get("/api/ap/whoami", ActivityPubController, :whoami)
|
||||||
get("/users/:nickname/inbox", ActivityPubController, :read_inbox)
|
get("/users/:nickname/inbox", ActivityPubController, :read_inbox)
|
||||||
|
|
||||||
|
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
||||||
post("/users/:nickname/outbox", ActivityPubController, :update_outbox)
|
post("/users/:nickname/outbox", ActivityPubController, :update_outbox)
|
||||||
post("/api/ap/upload_media", ActivityPubController, :upload_media)
|
post("/api/ap/upload_media", ActivityPubController, :upload_media)
|
||||||
|
|
||||||
|
|
|
@ -804,17 +804,63 @@ test "it requires authentication", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /users/:nickname/outbox" do
|
describe "GET /users/:nickname/outbox" do
|
||||||
|
test "it paginates correctly", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
conn = assign(conn, :user, user)
|
||||||
|
outbox_endpoint = user.ap_id <> "/outbox"
|
||||||
|
|
||||||
|
_posts =
|
||||||
|
for i <- 0..25 do
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"})
|
||||||
|
activity
|
||||||
|
end
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get(outbox_endpoint <> "?page=true")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
result_ids = Enum.map(result["orderedItems"], fn x -> x["id"] end)
|
||||||
|
assert length(result["orderedItems"]) == 20
|
||||||
|
assert length(result_ids) == 20
|
||||||
|
assert result["next"]
|
||||||
|
assert String.starts_with?(result["next"], outbox_endpoint)
|
||||||
|
|
||||||
|
result_next =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get(result["next"])
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
result_next_ids = Enum.map(result_next["orderedItems"], fn x -> x["id"] end)
|
||||||
|
assert length(result_next["orderedItems"]) == 6
|
||||||
|
assert length(result_next_ids) == 6
|
||||||
|
refute Enum.find(result_next_ids, fn x -> x in result_ids end)
|
||||||
|
refute Enum.find(result_ids, fn x -> x in result_next_ids end)
|
||||||
|
assert String.starts_with?(result["id"], outbox_endpoint)
|
||||||
|
|
||||||
|
result_next_again =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get(result_next["id"])
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert result_next == result_next_again
|
||||||
|
end
|
||||||
|
|
||||||
test "it returns 200 even if there're no activities", %{conn: conn} do
|
test "it returns 200 even if there're no activities", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
outbox_endpoint = user.ap_id <> "/outbox"
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> put_req_header("accept", "application/activity+json")
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|> get("/users/#{user.nickname}/outbox")
|
|> get(outbox_endpoint)
|
||||||
|
|
||||||
result = json_response(conn, 200)
|
result = json_response(conn, 200)
|
||||||
assert user.ap_id <> "/outbox" == result["id"]
|
assert outbox_endpoint == result["id"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns a note activity in a collection", %{conn: conn} do
|
test "it returns a note activity in a collection", %{conn: conn} do
|
||||||
|
|
|
@ -158,35 +158,4 @@ test "sets correct totalItems when follows are hidden but the follow counter is
|
||||||
assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
|
assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "activity collection page aginates correctly" do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
posts =
|
|
||||||
for i <- 0..25 do
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"})
|
|
||||||
activity
|
|
||||||
end
|
|
||||||
|
|
||||||
# outbox sorts chronologically, newest first, with ten per page
|
|
||||||
posts = Enum.reverse(posts)
|
|
||||||
|
|
||||||
%{"next" => next_url} =
|
|
||||||
UserView.render("activity_collection_page.json", %{
|
|
||||||
iri: "#{user.ap_id}/outbox",
|
|
||||||
activities: Enum.take(posts, 10)
|
|
||||||
})
|
|
||||||
|
|
||||||
next_id = Enum.at(posts, 9).id
|
|
||||||
assert next_url =~ next_id
|
|
||||||
|
|
||||||
%{"next" => next_url} =
|
|
||||||
UserView.render("activity_collection_page.json", %{
|
|
||||||
iri: "#{user.ap_id}/outbox",
|
|
||||||
activities: Enum.take(Enum.drop(posts, 10), 10)
|
|
||||||
})
|
|
||||||
|
|
||||||
next_id = Enum.at(posts, 19).id
|
|
||||||
assert next_url =~ next_id
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue