Merge branch 'output-of-relationships-in-statuses' into 'develop'
Ability to control the output of AccountView.pleroma.relationship in statuses / notifications See merge request pleroma/pleroma!2342
This commit is contained in:
commit
ef37774403
|
@ -67,7 +67,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [2.0.0] - 2019-03-08
|
## [2.0.0] - 2019-03-08
|
||||||
### Security
|
### Security
|
||||||
- Mastodon API: Fix being able to request enourmous amount of statuses in timelines leading to DoS. Now limited to 40 per request.
|
- Mastodon API: Fix being able to request enormous amount of statuses in timelines leading to DoS. Now limited to 40 per request.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media`
|
- **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media`
|
||||||
|
|
|
@ -386,47 +386,56 @@ defp render_timelines(user) do
|
||||||
|
|
||||||
favourites = ActivityPub.fetch_favourites(user)
|
favourites = ActivityPub.fetch_favourites(user)
|
||||||
|
|
||||||
|
output_relationships =
|
||||||
|
!!Pleroma.Config.get([:extensions, :output_relationships_in_statuses_by_default])
|
||||||
|
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
%{
|
%{
|
||||||
"Rendering home timeline" => fn ->
|
"Rendering home timeline" => fn ->
|
||||||
StatusView.render("index.json", %{
|
StatusView.render("index.json", %{
|
||||||
activities: home_activities,
|
activities: home_activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity
|
as: :activity,
|
||||||
|
skip_relationships: !output_relationships
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
"Rendering direct timeline" => fn ->
|
"Rendering direct timeline" => fn ->
|
||||||
StatusView.render("index.json", %{
|
StatusView.render("index.json", %{
|
||||||
activities: direct_activities,
|
activities: direct_activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity
|
as: :activity,
|
||||||
|
skip_relationships: !output_relationships
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
"Rendering public timeline" => fn ->
|
"Rendering public timeline" => fn ->
|
||||||
StatusView.render("index.json", %{
|
StatusView.render("index.json", %{
|
||||||
activities: public_activities,
|
activities: public_activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity
|
as: :activity,
|
||||||
|
skip_relationships: !output_relationships
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
"Rendering tag timeline" => fn ->
|
"Rendering tag timeline" => fn ->
|
||||||
StatusView.render("index.json", %{
|
StatusView.render("index.json", %{
|
||||||
activities: tag_activities,
|
activities: tag_activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity
|
as: :activity,
|
||||||
|
skip_relationships: !output_relationships
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
"Rendering notifications" => fn ->
|
"Rendering notifications" => fn ->
|
||||||
Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
|
Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
|
||||||
notifications: notifications,
|
notifications: notifications,
|
||||||
for: user
|
for: user,
|
||||||
|
skip_relationships: !output_relationships
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
"Rendering favourites timeline" => fn ->
|
"Rendering favourites timeline" => fn ->
|
||||||
StatusView.render("index.json", %{
|
StatusView.render("index.json", %{
|
||||||
activities: favourites,
|
activities: favourites,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity
|
as: :activity,
|
||||||
|
skip_relationships: !output_relationships
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
|
@ -240,6 +240,8 @@
|
||||||
extended_nickname_format: true,
|
extended_nickname_format: true,
|
||||||
cleanup_attachments: false
|
cleanup_attachments: false
|
||||||
|
|
||||||
|
config :pleroma, :extensions, output_relationships_in_statuses_by_default: true
|
||||||
|
|
||||||
config :pleroma, :feed,
|
config :pleroma, :feed,
|
||||||
post_title: %{
|
post_title: %{
|
||||||
max_length: 100,
|
max_length: 100,
|
||||||
|
|
|
@ -67,7 +67,8 @@ def run(["render_timeline", nickname | _] = args) do
|
||||||
Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
|
Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity
|
as: :activity,
|
||||||
|
skip_relationships: true
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
|
@ -130,17 +130,27 @@ def exists?(dictionary, rel_type, source, target, func) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc ":relationships option for StatusView / AccountView / NotificationView"
|
@doc ":relationships option for StatusView / AccountView / NotificationView"
|
||||||
def view_relationships_option(nil = _reading_user, _actors) do
|
def view_relationships_option(reading_user, actors, opts \\ [])
|
||||||
|
|
||||||
|
def view_relationships_option(nil = _reading_user, _actors, _opts) do
|
||||||
%{user_relationships: [], following_relationships: []}
|
%{user_relationships: [], following_relationships: []}
|
||||||
end
|
end
|
||||||
|
|
||||||
def view_relationships_option(%User{} = reading_user, actors) do
|
def view_relationships_option(%User{} = reading_user, actors, opts) do
|
||||||
|
{source_to_target_rel_types, target_to_source_rel_types} =
|
||||||
|
if opts[:source_mutes_only] do
|
||||||
|
# This option is used for rendering statuses (FE needs `muted` flag for each one anyways)
|
||||||
|
{[:mute], []}
|
||||||
|
else
|
||||||
|
{[:block, :mute, :notification_mute, :reblog_mute], [:block, :inverse_subscription]}
|
||||||
|
end
|
||||||
|
|
||||||
user_relationships =
|
user_relationships =
|
||||||
UserRelationship.dictionary(
|
UserRelationship.dictionary(
|
||||||
[reading_user],
|
[reading_user],
|
||||||
actors,
|
actors,
|
||||||
[:block, :mute, :notification_mute, :reblog_mute],
|
source_to_target_rel_types,
|
||||||
[:block, :inverse_subscription]
|
target_to_source_rel_types
|
||||||
)
|
)
|
||||||
|
|
||||||
following_relationships = FollowingRelationship.all_between_user_sets([reading_user], actors)
|
following_relationships = FollowingRelationship.all_between_user_sets([reading_user], actors)
|
||||||
|
|
|
@ -258,7 +258,7 @@ def list_instance_statuses(conn, %{"instance" => instance} = params) do
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(Pleroma.Web.AdminAPI.StatusView)
|
|> put_view(Pleroma.Web.AdminAPI.StatusView)
|
||||||
|> render("index.json", %{activities: activities, as: :activity})
|
|> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
||||||
|
@ -277,7 +277,7 @@ def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> render("index.json", %{activities: activities, as: :activity})
|
|> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
|
||||||
else
|
else
|
||||||
_ -> {:error, :not_found}
|
_ -> {:error, :not_found}
|
||||||
end
|
end
|
||||||
|
@ -812,7 +812,7 @@ def list_statuses(%{assigns: %{user: _admin}} = conn, params) do
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(Pleroma.Web.AdminAPI.StatusView)
|
|> put_view(Pleroma.Web.AdminAPI.StatusView)
|
||||||
|> render("index.json", %{activities: activities, as: :activity})
|
|> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
|
||||||
end
|
end
|
||||||
|
|
||||||
def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
|
def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
|
||||||
|
|
|
@ -38,7 +38,12 @@ def render("show.json", %{report: report, user: user, account: account, statuses
|
||||||
actor: merge_account_views(user),
|
actor: merge_account_views(user),
|
||||||
content: content,
|
content: content,
|
||||||
created_at: created_at,
|
created_at: created_at,
|
||||||
statuses: StatusView.render("index.json", %{activities: statuses, as: :activity}),
|
statuses:
|
||||||
|
StatusView.render("index.json", %{
|
||||||
|
activities: statuses,
|
||||||
|
as: :activity,
|
||||||
|
skip_relationships: false
|
||||||
|
}),
|
||||||
state: report.data["state"],
|
state: report.data["state"],
|
||||||
notes: render(__MODULE__, "index_notes.json", %{notes: report.report_notes})
|
notes: render(__MODULE__, "index_notes.json", %{notes: report.report_notes})
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,7 +187,7 @@ defp object(draft) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp preview?(draft) do
|
defp preview?(draft) do
|
||||||
preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params["preview"]) || false
|
preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params["preview"])
|
||||||
%__MODULE__{draft | preview?: preview?}
|
%__MODULE__{draft | preview?: preview?}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,18 @@
|
||||||
defmodule Pleroma.Web.ControllerHelper do
|
defmodule Pleroma.Web.ControllerHelper do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
# As in MastoAPI, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
|
alias Pleroma.Config
|
||||||
|
|
||||||
|
# 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"]
|
||||||
def truthy_param?(blank_value) when blank_value in [nil, ""], do: nil
|
|
||||||
def truthy_param?(value), do: value not in @falsy_param_values
|
def explicitly_falsy_param?(value), do: value in @falsy_param_values
|
||||||
|
|
||||||
|
# Note: `nil` and `""` are considered falsy values in Pleroma
|
||||||
|
def falsy_param?(value),
|
||||||
|
do: explicitly_falsy_param?(value) or value in [nil, ""]
|
||||||
|
|
||||||
|
def truthy_param?(value), do: not falsy_param?(value)
|
||||||
|
|
||||||
def json_response(conn, status, json) do
|
def json_response(conn, status, json) do
|
||||||
conn
|
conn
|
||||||
|
@ -96,4 +104,14 @@ def try_render(conn, _, _) do
|
||||||
def put_if_exist(map, _key, nil), do: map
|
def put_if_exist(map, _key, nil), do: map
|
||||||
|
|
||||||
def put_if_exist(map, key, value), do: Map.put(map, key, value)
|
def put_if_exist(map, key, value), do: Map.put(map, key, value)
|
||||||
|
|
||||||
|
@doc "Whether to skip rendering `[:account][:pleroma][:relationship]`for statuses/notifications"
|
||||||
|
def skip_relationships?(params) do
|
||||||
|
if Config.get([:extensions, :output_relationships_in_statuses_by_default]) do
|
||||||
|
false
|
||||||
|
else
|
||||||
|
# BREAKING: older PleromaFE versions do not send this param but _do_ expect relationships.
|
||||||
|
not truthy_param?(params["with_relationships"])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper,
|
import Pleroma.Web.ControllerHelper,
|
||||||
only: [add_link_headers: 2, truthy_param?: 1, assign_account_by_id: 2, json_response: 3]
|
only: [
|
||||||
|
add_link_headers: 2,
|
||||||
|
truthy_param?: 1,
|
||||||
|
assign_account_by_id: 2,
|
||||||
|
json_response: 3,
|
||||||
|
skip_relationships?: 1
|
||||||
|
]
|
||||||
|
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
|
@ -237,7 +243,12 @@ def statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(activities)
|
|> add_link_headers(activities)
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> render("index.json", activities: activities, for: reading_user, as: :activity)
|
|> render("index.json",
|
||||||
|
activities: activities,
|
||||||
|
for: reading_user,
|
||||||
|
as: :activity,
|
||||||
|
skip_relationships: skip_relationships?(params)
|
||||||
|
)
|
||||||
else
|
else
|
||||||
_e -> render_error(conn, :not_found, "Can't find user")
|
_e -> render_error(conn, :not_found, "Can't find user")
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.NotificationController do
|
defmodule Pleroma.Web.MastodonAPI.NotificationController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
|
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, skip_relationships?: 1]
|
||||||
|
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
@ -45,7 +45,11 @@ def index(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(notifications)
|
|> add_link_headers(notifications)
|
||||||
|> render("index.json", notifications: notifications, for: user)
|
|> render("index.json",
|
||||||
|
notifications: notifications,
|
||||||
|
for: user,
|
||||||
|
skip_relationships: skip_relationships?(params)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /api/v1/notifications/:id
|
# GET /api/v1/notifications/:id
|
||||||
|
|
|
@ -5,13 +5,14 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.SearchController do
|
defmodule Pleroma.Web.MastodonAPI.SearchController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
import Pleroma.Web.ControllerHelper, only: [fetch_integer_param: 2, skip_relationships?: 1]
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
alias Pleroma.Web.ControllerHelper
|
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
|
||||||
|
@ -66,10 +67,11 @@ defp do_search(version, %{assigns: %{user: user}} = conn, %{"q" => query} = para
|
||||||
|
|
||||||
defp search_options(params, user) do
|
defp search_options(params, user) do
|
||||||
[
|
[
|
||||||
|
skip_relationships: skip_relationships?(params),
|
||||||
resolve: params["resolve"] == "true",
|
resolve: params["resolve"] == "true",
|
||||||
following: params["following"] == "true",
|
following: params["following"] == "true",
|
||||||
limit: ControllerHelper.fetch_integer_param(params, "limit"),
|
limit: fetch_integer_param(params, "limit"),
|
||||||
offset: ControllerHelper.fetch_integer_param(params, "offset"),
|
offset: fetch_integer_param(params, "offset"),
|
||||||
type: params["type"],
|
type: params["type"],
|
||||||
author: get_author(params),
|
author: get_author(params),
|
||||||
for_user: user
|
for_user: user
|
||||||
|
@ -79,12 +81,24 @@ defp search_options(params, user) do
|
||||||
|
|
||||||
defp resource_search(_, "accounts", query, options) do
|
defp resource_search(_, "accounts", query, options) do
|
||||||
accounts = with_fallback(fn -> User.search(query, options) end)
|
accounts = with_fallback(fn -> User.search(query, options) end)
|
||||||
AccountView.render("index.json", users: accounts, for: options[:for_user], as: :user)
|
|
||||||
|
AccountView.render("index.json",
|
||||||
|
users: accounts,
|
||||||
|
for: options[:for_user],
|
||||||
|
as: :user,
|
||||||
|
skip_relationships: false
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp resource_search(_, "statuses", query, options) do
|
defp resource_search(_, "statuses", query, options) do
|
||||||
statuses = with_fallback(fn -> Activity.search(options[:for_user], query, options) end)
|
statuses = with_fallback(fn -> Activity.search(options[:for_user], query, options) end)
|
||||||
StatusView.render("index.json", activities: statuses, for: options[:for_user], as: :activity)
|
|
||||||
|
StatusView.render("index.json",
|
||||||
|
activities: statuses,
|
||||||
|
for: options[:for_user],
|
||||||
|
as: :activity,
|
||||||
|
skip_relationships: options[:skip_relationships]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp resource_search(:v2, "hashtags", query, _options) do
|
defp resource_search(:v2, "hashtags", query, _options) do
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.StatusController do
|
defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [try_render: 3, add_link_headers: 2]
|
import Pleroma.Web.ControllerHelper,
|
||||||
|
only: [try_render: 3, add_link_headers: 2, skip_relationships?: 1]
|
||||||
|
|
||||||
require Ecto.Query
|
require Ecto.Query
|
||||||
|
|
||||||
|
@ -101,7 +102,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||||
|
|
||||||
`ids` query param is required
|
`ids` query param is required
|
||||||
"""
|
"""
|
||||||
def index(%{assigns: %{user: user}} = conn, %{"ids" => ids}) do
|
def index(%{assigns: %{user: user}} = conn, %{"ids" => ids} = params) do
|
||||||
limit = 100
|
limit = 100
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
|
@ -110,7 +111,12 @@ def index(%{assigns: %{user: user}} = conn, %{"ids" => ids}) do
|
||||||
|> Activity.all_by_ids_with_object()
|
|> Activity.all_by_ids_with_object()
|
||||||
|> Enum.filter(&Visibility.visible_for_user?(&1, user))
|
|> Enum.filter(&Visibility.visible_for_user?(&1, user))
|
||||||
|
|
||||||
render(conn, "index.json", activities: activities, for: user, as: :activity)
|
render(conn, "index.json",
|
||||||
|
activities: activities,
|
||||||
|
for: user,
|
||||||
|
as: :activity,
|
||||||
|
skip_relationships: skip_relationships?(params)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -360,7 +366,12 @@ def favourites(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(activities)
|
|> add_link_headers(activities)
|
||||||
|> render("index.json", activities: activities, for: user, as: :activity)
|
|> render("index.json",
|
||||||
|
activities: activities,
|
||||||
|
for: user,
|
||||||
|
as: :activity,
|
||||||
|
skip_relationships: skip_relationships?(params)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/bookmarks"
|
@doc "GET /api/v1/bookmarks"
|
||||||
|
@ -378,6 +389,11 @@ def bookmarks(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(bookmarks)
|
|> add_link_headers(bookmarks)
|
||||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
|> render("index.json",
|
||||||
|
activities: activities,
|
||||||
|
for: user,
|
||||||
|
as: :activity,
|
||||||
|
skip_relationships: skip_relationships?(params)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper,
|
import Pleroma.Web.ControllerHelper,
|
||||||
only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1]
|
only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1, skip_relationships?: 1]
|
||||||
|
|
||||||
alias Pleroma.Pagination
|
alias Pleroma.Pagination
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
@ -14,9 +14,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
|
||||||
# TODO: Replace with a macro when there is a Phoenix release with
|
# TODO: Replace with a macro when there is a Phoenix release with the following commit in it:
|
||||||
# https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e
|
# https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e
|
||||||
# in it
|
|
||||||
|
|
||||||
plug(RateLimiter, [name: :timeline, bucket_name: :direct_timeline] when action == :direct)
|
plug(RateLimiter, [name: :timeline, bucket_name: :direct_timeline] when action == :direct)
|
||||||
plug(RateLimiter, [name: :timeline, bucket_name: :public_timeline] when action == :public)
|
plug(RateLimiter, [name: :timeline, bucket_name: :public_timeline] when action == :public)
|
||||||
|
@ -49,7 +48,12 @@ def home(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(activities)
|
|> add_link_headers(activities)
|
||||||
|> render("index.json", activities: activities, for: user, as: :activity)
|
|> render("index.json",
|
||||||
|
activities: activities,
|
||||||
|
for: user,
|
||||||
|
as: :activity,
|
||||||
|
skip_relationships: skip_relationships?(params)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /api/v1/timelines/direct
|
# GET /api/v1/timelines/direct
|
||||||
|
@ -68,7 +72,12 @@ def direct(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(activities)
|
|> add_link_headers(activities)
|
||||||
|> render("index.json", activities: activities, for: user, as: :activity)
|
|> render("index.json",
|
||||||
|
activities: activities,
|
||||||
|
for: user,
|
||||||
|
as: :activity,
|
||||||
|
skip_relationships: skip_relationships?(params)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /api/v1/timelines/public
|
# GET /api/v1/timelines/public
|
||||||
|
@ -95,7 +104,12 @@ def public(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(activities, %{"local" => local_only})
|
|> add_link_headers(activities, %{"local" => local_only})
|
||||||
|> render("index.json", activities: activities, for: user, as: :activity)
|
|> render("index.json",
|
||||||
|
activities: activities,
|
||||||
|
for: user,
|
||||||
|
as: :activity,
|
||||||
|
skip_relationships: skip_relationships?(params)
|
||||||
|
)
|
||||||
else
|
else
|
||||||
render_error(conn, :unauthorized, "authorization required for timeline view")
|
render_error(conn, :unauthorized, "authorization required for timeline view")
|
||||||
end
|
end
|
||||||
|
@ -140,7 +154,12 @@ def hashtag(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(activities, %{"local" => local_only})
|
|> add_link_headers(activities, %{"local" => local_only})
|
||||||
|> render("index.json", activities: activities, for: user, as: :activity)
|
|> render("index.json",
|
||||||
|
activities: activities,
|
||||||
|
for: user,
|
||||||
|
as: :activity,
|
||||||
|
skip_relationships: skip_relationships?(params)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /api/v1/timelines/list/:list_id
|
# GET /api/v1/timelines/list/:list_id
|
||||||
|
@ -164,7 +183,12 @@ def list(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do
|
||||||
|> ActivityPub.fetch_activities_bounded(following, params)
|
|> ActivityPub.fetch_activities_bounded(following, params)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
render(conn, "index.json", activities: activities, for: user, as: :activity)
|
render(conn, "index.json",
|
||||||
|
activities: activities,
|
||||||
|
for: user,
|
||||||
|
as: :activity,
|
||||||
|
skip_relationships: skip_relationships?(params)
|
||||||
|
)
|
||||||
else
|
else
|
||||||
_e -> render_error(conn, :forbidden, "Error.")
|
_e -> render_error(conn, :forbidden, "Error.")
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||||
def render("index.json", %{users: users} = opts) do
|
def render("index.json", %{users: users} = opts) do
|
||||||
reading_user = opts[:for]
|
reading_user = opts[:for]
|
||||||
|
|
||||||
|
# Note: :skip_relationships option is currently intentionally not supported for accounts
|
||||||
relationships_opt =
|
relationships_opt =
|
||||||
cond do
|
cond do
|
||||||
Map.has_key?(opts, :relationships) ->
|
Map.has_key?(opts, :relationships) ->
|
||||||
|
@ -192,11 +193,15 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
end)
|
end)
|
||||||
|
|
||||||
relationship =
|
relationship =
|
||||||
|
if opts[:skip_relationships] do
|
||||||
|
%{}
|
||||||
|
else
|
||||||
render("relationship.json", %{
|
render("relationship.json", %{
|
||||||
user: opts[:for],
|
user: opts[:for],
|
||||||
target: user,
|
target: user,
|
||||||
relationships: opts[:relationships]
|
relationships: opts[:relationships]
|
||||||
})
|
})
|
||||||
|
end
|
||||||
|
|
||||||
%{
|
%{
|
||||||
id: to_string(user.id),
|
id: to_string(user.id),
|
||||||
|
|
|
@ -51,14 +51,15 @@ def render("index.json", %{notifications: notifications, for: reading_user} = op
|
||||||
|> Enum.filter(& &1)
|
|> Enum.filter(& &1)
|
||||||
|> Kernel.++(move_activities_targets)
|
|> Kernel.++(move_activities_targets)
|
||||||
|
|
||||||
UserRelationship.view_relationships_option(reading_user, actors)
|
UserRelationship.view_relationships_option(reading_user, actors,
|
||||||
|
source_mutes_only: opts[:skip_relationships]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
opts = %{
|
opts =
|
||||||
for: reading_user,
|
opts
|
||||||
parent_activities: parent_activities,
|
|> Map.put(:parent_activities, parent_activities)
|
||||||
relationships: relationships_opt
|
|> Map.put(:relationships, relationships_opt)
|
||||||
}
|
|
||||||
|
|
||||||
safe_render_many(notifications, NotificationView, "show.json", opts)
|
safe_render_many(notifications, NotificationView, "show.json", opts)
|
||||||
end
|
end
|
||||||
|
@ -82,12 +83,16 @@ def render(
|
||||||
|
|
||||||
mastodon_type = Activity.mastodon_notification_type(activity)
|
mastodon_type = Activity.mastodon_notification_type(activity)
|
||||||
|
|
||||||
|
render_opts = %{
|
||||||
|
relationships: opts[:relationships],
|
||||||
|
skip_relationships: opts[:skip_relationships]
|
||||||
|
}
|
||||||
|
|
||||||
with %{id: _} = account <-
|
with %{id: _} = account <-
|
||||||
AccountView.render("show.json", %{
|
AccountView.render(
|
||||||
user: actor,
|
"show.json",
|
||||||
for: reading_user,
|
Map.merge(render_opts, %{user: actor, for: reading_user})
|
||||||
relationships: opts[:relationships]
|
) do
|
||||||
}) do
|
|
||||||
response = %{
|
response = %{
|
||||||
id: to_string(notification.id),
|
id: to_string(notification.id),
|
||||||
type: mastodon_type,
|
type: mastodon_type,
|
||||||
|
@ -98,8 +103,6 @@ def render(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render_opts = %{relationships: opts[:relationships]}
|
|
||||||
|
|
||||||
case mastodon_type do
|
case mastodon_type do
|
||||||
"mention" ->
|
"mention" ->
|
||||||
put_status(response, activity, reading_user, render_opts)
|
put_status(response, activity, reading_user, render_opts)
|
||||||
|
@ -111,6 +114,7 @@ def render(
|
||||||
put_status(response, parent_activity_fn.(), reading_user, render_opts)
|
put_status(response, parent_activity_fn.(), reading_user, render_opts)
|
||||||
|
|
||||||
"move" ->
|
"move" ->
|
||||||
|
# Note: :skip_relationships option being applied to _account_ rendering (here)
|
||||||
put_target(response, activity, reading_user, render_opts)
|
put_target(response, activity, reading_user, render_opts)
|
||||||
|
|
||||||
"follow" ->
|
"follow" ->
|
||||||
|
|
|
@ -99,7 +99,9 @@ def render("index.json", opts) do
|
||||||
true ->
|
true ->
|
||||||
actors = Enum.map(activities ++ parent_activities, &get_user(&1.data["actor"]))
|
actors = Enum.map(activities ++ parent_activities, &get_user(&1.data["actor"]))
|
||||||
|
|
||||||
UserRelationship.view_relationships_option(reading_user, actors)
|
UserRelationship.view_relationships_option(reading_user, actors,
|
||||||
|
source_mutes_only: opts[:skip_relationships]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
opts =
|
opts =
|
||||||
|
@ -153,7 +155,8 @@ def render(
|
||||||
AccountView.render("show.json", %{
|
AccountView.render("show.json", %{
|
||||||
user: user,
|
user: user,
|
||||||
for: opts[:for],
|
for: opts[:for],
|
||||||
relationships: opts[:relationships]
|
relationships: opts[:relationships],
|
||||||
|
skip_relationships: opts[:skip_relationships]
|
||||||
}),
|
}),
|
||||||
in_reply_to_id: nil,
|
in_reply_to_id: nil,
|
||||||
in_reply_to_account_id: nil,
|
in_reply_to_account_id: nil,
|
||||||
|
@ -301,6 +304,7 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
|
||||||
_ -> []
|
_ -> []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Status muted state (would do 1 request per status unless user mutes are preloaded)
|
||||||
muted =
|
muted =
|
||||||
thread_muted? ||
|
thread_muted? ||
|
||||||
UserRelationship.exists?(
|
UserRelationship.exists?(
|
||||||
|
@ -319,7 +323,8 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
|
||||||
AccountView.render("show.json", %{
|
AccountView.render("show.json", %{
|
||||||
user: user,
|
user: user,
|
||||||
for: opts[:for],
|
for: opts[:for],
|
||||||
relationships: opts[:relationships]
|
relationships: opts[:relationships],
|
||||||
|
skip_relationships: opts[:skip_relationships]
|
||||||
}),
|
}),
|
||||||
in_reply_to_id: reply_to && to_string(reply_to.id),
|
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),
|
in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
|
||||||
|
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper,
|
import Pleroma.Web.ControllerHelper,
|
||||||
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2]
|
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2, skip_relationships?: 1]
|
||||||
|
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
@ -139,7 +139,12 @@ def favourites(%{assigns: %{user: for_user, account: user}} = conn, params) do
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(activities)
|
|> add_link_headers(activities)
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> render("index.json", activities: activities, for: for_user, as: :activity)
|
|> render("index.json",
|
||||||
|
activities: activities,
|
||||||
|
for: for_user,
|
||||||
|
as: :activity,
|
||||||
|
skip_relationships: skip_relationships?(params)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "POST /api/v1/pleroma/accounts/:id/subscribe"
|
@doc "POST /api/v1/pleroma/accounts/:id/subscribe"
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
|
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, skip_relationships?: 1]
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
|
@ -130,7 +130,12 @@ def conversation_statuses(
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(activities)
|
|> add_link_headers(activities)
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
|> render("index.json",
|
||||||
|
activities: activities,
|
||||||
|
for: user,
|
||||||
|
as: :activity,
|
||||||
|
skip_relationships: skip_relationships?(params)
|
||||||
|
)
|
||||||
else
|
else
|
||||||
_error ->
|
_error ->
|
||||||
conn
|
conn
|
||||||
|
@ -184,13 +189,17 @@ def read_notification(%{assigns: %{user: user}} = conn, %{"id" => notification_i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_notification(%{assigns: %{user: user}} = conn, %{"max_id" => max_id}) do
|
def read_notification(%{assigns: %{user: user}} = conn, %{"max_id" => max_id} = params) do
|
||||||
with notifications <- Notification.set_read_up_to(user, max_id) do
|
with notifications <- Notification.set_read_up_to(user, max_id) do
|
||||||
notifications = Enum.take(notifications, 80)
|
notifications = Enum.take(notifications, 80)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(NotificationView)
|
|> put_view(NotificationView)
|
||||||
|> render("index.json", %{notifications: notifications, for: user})
|
|> render("index.json",
|
||||||
|
notifications: notifications,
|
||||||
|
for: user,
|
||||||
|
skip_relationships: skip_relationships?(params)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,6 @@ defmodule Pleroma.Repo.Migrations.MigrateOldBookmarks do
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Bookmark
|
alias Pleroma.Bookmark
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
|
||||||
def up do
|
def up do
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
defmodule Pleroma.Repo.Migrations.CreateSafeJsonbSet do
|
defmodule Pleroma.Repo.Migrations.CreateSafeJsonbSet do
|
||||||
use Ecto.Migration
|
use Ecto.Migration
|
||||||
alias Pleroma.User
|
|
||||||
|
|
||||||
def change do
|
def change do
|
||||||
execute("""
|
execute("""
|
||||||
|
|
|
@ -12,6 +12,26 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
test "does NOT render account/pleroma/relationship if this is disabled by default" do
|
||||||
|
clear_config([:extensions, :output_relationships_in_statuses_by_default], false)
|
||||||
|
|
||||||
|
%{user: user, conn: conn} = oauth_access(["read:notifications"])
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
|
||||||
|
{:ok, [_notification]} = Notification.create_notifications(activity)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/notifications")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert Enum.all?(response, fn n ->
|
||||||
|
get_in(n, ["account", "pleroma", "relationship"]) == %{}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
test "list of notifications" do
|
test "list of notifications" do
|
||||||
%{user: user, conn: conn} = oauth_access(["read:notifications"])
|
%{user: user, conn: conn} = oauth_access(["read:notifications"])
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
|
@ -1047,6 +1047,8 @@ test "replaces missing description with an empty string", %{conn: conn, user: us
|
||||||
end
|
end
|
||||||
|
|
||||||
test "bookmarks" do
|
test "bookmarks" do
|
||||||
|
bookmarks_uri = "/api/v1/bookmarks?with_relationships=true"
|
||||||
|
|
||||||
%{conn: conn} = oauth_access(["write:bookmarks", "read:bookmarks"])
|
%{conn: conn} = oauth_access(["write:bookmarks", "read:bookmarks"])
|
||||||
author = insert(:user)
|
author = insert(:user)
|
||||||
|
|
||||||
|
@ -1068,7 +1070,7 @@ test "bookmarks" do
|
||||||
|
|
||||||
assert json_response(response2, 200)["bookmarked"] == true
|
assert json_response(response2, 200)["bookmarked"] == true
|
||||||
|
|
||||||
bookmarks = get(conn, "/api/v1/bookmarks")
|
bookmarks = get(conn, bookmarks_uri)
|
||||||
|
|
||||||
assert [json_response(response2, 200), json_response(response1, 200)] ==
|
assert [json_response(response2, 200), json_response(response1, 200)] ==
|
||||||
json_response(bookmarks, 200)
|
json_response(bookmarks, 200)
|
||||||
|
@ -1077,7 +1079,7 @@ test "bookmarks" do
|
||||||
|
|
||||||
assert json_response(response1, 200)["bookmarked"] == false
|
assert json_response(response1, 200)["bookmarked"] == false
|
||||||
|
|
||||||
bookmarks = get(conn, "/api/v1/bookmarks")
|
bookmarks = get(conn, bookmarks_uri)
|
||||||
|
|
||||||
assert [json_response(response2, 200)] == json_response(bookmarks, 200)
|
assert [json_response(response2, 200)] == json_response(bookmarks, 200)
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,7 +20,30 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
|
||||||
describe "home" do
|
describe "home" do
|
||||||
setup do: oauth_access(["read:statuses"])
|
setup do: oauth_access(["read:statuses"])
|
||||||
|
|
||||||
|
test "does NOT render account/pleroma/relationship if this is disabled by default", %{
|
||||||
|
user: user,
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
clear_config([:extensions, :output_relationships_in_statuses_by_default], false)
|
||||||
|
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/timelines/home")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert Enum.all?(response, fn n ->
|
||||||
|
get_in(n, ["account", "pleroma", "relationship"]) == %{}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
test "the home timeline", %{user: user, conn: conn} do
|
test "the home timeline", %{user: user, conn: conn} do
|
||||||
|
uri = "/api/v1/timelines/home?with_relationships=true"
|
||||||
|
|
||||||
following = insert(:user, nickname: "followed")
|
following = insert(:user, nickname: "followed")
|
||||||
third_user = insert(:user, nickname: "repeated")
|
third_user = insert(:user, nickname: "repeated")
|
||||||
|
|
||||||
|
@ -28,13 +51,13 @@ test "the home timeline", %{user: user, conn: conn} do
|
||||||
{:ok, activity} = CommonAPI.post(third_user, %{"status" => "repeated post"})
|
{:ok, activity} = CommonAPI.post(third_user, %{"status" => "repeated post"})
|
||||||
{:ok, _, _} = CommonAPI.repeat(activity.id, following)
|
{:ok, _, _} = CommonAPI.repeat(activity.id, following)
|
||||||
|
|
||||||
ret_conn = get(conn, "/api/v1/timelines/home")
|
ret_conn = get(conn, uri)
|
||||||
|
|
||||||
assert Enum.empty?(json_response(ret_conn, :ok))
|
assert Enum.empty?(json_response(ret_conn, :ok))
|
||||||
|
|
||||||
{:ok, _user} = User.follow(user, following)
|
{:ok, _user} = User.follow(user, following)
|
||||||
|
|
||||||
ret_conn = get(conn, "/api/v1/timelines/home")
|
ret_conn = get(conn, uri)
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{
|
%{
|
||||||
|
@ -59,7 +82,7 @@ test "the home timeline", %{user: user, conn: conn} do
|
||||||
|
|
||||||
{:ok, _user} = User.follow(third_user, user)
|
{:ok, _user} = User.follow(third_user, user)
|
||||||
|
|
||||||
ret_conn = get(conn, "/api/v1/timelines/home")
|
ret_conn = get(conn, uri)
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{
|
%{
|
||||||
|
|
Loading…
Reference in New Issue