Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms

This commit is contained in:
lain 2020-04-15 14:41:43 +02:00
commit 44bfb491ea
60 changed files with 826 additions and 245 deletions

View file

@ -4,9 +4,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [unreleased] ## [unreleased]
### Changed
- **Breaking:** BBCode and Markdown formatters will no longer return any `\n` and only use `<br/>` for newlines
### Removed ### Removed
- **Breaking:** removed `with_move` parameter from notifications timeline. - **Breaking:** removed `with_move` parameter from notifications timeline.
@ -18,11 +15,61 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
<details> <details>
<summary>API Changes</summary> <summary>API Changes</summary>
- Mastodon API: Support for `include_types` in `/api/v1/notifications`. - Mastodon API: Support for `include_types` in `/api/v1/notifications`.
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.
</details> </details>
### Fixed
- Support pagination in conversations API
## [unreleased-patch]
### Fixed
- Logger configuration through AdminFE
## [2.0.2] - 2020-04-08
### Added
- Support for Funkwhale's `Audio` activity
- Admin API: `PATCH /api/pleroma/admin/users/:nickname/update_credentials`
### Fixed
- Blocked/muted users still generating push notifications
- Input textbox for bio ignoring newlines
- OTP: Inability to use PostgreSQL databases with SSL
- `user delete_activities` breaking when trying to delete already deleted posts
- Incorrect URL for Funkwhale channels
### Upgrade notes
1. Restart Pleroma
## [2.0.1] - 2020-03-15
### Security
- Static-FE: Fix remote posts not being sanitized
### Fixed
- 500 errors when no `Accept` header is present if Static-FE is enabled
- Instance panel not being updated immediately due to wrong `Cache-Control` headers
- Statuses posted with BBCode/Markdown having unncessary newlines in Pleroma-FE
- OTP: Fix some settings not being migrated to in-database config properly
- No `Cache-Control` headers on attachment/media proxy requests
- Character limit enforcement being off by 1
- Mastodon Streaming API: hashtag timelines not working
### Changed
- BBCode and Markdown formatters will no longer return any `\n` and only use `<br/>` for newlines
- Mastodon API: Allow registration without email if email verification is not enabled
### Upgrade notes
#### Nginx only
1. Remove `proxy_ignore_headers Cache-Control;` and `proxy_hide_header Cache-Control;` from your config.
#### Everyone
1. Run database migrations (inside Pleroma directory):
- OTP: `./bin/pleroma_ctl migrate`
- From Source: `mix ecto.migrate`
2. Restart Pleroma
## [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`

View file

@ -386,47 +386,56 @@ defmodule Pleroma.LoadTesting.Fetcher 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
}, },

View file

@ -240,6 +240,8 @@ config :pleroma, :instance,
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,

View file

@ -5,7 +5,6 @@
defmodule Mix.Pleroma do defmodule Mix.Pleroma do
@doc "Common functions to be reused in mix tasks" @doc "Common functions to be reused in mix tasks"
def start_pleroma do def start_pleroma do
Mix.Task.run("app.start")
Application.put_env(:phoenix, :serve_endpoints, false, persistent: true) Application.put_env(:phoenix, :serve_endpoints, false, persistent: true)
if Pleroma.Config.get(:env) != :test do if Pleroma.Config.get(:env) != :test do

View file

@ -67,7 +67,8 @@ defmodule Mix.Tasks.Pleroma.Benchmark 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
}, },

View file

@ -54,10 +54,19 @@ defmodule Pleroma.Config.TransferTask do
[:pleroma, nil, :prometheus] [:pleroma, nil, :prometheus]
end end
{logger, other} =
(Repo.all(ConfigDB) ++ deleted_settings)
|> Enum.map(&transform_and_merge/1)
|> Enum.split_with(fn {group, _, _, _} -> group in [:logger, :quack] end)
logger
|> Enum.sort()
|> Enum.each(&configure/1)
started_applications = Application.started_applications() started_applications = Application.started_applications()
(Repo.all(ConfigDB) ++ deleted_settings) other
|> Enum.map(&merge_and_update/1) |> Enum.map(&update/1)
|> Enum.uniq() |> Enum.uniq()
|> Enum.reject(&(&1 in reject_restart)) |> Enum.reject(&(&1 in reject_restart))
|> maybe_set_pleroma_last() |> maybe_set_pleroma_last()
@ -81,51 +90,66 @@ defmodule Pleroma.Config.TransferTask do
end end
end end
defp group_for_restart(:logger, key, _, merged_value) do defp transform_and_merge(%{group: group, key: key, value: value} = setting) do
# change logger configuration in runtime, without restart group = ConfigDB.from_string(group)
if Keyword.keyword?(merged_value) and key = ConfigDB.from_string(key)
key not in [:compile_time_application, :backends, :compile_time_purge_matching] do value = ConfigDB.from_binary(value)
Logger.configure_backend(key, merged_value)
else
Logger.configure([{key, merged_value}])
end
nil default = Config.Holder.default_config(group, key)
merged =
cond do
Ecto.get_meta(setting, :state) == :deleted -> default
can_be_merged?(default, value) -> ConfigDB.merge_group(group, key, default, value)
true -> value
end
{group, key, value, merged}
end end
defp group_for_restart(group, _, _, _) when group != :pleroma, do: group # change logger configuration in runtime, without restart
defp configure({:quack, key, _, merged}) do
defp group_for_restart(group, key, value, _) do Logger.configure_backend(Quack.Logger, [{key, merged}])
if pleroma_need_restart?(group, key, value), do: group :ok = update_env(:quack, key, merged)
end end
defp merge_and_update(setting) do defp configure({_, :backends, _, merged}) do
# removing current backends
Enum.each(Application.get_env(:logger, :backends), &Logger.remove_backend/1)
Enum.each(merged, &Logger.add_backend/1)
:ok = update_env(:logger, :backends, merged)
end
defp configure({group, key, _, merged}) do
merged =
if key == :console do
put_in(merged[:format], merged[:format] <> "\n")
else
merged
end
backend =
if key == :ex_syslogger,
do: {ExSyslogger, :ex_syslogger},
else: key
Logger.configure_backend(backend, merged)
:ok = update_env(:logger, group, merged)
end
defp update({group, key, value, merged}) do
try do try do
key = ConfigDB.from_string(setting.key) :ok = update_env(group, key, merged)
group = ConfigDB.from_string(setting.group)
default = Config.Holder.default_config(group, key) if group != :pleroma or pleroma_need_restart?(group, key, value), do: group
value = ConfigDB.from_binary(setting.value)
merged_value =
cond do
Ecto.get_meta(setting, :state) == :deleted -> default
can_be_merged?(default, value) -> ConfigDB.merge_group(group, key, default, value)
true -> value
end
:ok = update_env(group, key, merged_value)
group_for_restart(group, key, value, merged_value)
rescue rescue
error -> error ->
error_msg = error_msg =
"updating env causes error, group: " <> "updating env causes error, group: #{inspect(group)}, key: #{inspect(key)}, value: #{
inspect(setting.group) <> inspect(value)
" key: " <> } error: #{inspect(error)}"
inspect(setting.key) <>
" value: " <>
inspect(ConfigDB.from_binary(setting.value)) <> " error: " <> inspect(error)
Logger.warn(error_msg) Logger.warn(error_msg)
@ -133,6 +157,9 @@ defmodule Pleroma.Config.TransferTask do
end end
end end
defp update_env(group, key, nil), do: Application.delete_env(group, key)
defp update_env(group, key, value), do: Application.put_env(group, key, value)
@spec pleroma_need_restart?(atom(), atom(), any()) :: boolean() @spec pleroma_need_restart?(atom(), atom(), any()) :: boolean()
def pleroma_need_restart?(group, key, value) do def pleroma_need_restart?(group, key, value) do
group_and_key_need_reboot?(group, key) or group_and_subkey_need_reboot?(group, key, value) group_and_key_need_reboot?(group, key) or group_and_subkey_need_reboot?(group, key, value)
@ -150,9 +177,6 @@ defmodule Pleroma.Config.TransferTask do
end) end)
end end
defp update_env(group, key, nil), do: Application.delete_env(group, key)
defp update_env(group, key, value), do: Application.put_env(group, key, value)
defp restart(_, :pleroma, env), do: Restarter.Pleroma.restart_after_boot(env) defp restart(_, :pleroma, env), do: Restarter.Pleroma.restart_after_boot(env)
defp restart(started_applications, app, _) do defp restart(started_applications, app, _) do

View file

@ -4,10 +4,16 @@
import EctoEnum import EctoEnum
defenum(UserRelationshipTypeEnum, defenum(Pleroma.UserRelationship.Type,
block: 1, block: 1,
mute: 2, mute: 2,
reblog_mute: 3, reblog_mute: 3,
notification_mute: 4, notification_mute: 4,
inverse_subscription: 5 inverse_subscription: 5
) )
defenum(Pleroma.FollowingRelationship.State,
follow_pending: 1,
follow_accept: 2,
follow_reject: 3
)

View file

@ -8,12 +8,13 @@ defmodule Pleroma.FollowingRelationship do
import Ecto.Changeset import Ecto.Changeset
import Ecto.Query import Ecto.Query
alias Ecto.Changeset
alias FlakeId.Ecto.CompatType alias FlakeId.Ecto.CompatType
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
schema "following_relationships" do schema "following_relationships" do
field(:state, :string, default: "accept") field(:state, Pleroma.FollowingRelationship.State, default: :follow_pending)
belongs_to(:follower, User, type: CompatType) belongs_to(:follower, User, type: CompatType)
belongs_to(:following, User, type: CompatType) belongs_to(:following, User, type: CompatType)
@ -27,6 +28,18 @@ defmodule Pleroma.FollowingRelationship do
|> put_assoc(:follower, attrs.follower) |> put_assoc(:follower, attrs.follower)
|> put_assoc(:following, attrs.following) |> put_assoc(:following, attrs.following)
|> validate_required([:state, :follower, :following]) |> validate_required([:state, :follower, :following])
|> unique_constraint(:follower_id,
name: :following_relationships_follower_id_following_id_index
)
|> validate_not_self_relationship()
end
def state_to_enum(state) when state in ["pending", "accept", "reject"] do
String.to_existing_atom("follow_#{state}")
end
def state_to_enum(state) do
raise "State is not convertible to Pleroma.FollowingRelationship.State: #{state}"
end end
def get(%User{} = follower, %User{} = following) do def get(%User{} = follower, %User{} = following) do
@ -35,7 +48,7 @@ defmodule Pleroma.FollowingRelationship do
|> Repo.one() |> Repo.one()
end end
def update(follower, following, "reject"), do: unfollow(follower, following) def update(follower, following, :follow_reject), do: unfollow(follower, following)
def update(%User{} = follower, %User{} = following, state) do def update(%User{} = follower, %User{} = following, state) do
case get(follower, following) do case get(follower, following) do
@ -50,7 +63,7 @@ defmodule Pleroma.FollowingRelationship do
end end
end end
def follow(%User{} = follower, %User{} = following, state \\ "accept") do def follow(%User{} = follower, %User{} = following, state \\ :follow_accept) do
%__MODULE__{} %__MODULE__{}
|> changeset(%{follower: follower, following: following, state: state}) |> changeset(%{follower: follower, following: following, state: state})
|> Repo.insert(on_conflict: :nothing) |> Repo.insert(on_conflict: :nothing)
@ -80,7 +93,7 @@ defmodule Pleroma.FollowingRelationship do
def get_follow_requests(%User{id: id}) do def get_follow_requests(%User{id: id}) do
__MODULE__ __MODULE__
|> join(:inner, [r], f in assoc(r, :follower)) |> join(:inner, [r], f in assoc(r, :follower))
|> where([r], r.state == "pending") |> where([r], r.state == ^:follow_pending)
|> where([r], r.following_id == ^id) |> where([r], r.following_id == ^id)
|> select([r, f], f) |> select([r, f], f)
|> Repo.all() |> Repo.all()
@ -88,7 +101,7 @@ defmodule Pleroma.FollowingRelationship do
def following?(%User{id: follower_id}, %User{id: followed_id}) do def following?(%User{id: follower_id}, %User{id: followed_id}) do
__MODULE__ __MODULE__
|> where(follower_id: ^follower_id, following_id: ^followed_id, state: "accept") |> where(follower_id: ^follower_id, following_id: ^followed_id, state: ^:follow_accept)
|> Repo.exists?() |> Repo.exists?()
end end
@ -97,7 +110,7 @@ defmodule Pleroma.FollowingRelationship do
__MODULE__ __MODULE__
|> join(:inner, [r], u in User, on: r.following_id == u.id) |> join(:inner, [r], u in User, on: r.following_id == u.id)
|> where([r], r.follower_id == ^user.id) |> where([r], r.follower_id == ^user.id)
|> where([r], r.state == "accept") |> where([r], r.state == ^:follow_accept)
|> select([r, u], u.follower_address) |> select([r, u], u.follower_address)
|> Repo.all() |> Repo.all()
@ -157,4 +170,30 @@ defmodule Pleroma.FollowingRelationship do
fr -> fr.follower_id == follower.id and fr.following_id == following.id fr -> fr.follower_id == follower.id and fr.following_id == following.id
end) end)
end end
defp validate_not_self_relationship(%Changeset{} = changeset) do
changeset
|> validate_follower_id_following_id_inequality()
|> validate_following_id_follower_id_inequality()
end
defp validate_follower_id_following_id_inequality(%Changeset{} = changeset) do
validate_change(changeset, :follower_id, fn _, follower_id ->
if follower_id == get_field(changeset, :following_id) do
[source_id: "can't be equal to following_id"]
else
[]
end
end)
end
defp validate_following_id_follower_id_inequality(%Changeset{} = changeset) do
validate_change(changeset, :following_id, fn _, following_id ->
if following_id == get_field(changeset, :follower_id) do
[target_id: "can't be equal to follower_id"]
else
[]
end
end)
end
end end

View file

@ -42,13 +42,13 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do
else else
{:user_match, false} -> {:user_match, false} ->
Logger.debug("Failed to map identity from signature (payload actor mismatch)") Logger.debug("Failed to map identity from signature (payload actor mismatch)")
Logger.debug("key_id=#{key_id_from_conn(conn)}, actor=#{actor}") Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{inspect(actor)}")
assign(conn, :valid_signature, false) assign(conn, :valid_signature, false)
# remove me once testsuite uses mapped capabilities instead of what we do now # remove me once testsuite uses mapped capabilities instead of what we do now
{:user, nil} -> {:user, nil} ->
Logger.debug("Failed to map identity from signature (lookup failure)") Logger.debug("Failed to map identity from signature (lookup failure)")
Logger.debug("key_id=#{key_id_from_conn(conn)}, actor=#{actor}") Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}")
conn conn
end end
end end
@ -60,7 +60,7 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do
else else
_ -> _ ->
Logger.debug("Failed to map identity from signature (no payload actor mismatch)") Logger.debug("Failed to map identity from signature (no payload actor mismatch)")
Logger.debug("key_id=#{key_id_from_conn(conn)}") Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}")
assign(conn, :valid_signature, false) assign(conn, :valid_signature, false)
end end
end end

View file

@ -41,6 +41,7 @@ defmodule Pleroma.Plugs.UploadedMedia do
conn -> conn ->
conn conn
end end
|> merge_resp_headers([{"content-security-policy", "sandbox"}])
config = Pleroma.Config.get(Pleroma.Upload) config = Pleroma.Config.get(Pleroma.Upload)

View file

@ -243,7 +243,7 @@ defmodule Pleroma.Pool.Connections do
@impl true @impl true
def handle_info({:DOWN, _ref, :process, conn_pid, reason}, state) do def handle_info({:DOWN, _ref, :process, conn_pid, reason}, state) do
Logger.debug("received DOWM message for #{inspect(conn_pid)} reason -> #{inspect(reason)}") Logger.debug("received DOWN message for #{inspect(conn_pid)} reason -> #{inspect(reason)}")
state = state =
with {key, conn} <- find_conn(state.conns, conn_pid) do with {key, conn} <- find_conn(state.conns, conn_pid) do

View file

@ -700,7 +700,7 @@ defmodule Pleroma.User do
@spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()} @spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do
follow(follower, followed, "pending") follow(follower, followed, :follow_pending)
end end
def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do
@ -720,14 +720,14 @@ defmodule Pleroma.User do
def follow_all(follower, followeds) do def follow_all(follower, followeds) do
followeds followeds
|> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end) |> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end)
|> Enum.each(&follow(follower, &1, "accept")) |> Enum.each(&follow(follower, &1, :follow_accept))
set_cache(follower) set_cache(follower)
end end
defdelegate following(user), to: FollowingRelationship defdelegate following(user), to: FollowingRelationship
def follow(%User{} = follower, %User{} = followed, state \\ "accept") do def follow(%User{} = follower, %User{} = followed, state \\ :follow_accept) do
deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked]) deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
cond do cond do
@ -754,7 +754,7 @@ defmodule Pleroma.User do
def unfollow(%User{} = follower, %User{} = followed) do def unfollow(%User{} = follower, %User{} = followed) do
case get_follow_state(follower, followed) do case get_follow_state(follower, followed) do
state when state in ["accept", "pending"] -> state when state in [:follow_pending, :follow_accept] ->
FollowingRelationship.unfollow(follower, followed) FollowingRelationship.unfollow(follower, followed)
{:ok, followed} = update_follower_count(followed) {:ok, followed} = update_follower_count(followed)
@ -772,6 +772,7 @@ defmodule Pleroma.User do
defdelegate following?(follower, followed), to: FollowingRelationship defdelegate following?(follower, followed), to: FollowingRelationship
@doc "Returns follow state as Pleroma.FollowingRelationship.State value"
def get_follow_state(%User{} = follower, %User{} = following) do def get_follow_state(%User{} = follower, %User{} = following) do
following_relationship = FollowingRelationship.get(follower, following) following_relationship = FollowingRelationship.get(follower, following)
get_follow_state(follower, following, following_relationship) get_follow_state(follower, following, following_relationship)
@ -785,8 +786,11 @@ defmodule Pleroma.User do
case {following_relationship, following.local} do case {following_relationship, following.local} do
{nil, false} -> {nil, false} ->
case Utils.fetch_latest_follow(follower, following) do case Utils.fetch_latest_follow(follower, following) do
%{data: %{"state" => state}} when state in ["pending", "accept"] -> state %Activity{data: %{"state" => state}} when state in ["pending", "accept"] ->
_ -> nil FollowingRelationship.state_to_enum(state)
_ ->
nil
end end
{%{state: state}, _} -> {%{state: state}, _} ->
@ -1285,7 +1289,7 @@ defmodule Pleroma.User do
def blocks?(%User{} = user, %User{} = target) do def blocks?(%User{} = user, %User{} = target) do
blocks_user?(user, target) || blocks_user?(user, target) ||
(!User.following?(user, target) && blocks_domain?(user, target)) (blocks_domain?(user, target) and not User.following?(user, target))
end end
def blocks_user?(%User{} = user, %User{} = target) do def blocks_user?(%User{} = user, %User{} = target) do

View file

@ -148,7 +148,7 @@ defmodule Pleroma.User.Query do
as: :relationships, as: :relationships,
on: r.following_id == ^id and r.follower_id == u.id on: r.following_id == ^id and r.follower_id == u.id
) )
|> where([relationships: r], r.state == "accept") |> where([relationships: r], r.state == ^:follow_accept)
end end
defp compose_query({:friends, %User{id: id}}, query) do defp compose_query({:friends, %User{id: id}}, query) do
@ -158,7 +158,7 @@ defmodule Pleroma.User.Query do
as: :relationships, as: :relationships,
on: r.following_id == u.id and r.follower_id == ^id on: r.following_id == u.id and r.follower_id == ^id
) )
|> where([relationships: r], r.state == "accept") |> where([relationships: r], r.state == ^:follow_accept)
end end
defp compose_query({:recipients_from_activity, to}, query) do defp compose_query({:recipients_from_activity, to}, query) do
@ -173,7 +173,7 @@ defmodule Pleroma.User.Query do
) )
|> where( |> where(
[u, following: f, relationships: r], [u, following: f, relationships: r],
u.ap_id in ^to or (f.follower_address in ^to and r.state == "accept") u.ap_id in ^to or (f.follower_address in ^to and r.state == ^:follow_accept)
) )
|> distinct(true) |> distinct(true)
end end

View file

@ -8,6 +8,7 @@ defmodule Pleroma.UserRelationship do
import Ecto.Changeset import Ecto.Changeset
import Ecto.Query import Ecto.Query
alias Ecto.Changeset
alias Pleroma.FollowingRelationship alias Pleroma.FollowingRelationship
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
@ -16,12 +17,12 @@ defmodule Pleroma.UserRelationship do
schema "user_relationships" do schema "user_relationships" do
belongs_to(:source, User, type: FlakeId.Ecto.CompatType) belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
belongs_to(:target, User, type: FlakeId.Ecto.CompatType) belongs_to(:target, User, type: FlakeId.Ecto.CompatType)
field(:relationship_type, UserRelationshipTypeEnum) field(:relationship_type, Pleroma.UserRelationship.Type)
timestamps(updated_at: false) timestamps(updated_at: false)
end end
for relationship_type <- Keyword.keys(UserRelationshipTypeEnum.__enum_map__()) do for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do
# `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`, # `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`,
# `def create_notification_mute/2`, `def create_inverse_subscription/2` # `def create_notification_mute/2`, `def create_inverse_subscription/2`
def unquote(:"create_#{relationship_type}")(source, target), def unquote(:"create_#{relationship_type}")(source, target),
@ -40,7 +41,7 @@ defmodule Pleroma.UserRelationship do
def user_relationship_types, do: Keyword.keys(user_relationship_mappings()) def user_relationship_types, do: Keyword.keys(user_relationship_mappings())
def user_relationship_mappings, do: UserRelationshipTypeEnum.__enum_map__() def user_relationship_mappings, do: Pleroma.UserRelationship.Type.__enum_map__()
def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do
user_relationship user_relationship
@ -129,17 +130,27 @@ defmodule Pleroma.UserRelationship 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)
@ -147,16 +158,14 @@ defmodule Pleroma.UserRelationship do
%{user_relationships: user_relationships, following_relationships: following_relationships} %{user_relationships: user_relationships, following_relationships: following_relationships}
end end
defp validate_not_self_relationship(%Ecto.Changeset{} = changeset) do defp validate_not_self_relationship(%Changeset{} = changeset) do
changeset changeset
|> validate_change(:target_id, fn _, target_id -> |> validate_source_id_target_id_inequality()
if target_id == get_field(changeset, :source_id) do |> validate_target_id_source_id_inequality()
[target_id: "can't be equal to source_id"] end
else
[] defp validate_source_id_target_id_inequality(%Changeset{} = changeset) do
end validate_change(changeset, :source_id, fn _, source_id ->
end)
|> validate_change(:source_id, fn _, source_id ->
if source_id == get_field(changeset, :target_id) do if source_id == get_field(changeset, :target_id) do
[source_id: "can't be equal to target_id"] [source_id: "can't be equal to target_id"]
else else
@ -164,4 +173,14 @@ defmodule Pleroma.UserRelationship do
end end
end) end)
end end
defp validate_target_id_source_id_inequality(%Changeset{} = changeset) do
validate_change(changeset, :target_id, fn _, target_id ->
if target_id == get_field(changeset, :source_id) do
[target_id: "can't be equal to source_id"]
else
[]
end
end)
end
end end

View file

@ -722,7 +722,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
end end
defp fetch_activities_for_context_query(context, opts) do def fetch_activities_for_context_query(context, opts) do
public = [Constants.as_public()] public = [Constants.as_public()]
recipients = recipients =

View file

@ -206,16 +206,46 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier 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 ->
media_type = data["mediaType"] || data["mimeType"] url =
href = data["url"] || data["href"] cond do
url = [%{"type" => "Link", "mediaType" => media_type, "href" => href}] is_list(data["url"]) -> List.first(data["url"])
is_map(data["url"]) -> data["url"]
true -> nil
end
data media_type =
|> Map.put("mediaType", media_type) cond do
|> Map.put("url", url) is_map(url) && is_binary(url["mediaType"]) -> url["mediaType"]
is_binary(data["mediaType"]) -> data["mediaType"]
is_binary(data["mimeType"]) -> data["mimeType"]
true -> nil
end
href =
cond do
is_map(url) && is_binary(url["href"]) -> url["href"]
is_binary(data["url"]) -> data["url"]
is_binary(data["href"]) -> data["href"]
end
attachment_url =
%{"href" => href}
|> add_if_present("mediaType", media_type)
|> add_if_present("type", Map.get(url || %{}, "type"))
%{"url" => [attachment_url]}
|> add_if_present("mediaType", media_type)
|> add_if_present("type", data["type"])
|> add_if_present("name", data["name"])
end) end)
Map.put(object, "attachment", attachments) Map.put(object, "attachment", attachments)
@ -495,7 +525,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{_, {:ok, follower}} <- {:follow, User.follow(follower, followed)}, {_, {:ok, follower}} <- {:follow, User.follow(follower, followed)},
{_, {:ok, _}} <- {_, {:ok, _}} <-
{:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")}, {:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")},
{:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do {:ok, _relationship} <-
FollowingRelationship.update(follower, followed, :follow_accept) do
ActivityPub.accept(%{ ActivityPub.accept(%{
to: [follower.ap_id], to: [follower.ap_id],
actor: followed, actor: followed,
@ -505,7 +536,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
else else
{:user_blocked, true} -> {:user_blocked, true} ->
{:ok, _} = Utils.update_follow_state_for_all(activity, "reject") {:ok, _} = Utils.update_follow_state_for_all(activity, "reject")
{:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject") {:ok, _relationship} = FollowingRelationship.update(follower, followed, :follow_reject)
ActivityPub.reject(%{ ActivityPub.reject(%{
to: [follower.ap_id], to: [follower.ap_id],
@ -516,7 +547,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:follow, {:error, _}} -> {:follow, {:error, _}} ->
{:ok, _} = Utils.update_follow_state_for_all(activity, "reject") {:ok, _} = Utils.update_follow_state_for_all(activity, "reject")
{:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject") {:ok, _relationship} = FollowingRelationship.update(follower, followed, :follow_reject)
ActivityPub.reject(%{ ActivityPub.reject(%{
to: [follower.ap_id], to: [follower.ap_id],
@ -526,7 +557,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
}) })
{:user_locked, true} -> {:user_locked, true} ->
{:ok, _relationship} = FollowingRelationship.update(follower, followed, "pending") {:ok, _relationship} = FollowingRelationship.update(follower, followed, :follow_pending)
:noop :noop
end end
@ -546,7 +577,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, follow_activity} <- get_follow_activity(follow_object, followed), {:ok, follow_activity} <- get_follow_activity(follow_object, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
{:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept) do
ActivityPub.accept(%{ ActivityPub.accept(%{
to: follow_activity.data["to"], to: follow_activity.data["to"],
type: "Accept", type: "Accept",
@ -569,7 +600,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, follow_activity} <- get_follow_activity(follow_object, followed), {:ok, follow_activity} <- get_follow_activity(follow_object, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"),
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
{:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"), {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_reject),
{:ok, activity} <- {:ok, activity} <-
ActivityPub.reject(%{ ActivityPub.reject(%{
to: follow_activity.data["to"], to: follow_activity.data["to"],

View file

@ -258,7 +258,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController 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 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController 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 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController 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

View file

@ -38,7 +38,12 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
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})
} }

View file

@ -31,7 +31,7 @@ defmodule Pleroma.Web.ApiSpec do
password: %OpenApiSpex.OAuthFlow{ password: %OpenApiSpex.OAuthFlow{
authorizationUrl: "/oauth/authorize", authorizationUrl: "/oauth/authorize",
tokenUrl: "/oauth/token", tokenUrl: "/oauth/token",
scopes: %{"read" => "read"} scopes: %{"read" => "read", "write" => "write", "follow" => "follow"}
} }
} }
} }

View file

@ -4,7 +4,7 @@
defmodule Pleroma.Web.ApiSpec.Helpers do defmodule Pleroma.Web.ApiSpec.Helpers do
def request_body(description, schema_ref, opts \\ []) do def request_body(description, schema_ref, opts \\ []) do
media_types = ["application/json", "multipart/form-data"] media_types = ["application/json", "multipart/form-data", "application/x-www-form-urlencoded"]
content = content =
media_types media_types

View file

@ -0,0 +1,64 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Helpers
alias Pleroma.Web.ApiSpec.Schemas.DomainBlockRequest
alias Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse
def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, [])
end
def index_operation do
%Operation{
tags: ["domain_blocks"],
summary: "Fetch domain blocks",
description: "View domains the user has blocked.",
security: [%{"oAuth" => ["follow", "read:blocks"]}],
operationId: "DomainBlockController.index",
responses: %{
200 => Operation.response("Domain blocks", "application/json", DomainBlocksResponse)
}
}
end
def create_operation do
%Operation{
tags: ["domain_blocks"],
summary: "Block a domain",
description: """
Block a domain to:
- hide all public posts from it
- hide all notifications from it
- remove all followers from it
- prevent following new users from it (but does not remove existing follows)
""",
operationId: "DomainBlockController.create",
requestBody: Helpers.request_body("Parameters", DomainBlockRequest, required: true),
security: [%{"oAuth" => ["follow", "write:blocks"]}],
responses: %{
200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
}
}
end
def delete_operation do
%Operation{
tags: ["domain_blocks"],
summary: "Unblock a domain",
description: "Remove a domain block, if it exists in the user's array of blocked domains.",
operationId: "DomainBlockController.delete",
requestBody: Helpers.request_body("Parameters", DomainBlockRequest, required: true),
security: [%{"oAuth" => ["follow", "write:blocks"]}],
responses: %{
200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
}
}
end
end

View file

@ -0,0 +1,20 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.DomainBlockRequest do
alias OpenApiSpex.Schema
require OpenApiSpex
OpenApiSpex.schema(%{
title: "DomainBlockRequest",
type: :object,
properties: %{
domain: %Schema{type: :string}
},
required: [:domain],
example: %{
"domain" => "facebook.com"
}
})
end

View file

@ -0,0 +1,16 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse do
require OpenApiSpex
alias OpenApiSpex.Schema
OpenApiSpex.schema(%{
title: "DomainBlocksResponse",
description: "Response schema for domain blocks",
type: :array,
items: %Schema{type: :string},
example: ["google.com", "facebook.com"]
})
end

View file

@ -187,7 +187,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft 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

View file

@ -68,7 +68,7 @@ defmodule Pleroma.Web.CommonAPI do
with {:ok, follower} <- User.follow(follower, followed), with {:ok, follower} <- User.follow(follower, followed),
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
{:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept"), {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept),
{:ok, _activity} <- {:ok, _activity} <-
ActivityPub.accept(%{ ActivityPub.accept(%{
to: [follower.ap_id], to: [follower.ap_id],
@ -83,7 +83,7 @@ defmodule Pleroma.Web.CommonAPI do
def reject_follow_request(follower, followed) do def reject_follow_request(follower, followed) do
with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"),
{:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"), {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_reject),
{:ok, _activity} <- {:ok, _activity} <-
ActivityPub.reject(%{ ActivityPub.reject(%{
to: [follower.ap_id], to: [follower.ap_id],

View file

@ -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 @@ defmodule Pleroma.Web.ControllerHelper 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

View file

@ -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 @@ defmodule Pleroma.Web.MastodonAPI.AccountController 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

View file

@ -8,6 +8,9 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do
alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.User alias Pleroma.User
plug(OpenApiSpex.Plug.CastAndValidate)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DomainBlockOperation
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
%{scopes: ["follow", "read:blocks"]} when action == :index %{scopes: ["follow", "read:blocks"]} when action == :index
@ -26,13 +29,13 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do
end end
@doc "POST /api/v1/domain_blocks" @doc "POST /api/v1/domain_blocks"
def create(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do def create(%{assigns: %{user: blocker}, body_params: %{domain: domain}} = conn, _params) do
User.block_domain(blocker, domain) User.block_domain(blocker, domain)
json(conn, %{}) json(conn, %{})
end end
@doc "DELETE /api/v1/domain_blocks" @doc "DELETE /api/v1/domain_blocks"
def delete(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do def delete(%{assigns: %{user: blocker}, body_params: %{domain: domain}} = conn, _params) do
User.unblock_domain(blocker, domain) User.unblock_domain(blocker, domain)
json(conn, %{}) json(conn, %{})
end end

View file

@ -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 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController 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
@ -66,7 +70,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
json(conn, %{}) json(conn, %{})
end end
# POST /api/v1/notifications/dismiss # POST /api/v1/notifications/:id/dismiss
# POST /api/v1/notifications/dismiss (deprecated)
def dismiss(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do def dismiss(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do
with {:ok, _notif} <- Notification.dismiss(user, id) do with {:ok, _notif} <- Notification.dismiss(user, id) do
json(conn, %{}) json(conn, %{})

View file

@ -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 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
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 @@ defmodule Pleroma.Web.MastodonAPI.SearchController 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

View file

@ -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 @@ defmodule Pleroma.Web.MastodonAPI.StatusController 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 @@ defmodule Pleroma.Web.MastodonAPI.StatusController 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 @@ defmodule Pleroma.Web.MastodonAPI.StatusController 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

View file

@ -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 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController 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 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController 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 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController 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 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController 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 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController 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

View file

@ -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) ->
@ -73,7 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
followed_by = followed_by =
if following_relationships do if following_relationships do
case FollowingRelationship.find(following_relationships, target, reading_user) do case FollowingRelationship.find(following_relationships, target, reading_user) do
%{state: "accept"} -> true %{state: :follow_accept} -> true
_ -> false _ -> false
end end
else else
@ -83,7 +84,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
# NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags # NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags
%{ %{
id: to_string(target.id), id: to_string(target.id),
following: follow_state == "accept", following: follow_state == :follow_accept,
followed_by: followed_by, followed_by: followed_by,
blocking: blocking:
UserRelationship.exists?( UserRelationship.exists?(
@ -125,7 +126,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
reading_user, reading_user,
&User.subscribed_to?(&2, &1) &User.subscribed_to?(&2, &1)
), ),
requested: follow_state == "pending", requested: follow_state == :follow_pending,
domain_blocking: User.blocks_domain?(reading_user, target), domain_blocking: User.blocks_domain?(reading_user, target),
showing_reblogs: showing_reblogs:
not UserRelationship.exists?( not UserRelationship.exists?(
@ -192,11 +193,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
end) end)
relationship = relationship =
render("relationship.json", %{ if opts[:skip_relationships] do
user: opts[:for], %{}
target: user, else
relationships: opts[:relationships] render("relationship.json", %{
}) user: opts[:for],
target: user,
relationships: opts[:relationships]
})
end
%{ %{
id: to_string(user.id), id: to_string(user.id),

View file

@ -51,14 +51,15 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|> 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 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
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 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
} }
} }
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 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
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" ->

View file

@ -99,7 +99,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView 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 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
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 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
_ -> [] _ -> []
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 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
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),

View file

@ -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 @@ defmodule Pleroma.Web.PleromaAPI.AccountController 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"

View file

@ -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
@ -110,12 +110,11 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
end end
def conversation_statuses( def conversation_statuses(
%{assigns: %{user: user}} = conn, %{assigns: %{user: %{id: user_id} = user}} = conn,
%{"id" => participation_id} = params %{"id" => participation_id} = params
) do ) do
with %Participation{} = participation <- with %Participation{user_id: ^user_id} = participation <-
Participation.get(participation_id, preload: [:conversation]), Participation.get(participation_id, preload: [:conversation]) do
true <- user.id == participation.user_id do
params = params =
params params
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
@ -124,13 +123,19 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
activities = activities =
participation.conversation.ap_id participation.conversation.ap_id
|> ActivityPub.fetch_activities_for_context(params) |> ActivityPub.fetch_activities_for_context_query(params)
|> Pleroma.Pagination.fetch_paginated(Map.put(params, "total", false))
|> Enum.reverse() |> Enum.reverse()
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 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
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

View file

@ -64,5 +64,8 @@ defmodule Pleroma.Web.RichMedia.Helpers do
def fetch_data_for_activity(_), do: %{} def fetch_data_for_activity(_), do: %{}
def perform(:fetch, %Activity{} = activity), do: fetch_data_for_activity(activity) def perform(:fetch, %Activity{} = activity) do
fetch_data_for_activity(activity)
:ok
end
end end

View file

@ -361,9 +361,11 @@ defmodule Pleroma.Web.Router do
get("/notifications", NotificationController, :index) get("/notifications", NotificationController, :index)
get("/notifications/:id", NotificationController, :show) get("/notifications/:id", NotificationController, :show)
post("/notifications/:id/dismiss", NotificationController, :dismiss)
post("/notifications/clear", NotificationController, :clear) post("/notifications/clear", NotificationController, :clear)
post("/notifications/dismiss", NotificationController, :dismiss)
delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple) delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple)
# Deprecated: was removed in Mastodon v3, use `/notifications/:id/dismiss` instead
post("/notifications/dismiss", NotificationController, :dismiss)
get("/scheduled_statuses", ScheduledActivityController, :index) get("/scheduled_statuses", ScheduledActivityController, :index)
get("/scheduled_statuses/:id", ScheduledActivityController, :show) get("/scheduled_statuses/:id", ScheduledActivityController, :show)

13
mix.exs
View file

@ -37,12 +37,21 @@ defmodule Pleroma.Mixfile do
pleroma: [ pleroma: [
include_executables_for: [:unix], include_executables_for: [:unix],
applications: [ex_syslogger: :load, syslog: :load], applications: [ex_syslogger: :load, syslog: :load],
steps: [:assemble, &copy_files/1, &copy_nginx_config/1] steps: [:assemble, &put_otp_version/1, &copy_files/1, &copy_nginx_config/1]
] ]
] ]
] ]
end end
def put_otp_version(%{path: target_path} = release) do
File.write!(
Path.join([target_path, "OTP_VERSION"]),
Pleroma.OTPVersion.version()
)
release
end
def copy_files(%{path: target_path} = release) do def copy_files(%{path: target_path} = release) do
File.cp_r!("./rel/files", target_path) File.cp_r!("./rel/files", target_path)
release release
@ -108,7 +117,7 @@ defmodule Pleroma.Mixfile do
{:ecto_enum, "~> 1.4"}, {:ecto_enum, "~> 1.4"},
{:ecto_sql, "~> 3.3.2"}, {:ecto_sql, "~> 3.3.2"},
{:postgrex, ">= 0.13.5"}, {:postgrex, ">= 0.13.5"},
{:oban, "~> 0.12.1"}, {:oban, "~> 1.2"},
{:gettext, "~> 0.15"}, {:gettext, "~> 0.15"},
{:comeonin, "~> 4.1.1"}, {:comeonin, "~> 4.1.1"},
{:pbkdf2_elixir, "~> 0.12.3"}, {:pbkdf2_elixir, "~> 0.12.3"},

View file

@ -26,7 +26,7 @@
"decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"},
"ecto": {:hex, :ecto, "3.3.3", "0830bf3aebcbf3d8c1a1811cd581773b6866886c012f52c0f027031fa96a0b53", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "12e368e3c2a2938d7776defaabdae40e82900fc4d8d66120ec1e01dfd8b93c3a"}, "ecto": {:hex, :ecto, "3.4.0", "a7a83ab8359bf816ce729e5e65981ce25b9fc5adfc89c2ea3980f4fed0bfd7c1", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "5eed18252f5b5bbadec56a24112b531343507dbe046273133176b12190ce19cc"},
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"}, "ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
"ecto_sql": {:hex, :ecto_sql, "3.3.4", "aa18af12eb875fbcda2f75e608b3bd534ebf020fc4f6448e4672fcdcbb081244", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5eccbdbf92e3c6f213007a82d5dbba4cd9bb659d1a21331f89f408e4c0efd7a8"}, "ecto_sql": {:hex, :ecto_sql, "3.3.4", "aa18af12eb875fbcda2f75e608b3bd534ebf020fc4f6448e4672fcdcbb081244", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5eccbdbf92e3c6f213007a82d5dbba4cd9bb659d1a21331f89f408e4c0efd7a8"},
"esshd": {:hex, :esshd, "0.1.1", "d4dd4c46698093a40a56afecce8a46e246eb35463c457c246dacba2e056f31b5", [:mix], [], "hexpm", "d73e341e3009d390aa36387dc8862860bf9f874c94d9fd92ade2926376f49981"}, "esshd": {:hex, :esshd, "0.1.1", "d4dd4c46698093a40a56afecce8a46e246eb35463c457c246dacba2e056f31b5", [:mix], [], "hexpm", "d73e341e3009d390aa36387dc8862860bf9f874c94d9fd92ade2926376f49981"},
@ -55,7 +55,7 @@
"httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"}, "httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"},
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"}, "inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fdf843bca858203ae1de16da2ee206f53416bbda5dc8c9e78f43243de4bc3afe"}, "jason": {:hex, :jason, "1.2.0", "10043418c42d2493d0ee212d3fddd25d7ffe484380afad769a0a38795938e448", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "116747dbe057794c3a3e4e143b7c8390b29f634e16c78a7f59ba75bfa6852e7f"},
"joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"}, "joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"},
"jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"}, "jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"},
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"}, "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
@ -73,7 +73,7 @@
"myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]}, "myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"}, "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]}, "nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
"oban": {:hex, :oban, "0.12.1", "695e9490c6e0edfca616d80639528e448bd29b3bff7b7dd10a56c79b00a5d7fb", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c1d58d69b8b5a86e7167abbb8cc92764a66f25f12f6172052595067fc6a30a17"}, "oban": {:hex, :oban, "1.2.0", "7cca94d341be43d220571e28f69131c4afc21095b25257397f50973d3fc59b07", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ba5f8b3f7d76967b3e23cf8014f6a13e4ccb33431e4808f036709a7f822362ee"},
"open_api_spex": {:hex, :open_api_spex, "3.6.0", "64205aba9f2607f71b08fd43e3351b9c5e9898ec5ef49fc0ae35890da502ade9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "126ba3473966277132079cb1d5bf1e3df9e36fe2acd00166e75fd125cecb59c5"}, "open_api_spex": {:hex, :open_api_spex, "3.6.0", "64205aba9f2607f71b08fd43e3351b9c5e9898ec5ef49fc0ae35890da502ade9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "126ba3473966277132079cb1d5bf1e3df9e36fe2acd00166e75fd125cecb59c5"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.4", "8dd29ed783f2e12195d7e0a4640effc0a7c37e6537da491f1db01839eee6d053", [:mix], [], "hexpm", "595d09db74cb093b1903381c9de423276a931a2480a46a1a5dc7f932a2a6375b"}, "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.4", "8dd29ed783f2e12195d7e0a4640effc0a7c37e6537da491f1db01839eee6d053", [:mix], [], "hexpm", "595d09db74cb093b1903381c9de423276a931a2480a46a1a5dc7f932a2a6375b"},

View file

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

View file

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

View file

@ -0,0 +1,29 @@
defmodule Pleroma.Repo.Migrations.ChangeFollowingRelationshipsStateToInteger do
use Ecto.Migration
@alter_following_relationship_state "ALTER TABLE following_relationships ALTER COLUMN state"
def up do
execute("""
#{@alter_following_relationship_state} TYPE integer USING
CASE
WHEN state = 'pending' THEN 1
WHEN state = 'accept' THEN 2
WHEN state = 'reject' THEN 3
ELSE 0
END;
""")
end
def down do
execute("""
#{@alter_following_relationship_state} TYPE varchar(255) USING
CASE
WHEN state = 1 THEN 'pending'
WHEN state = 2 THEN 'accept'
WHEN state = 3 THEN 'reject'
ELSE ''
END;
""")
end
end

View file

@ -0,0 +1,11 @@
defmodule Pleroma.Repo.Migrations.AddFollowingRelationshipsFollowingIdIndex do
use Ecto.Migration
# [:follower_index] index is useless because of [:follower_id, :following_id] index
# [:following_id] index makes sense because of user's followers-targeted queries
def change do
drop_if_exists(index(:following_relationships, [:follower_id]))
create_if_not_exists(index(:following_relationships, [:following_id]))
end
end

View file

@ -0,0 +1,11 @@
defmodule Pleroma.Repo.Migrations.UpdateObanJobsTable do
use Ecto.Migration
def up do
Oban.Migrations.up(version: 8)
end
def down do
Oban.Migrations.down(version: 7)
end
end

View file

@ -15,28 +15,28 @@ defmodule Pleroma.FollowingRelationshipTest do
test "returns following addresses without internal.fetch" do test "returns following addresses without internal.fetch" do
user = insert(:user) user = insert(:user)
fetch_actor = InternalFetchActor.get_actor() fetch_actor = InternalFetchActor.get_actor()
FollowingRelationship.follow(fetch_actor, user, "accept") FollowingRelationship.follow(fetch_actor, user, :follow_accept)
assert FollowingRelationship.following(fetch_actor) == [user.follower_address] assert FollowingRelationship.following(fetch_actor) == [user.follower_address]
end end
test "returns following addresses without relay" do test "returns following addresses without relay" do
user = insert(:user) user = insert(:user)
relay_actor = Relay.get_actor() relay_actor = Relay.get_actor()
FollowingRelationship.follow(relay_actor, user, "accept") FollowingRelationship.follow(relay_actor, user, :follow_accept)
assert FollowingRelationship.following(relay_actor) == [user.follower_address] assert FollowingRelationship.following(relay_actor) == [user.follower_address]
end end
test "returns following addresses without remote user" do test "returns following addresses without remote user" do
user = insert(:user) user = insert(:user)
actor = insert(:user, local: false) actor = insert(:user, local: false)
FollowingRelationship.follow(actor, user, "accept") FollowingRelationship.follow(actor, user, :follow_accept)
assert FollowingRelationship.following(actor) == [user.follower_address] assert FollowingRelationship.following(actor) == [user.follower_address]
end end
test "returns following addresses with local user" do test "returns following addresses with local user" do
user = insert(:user) user = insert(:user)
actor = insert(:user, local: true) actor = insert(:user, local: true)
FollowingRelationship.follow(actor, user, "accept") FollowingRelationship.follow(actor, user, :follow_accept)
assert FollowingRelationship.following(actor) == [ assert FollowingRelationship.following(actor) == [
actor.follower_address, actor.follower_address,

View file

@ -140,7 +140,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do
test "user is unsubscribed" do test "user is unsubscribed" do
followed = insert(:user) followed = insert(:user)
user = insert(:user) user = insert(:user)
User.follow(user, followed, "accept") User.follow(user, followed, :follow_accept)
Mix.Tasks.Pleroma.User.run(["unsubscribe", user.nickname]) Mix.Tasks.Pleroma.User.run(["unsubscribe", user.nickname])

View file

@ -194,7 +194,8 @@ defmodule Pleroma.UserTest do
CommonAPI.follow(pending_follower, locked) CommonAPI.follow(pending_follower, locked)
CommonAPI.follow(pending_follower, locked) CommonAPI.follow(pending_follower, locked)
CommonAPI.follow(accepted_follower, locked) CommonAPI.follow(accepted_follower, locked)
Pleroma.FollowingRelationship.update(accepted_follower, locked, "accept")
Pleroma.FollowingRelationship.update(accepted_follower, locked, :follow_accept)
assert [^pending_follower] = User.get_follow_requests(locked) assert [^pending_follower] = User.get_follow_requests(locked)
end end
@ -319,7 +320,7 @@ defmodule Pleroma.UserTest do
following_address: "http://localhost:4001/users/fuser2/following" following_address: "http://localhost:4001/users/fuser2/following"
}) })
{:ok, user} = User.follow(user, followed, "accept") {:ok, user} = User.follow(user, followed, :follow_accept)
{:ok, user, _activity} = User.unfollow(user, followed) {:ok, user, _activity} = User.unfollow(user, followed)
@ -332,7 +333,7 @@ defmodule Pleroma.UserTest do
followed = insert(:user) followed = insert(:user)
user = insert(:user) user = insert(:user)
{:ok, user} = User.follow(user, followed, "accept") {:ok, user} = User.follow(user, followed, :follow_accept)
assert User.following(user) == [user.follower_address, followed.follower_address] assert User.following(user) == [user.follower_address, followed.follower_address]
@ -353,7 +354,7 @@ defmodule Pleroma.UserTest do
test "test if a user is following another user" do test "test if a user is following another user" do
followed = insert(:user) followed = insert(:user)
user = insert(:user) user = insert(:user)
User.follow(user, followed, "accept") User.follow(user, followed, :follow_accept)
assert User.following?(user, followed) assert User.following?(user, followed)
refute User.following?(followed, user) refute User.following?(followed, user)

View file

@ -1239,16 +1239,56 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
filename: "an_image.jpg" filename: "an_image.jpg"
} }
conn = object =
conn conn
|> assign(:user, user) |> assign(:user, user)
|> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
|> json_response(:created)
assert object = json_response(conn, :created)
assert object["name"] == desc assert object["name"] == desc
assert object["type"] == "Document" assert object["type"] == "Document"
assert object["actor"] == user.ap_id assert object["actor"] == user.ap_id
assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"]
assert is_binary(object_href)
assert object_mediatype == "image/jpeg"
activity_request = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"type" => "Create",
"object" => %{
"type" => "Note",
"content" => "AP C2S test, attachment",
"attachment" => [object]
},
"to" => "https://www.w3.org/ns/activitystreams#Public",
"cc" => []
}
activity_response =
conn
|> assign(:user, user)
|> post("/users/#{user.nickname}/outbox", activity_request)
|> json_response(:created)
assert activity_response["id"]
assert activity_response["object"]
assert activity_response["actor"] == user.ap_id
assert %Object{data: %{"attachment" => [attachment]}} =
Object.normalize(activity_response["object"])
assert attachment["type"] == "Document"
assert attachment["name"] == desc
assert [
%{
"href" => ^object_href,
"type" => "Link",
"mediaType" => ^object_mediatype
}
] = attachment["url"]
# Fails if unauthenticated
conn conn
|> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
|> json_response(403) |> json_response(403)

View file

@ -1230,19 +1230,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
attachment = %{ attachment = %{
"type" => "Link", "type" => "Link",
"mediaType" => "video/mp4", "mediaType" => "video/mp4",
"href" =>
"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
"mimeType" => "video/mp4",
"size" => 5_015_880,
"url" => [ "url" => [
%{ %{
"href" => "href" =>
"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
"mediaType" => "video/mp4", "mediaType" => "video/mp4"
"type" => "Link"
} }
], ]
"width" => 480
} }
assert object.data["url"] == assert object.data["url"] ==
@ -1624,7 +1618,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
}) })
user_two = insert(:user) user_two = insert(:user)
Pleroma.FollowingRelationship.follow(user_two, user, "accept") Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept)
{:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
{:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"}) {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
@ -2063,11 +2057,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
%{ %{
"mediaType" => "video/mp4", "mediaType" => "video/mp4",
"url" => [ "url" => [
%{ %{"href" => "https://peertube.moe/stat-480.mp4", "mediaType" => "video/mp4"}
"href" => "https://peertube.moe/stat-480.mp4",
"mediaType" => "video/mp4",
"type" => "Link"
}
] ]
} }
] ]
@ -2085,23 +2075,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
%{ %{
"mediaType" => "video/mp4", "mediaType" => "video/mp4",
"url" => [ "url" => [
%{ %{"href" => "https://pe.er/stat-480.mp4", "mediaType" => "video/mp4"}
"href" => "https://pe.er/stat-480.mp4",
"mediaType" => "video/mp4",
"type" => "Link"
}
] ]
}, },
%{ %{
"href" => "https://pe.er/stat-480.mp4",
"mediaType" => "video/mp4", "mediaType" => "video/mp4",
"mimeType" => "video/mp4",
"url" => [ "url" => [
%{ %{"href" => "https://pe.er/stat-480.mp4", "mediaType" => "video/mp4"}
"href" => "https://pe.er/stat-480.mp4",
"mediaType" => "video/mp4",
"type" => "Link"
}
] ]
} }
] ]

View file

@ -2273,13 +2273,17 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
value: :erlang.term_to_binary([]) value: :erlang.term_to_binary([])
) )
Pleroma.Config.TransferTask.load_and_update_env([], false)
assert Application.get_env(:logger, :backends) == []
conn = conn =
post(conn, "/api/pleroma/admin/config", %{ post(conn, "/api/pleroma/admin/config", %{
configs: [ configs: [
%{ %{
group: config.group, group: config.group,
key: config.key, key: config.key,
value: [":console", %{"tuple" => ["ExSyslogger", ":ex_syslogger"]}] value: [":console"]
} }
] ]
}) })
@ -2290,8 +2294,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"group" => ":logger", "group" => ":logger",
"key" => ":backends", "key" => ":backends",
"value" => [ "value" => [
":console", ":console"
%{"tuple" => ["ExSyslogger", ":ex_syslogger"]}
], ],
"db" => [":backends"] "db" => [":backends"]
} }
@ -2299,14 +2302,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
} }
assert Application.get_env(:logger, :backends) == [ assert Application.get_env(:logger, :backends) == [
:console, :console
{ExSyslogger, :ex_syslogger}
] ]
capture_log(fn ->
require Logger
Logger.warn("Ooops...")
end) =~ "Ooops..."
end end
test "saving full setting if value is not keyword", %{conn: conn} do test "saving full setting if value is not keyword", %{conn: conn} do

View file

@ -586,7 +586,7 @@ defmodule Pleroma.Web.CommonAPITest do
assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} = assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
CommonAPI.follow(follower, followed) CommonAPI.follow(follower, followed)
assert User.get_follow_state(follower, followed) == "pending" assert User.get_follow_state(follower, followed) == :follow_pending
assert {:ok, follower} = CommonAPI.unfollow(follower, followed) assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
assert User.get_follow_state(follower, followed) == nil assert User.get_follow_state(follower, followed) == nil
@ -608,7 +608,7 @@ defmodule Pleroma.Web.CommonAPITest do
assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} = assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
CommonAPI.follow(follower, followed) CommonAPI.follow(follower, followed)
assert User.get_follow_state(follower, followed) == "pending" assert User.get_follow_state(follower, followed) == :follow_pending
assert {:ok, follower} = CommonAPI.unfollow(follower, followed) assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
assert User.get_follow_state(follower, followed) == nil assert User.get_follow_state(follower, followed) == nil

View file

@ -6,20 +6,29 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ApiSpec
alias Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse
import Pleroma.Factory import Pleroma.Factory
import OpenApiSpex.TestAssertions
test "blocking / unblocking a domain" do test "blocking / unblocking a domain" do
%{user: user, conn: conn} = oauth_access(["write:blocks"]) %{user: user, conn: conn} = oauth_access(["write:blocks"])
other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
ret_conn = post(conn, "/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) ret_conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
assert %{} = json_response(ret_conn, 200) assert %{} = json_response(ret_conn, 200)
user = User.get_cached_by_ap_id(user.ap_id) user = User.get_cached_by_ap_id(user.ap_id)
assert User.blocks?(user, other_user) assert User.blocks?(user, other_user)
ret_conn = delete(conn, "/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) ret_conn =
conn
|> put_req_header("content-type", "application/json")
|> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
assert %{} = json_response(ret_conn, 200) assert %{} = json_response(ret_conn, 200)
user = User.get_cached_by_ap_id(user.ap_id) user = User.get_cached_by_ap_id(user.ap_id)
@ -41,5 +50,12 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do
assert "bad.site" in domain_blocks assert "bad.site" in domain_blocks
assert "even.worse.site" in domain_blocks assert "even.worse.site" in domain_blocks
assert_schema(domain_blocks, "DomainBlocksResponse", ApiSpec.spec())
end
test "DomainBlocksResponse example matches schema" do
api_spec = ApiSpec.spec()
schema = DomainBlocksResponse.schema()
assert_schema(schema.example, "DomainBlocksResponse", api_spec)
end end
end end

View file

@ -21,7 +21,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do
other_user = insert(:user) other_user = insert(:user)
{:ok, _activity} = ActivityPub.follow(other_user, user) {:ok, _activity} = ActivityPub.follow(other_user, user)
{:ok, other_user} = User.follow(other_user, user, "pending") {:ok, other_user} = User.follow(other_user, user, :follow_pending)
assert User.following?(other_user, user) == false assert User.following?(other_user, user) == false
@ -35,7 +35,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do
other_user = insert(:user) other_user = insert(:user)
{:ok, _activity} = ActivityPub.follow(other_user, user) {:ok, _activity} = ActivityPub.follow(other_user, user)
{:ok, other_user} = User.follow(other_user, user, "pending") {:ok, other_user} = User.follow(other_user, user, :follow_pending)
user = User.get_cached_by_id(user.id) user = User.get_cached_by_id(user.id)
other_user = User.get_cached_by_id(other_user.id) other_user = User.get_cached_by_id(other_user.id)

View file

@ -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)
@ -53,7 +73,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
assert response == expected_response assert response == expected_response
end end
test "dismissing a single notification" do test "dismissing a single notification (deprecated endpoint)" do
%{user: user, conn: conn} = oauth_access(["write:notifications"]) %{user: user, conn: conn} = oauth_access(["write:notifications"])
other_user = insert(:user) other_user = insert(:user)
@ -69,6 +89,22 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
assert %{} = json_response(conn, 200) assert %{} = json_response(conn, 200)
end end
test "dismissing a single notification" do
%{user: user, conn: conn} = oauth_access(["write:notifications"])
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
{:ok, [notification]} = Notification.create_notifications(activity)
conn =
conn
|> assign(:user, user)
|> post("/api/v1/notifications/#{notification.id}/dismiss")
assert %{} = json_response(conn, 200)
end
test "clearing all notifications" do test "clearing all notifications" do
%{user: user, conn: conn} = oauth_access(["write:notifications", "read:notifications"]) %{user: user, conn: conn} = oauth_access(["write:notifications", "read:notifications"])
other_user = insert(:user) other_user = insert(:user)

View file

@ -1047,6 +1047,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
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 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest 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

View file

@ -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 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest 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 [
%{ %{

View file

@ -169,6 +169,23 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
id_one = activity.id id_one = activity.id
id_two = activity_two.id id_two = activity_two.id
assert [%{"id" => ^id_one}, %{"id" => ^id_two}] = result assert [%{"id" => ^id_one}, %{"id" => ^id_two}] = result
{:ok, %{id: id_three}} =
CommonAPI.post(other_user, %{
"status" => "Bye!",
"in_reply_to_status_id" => activity.id,
"in_reply_to_conversation_id" => participation.id
})
assert [%{"id" => ^id_two}, %{"id" => ^id_three}] =
conn
|> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?limit=2")
|> json_response(:ok)
assert [%{"id" => ^id_three}] =
conn
|> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?min_id=#{id_two}")
|> json_response(:ok)
end end
test "PATCH /api/v1/pleroma/conversations/:id" do test "PATCH /api/v1/pleroma/conversations/:id" do

View file

@ -209,7 +209,7 @@ defmodule Pleroma.Web.StreamerTest do
Pleroma.Config.put([:instance, :skip_thread_containment], false) Pleroma.Config.put([:instance, :skip_thread_containment], false)
author = insert(:user) author = insert(:user)
user = insert(:user) user = insert(:user)
User.follow(user, author, "accept") User.follow(user, author, :follow_accept)
activity = activity =
insert(:note_activity, insert(:note_activity,
@ -232,7 +232,7 @@ defmodule Pleroma.Web.StreamerTest do
Pleroma.Config.put([:instance, :skip_thread_containment], true) Pleroma.Config.put([:instance, :skip_thread_containment], true)
author = insert(:user) author = insert(:user)
user = insert(:user) user = insert(:user)
User.follow(user, author, "accept") User.follow(user, author, :follow_accept)
activity = activity =
insert(:note_activity, insert(:note_activity,
@ -255,7 +255,7 @@ defmodule Pleroma.Web.StreamerTest do
Pleroma.Config.put([:instance, :skip_thread_containment], false) Pleroma.Config.put([:instance, :skip_thread_containment], false)
author = insert(:user) author = insert(:user)
user = insert(:user, skip_thread_containment: true) user = insert(:user, skip_thread_containment: true)
User.follow(user, author, "accept") User.follow(user, author, :follow_accept)
activity = activity =
insert(:note_activity, insert(:note_activity,