Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into emoji-reaction-extensions
This commit is contained in:
commit
7d8b709d29
|
@ -44,6 +44,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Mastodon API, streaming: Add `pleroma.direct_conversation_id` to the `conversation` stream event payload.
|
- Mastodon API, streaming: Add `pleroma.direct_conversation_id` to the `conversation` stream event payload.
|
||||||
- Admin API: Render whole status in grouped reports
|
- Admin API: Render whole status in grouped reports
|
||||||
- Mastodon API: User timelines will now respect blocks, unless you are getting the user timeline of somebody you blocked (which would be empty otherwise).
|
- Mastodon API: User timelines will now respect blocks, unless you are getting the user timeline of somebody you blocked (which would be empty otherwise).
|
||||||
|
- Mastodon API: Favoriting / Repeating a post multiple times will now return the identical response every time. Before, executing that action twice would return an error ("already favorited") on the second try.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -20,7 +20,7 @@ def filter(%{"type" => message_type} = message) do
|
||||||
with accepted_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :accept]),
|
with accepted_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :accept]),
|
||||||
rejected_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :reject]),
|
rejected_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :reject]),
|
||||||
true <-
|
true <-
|
||||||
length(accepted_vocabulary) == 0 || Enum.member?(accepted_vocabulary, message_type),
|
Enum.empty?(accepted_vocabulary) || Enum.member?(accepted_vocabulary, message_type),
|
||||||
false <-
|
false <-
|
||||||
length(rejected_vocabulary) > 0 && Enum.member?(rejected_vocabulary, message_type),
|
length(rejected_vocabulary) > 0 && Enum.member?(rejected_vocabulary, message_type),
|
||||||
{:ok, _} <- filter(message["object"]) do
|
{:ok, _} <- filter(message["object"]) do
|
||||||
|
|
|
@ -639,7 +639,7 @@ def get_password_reset(conn, %{"nickname" => nickname}) do
|
||||||
def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||||
users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
|
users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
|
||||||
|
|
||||||
Enum.map(users, &User.force_password_reset_async/1)
|
Enum.each(users, &User.force_password_reset_async/1)
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
actor: admin,
|
actor: admin,
|
||||||
|
|
|
@ -85,9 +85,13 @@ def delete(activity_id, user) do
|
||||||
def repeat(id_or_ap_id, user, params \\ %{}) do
|
def repeat(id_or_ap_id, user, params \\ %{}) do
|
||||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
object <- Object.normalize(activity),
|
object <- Object.normalize(activity),
|
||||||
nil <- Utils.get_existing_announce(user.ap_id, object),
|
announce_activity <- Utils.get_existing_announce(user.ap_id, object),
|
||||||
public <- public_announce?(object, params) do
|
public <- public_announce?(object, params) do
|
||||||
ActivityPub.announce(user, object, nil, true, public)
|
if announce_activity do
|
||||||
|
{:ok, announce_activity, object}
|
||||||
|
else
|
||||||
|
ActivityPub.announce(user, object, nil, true, public)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
_ -> {:error, dgettext("errors", "Could not repeat")}
|
_ -> {:error, dgettext("errors", "Could not repeat")}
|
||||||
end
|
end
|
||||||
|
@ -105,8 +109,12 @@ def unrepeat(id_or_ap_id, user) do
|
||||||
def favorite(id_or_ap_id, user) do
|
def favorite(id_or_ap_id, user) do
|
||||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
object <- Object.normalize(activity),
|
object <- Object.normalize(activity),
|
||||||
nil <- Utils.get_existing_like(user.ap_id, object) do
|
like_activity <- Utils.get_existing_like(user.ap_id, object) do
|
||||||
ActivityPub.like(user, object)
|
if like_activity do
|
||||||
|
{:ok, like_activity, object}
|
||||||
|
else
|
||||||
|
ActivityPub.like(user, object)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
_ -> {:error, dgettext("errors", "Could not favorite")}
|
_ -> {:error, dgettext("errors", "Could not favorite")}
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,9 +6,9 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
|
||||||
@moduledoc "The module represents functions to manage user subscriptions."
|
@moduledoc "The module represents functions to manage user subscriptions."
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View
|
||||||
alias Pleroma.Web.Push
|
alias Pleroma.Web.Push
|
||||||
alias Pleroma.Web.Push.Subscription
|
alias Pleroma.Web.Push.Subscription
|
||||||
alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View
|
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
|
|
|
@ -14,10 +14,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
||||||
alias Pleroma.Web.ControllerHelper
|
alias Pleroma.Web.ControllerHelper
|
||||||
alias Pleroma.Web.OAuth.App
|
alias Pleroma.Web.OAuth.App
|
||||||
alias Pleroma.Web.OAuth.Authorization
|
alias Pleroma.Web.OAuth.Authorization
|
||||||
|
alias Pleroma.Web.OAuth.Scopes
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
alias Pleroma.Web.OAuth.Token.Strategy.RefreshToken
|
alias Pleroma.Web.OAuth.Token.Strategy.RefreshToken
|
||||||
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
|
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
|
||||||
alias Pleroma.Web.OAuth.Scopes
|
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
|
2
mix.exs
2
mix.exs
|
@ -124,7 +124,7 @@ defp deps do
|
||||||
{:earmark, "~> 1.3"},
|
{:earmark, "~> 1.3"},
|
||||||
{:bbcode, "~> 0.1.1"},
|
{:bbcode, "~> 0.1.1"},
|
||||||
{:ex_machina, "~> 2.3", only: :test},
|
{:ex_machina, "~> 2.3", only: :test},
|
||||||
{:credo, "~> 0.9.3", only: [:dev, :test]},
|
{:credo, "~> 1.1.0", only: [:dev, :test], runtime: false},
|
||||||
{:mock, "~> 0.3.3", only: :test},
|
{:mock, "~> 0.3.3", only: :test},
|
||||||
{:crypt,
|
{:crypt,
|
||||||
git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"},
|
git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"},
|
||||||
|
|
2
mix.lock
2
mix.lock
|
@ -16,7 +16,7 @@
|
||||||
"cors_plug": {:hex, :cors_plug, "1.5.2", "72df63c87e4f94112f458ce9d25800900cc88608c1078f0e4faddf20933eda6e", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
"cors_plug": {:hex, :cors_plug, "1.5.2", "72df63c87e4f94112f458ce9d25800900cc88608c1078f0e4faddf20933eda6e", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
|
"cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm"},
|
"cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm"},
|
||||||
"credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
|
"credo": {:hex, :credo, "1.1.5", "caec7a3cadd2e58609d7ee25b3931b129e739e070539ad1a0cd7efeeb47014f4", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
"crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
"crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},
|
"crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},
|
||||||
"custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm"},
|
"custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm"},
|
||||||
|
|
|
@ -745,7 +745,7 @@ test "it doesn't return notifications from a blocked user when with_muted is set
|
||||||
|
|
||||||
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
|
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
assert length(Notification.for_user(user, %{with_muted: true})) == 0
|
assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't return notifications from a domain-blocked user when with_muted is set" do
|
test "it doesn't return notifications from a domain-blocked user when with_muted is set" do
|
||||||
|
@ -755,7 +755,7 @@ test "it doesn't return notifications from a domain-blocked user when with_muted
|
||||||
|
|
||||||
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
|
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
assert length(Notification.for_user(user, %{with_muted: true})) == 0
|
assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns notifications from muted threads when with_muted is set" do
|
test "it returns notifications from muted threads when with_muted is set" do
|
||||||
|
|
|
@ -78,7 +78,7 @@ test "with locked accounts, it does not create a follow or an accept" do
|
||||||
)
|
)
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|
|
||||||
assert length(accepts) == 0
|
assert Enum.empty?(accepts)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it works for follow requests when you are already followed, creating a new accept activity" do
|
test "it works for follow requests when you are already followed, creating a new accept activity" do
|
||||||
|
|
|
@ -1363,9 +1363,9 @@ test "returns 404 when report id is invalid", %{conn: conn} do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "requires write:reports scope", %{conn: conn, id: id, admin: admin} do
|
test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
|
||||||
read_token = insert(:oauth_token, user: admin, scopes: ["read"])
|
read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
|
||||||
write_token = insert(:oauth_token, user: admin, scopes: ["write:reports"])
|
write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
|
||||||
|
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|
@ -1376,7 +1376,7 @@ test "requires write:reports scope", %{conn: conn, id: id, admin: admin} do
|
||||||
|> json_response(403)
|
|> json_response(403)
|
||||||
|
|
||||||
assert response == %{
|
assert response == %{
|
||||||
"error" => "Insufficient permissions: admin:write:reports | write:reports."
|
"error" => "Insufficient permissions: admin:write:reports."
|
||||||
}
|
}
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -2864,7 +2864,7 @@ test "GET /instances/:instance/statuses", %{conn: conn} do
|
||||||
|
|
||||||
response = json_response(ret_conn, 200)
|
response = json_response(ret_conn, 200)
|
||||||
|
|
||||||
assert length(response) == 0
|
assert Enum.empty?(response)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -284,22 +284,22 @@ test "favoriting a status" do
|
||||||
{:ok, %Activity{}, _} = CommonAPI.favorite(activity.id, user)
|
{:ok, %Activity{}, _} = CommonAPI.favorite(activity.id, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "retweeting a status twice returns an error" do
|
test "retweeting a status twice returns the status" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
|
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
|
||||||
{:ok, %Activity{}, _object} = CommonAPI.repeat(activity.id, user)
|
{:ok, %Activity{} = activity, object} = CommonAPI.repeat(activity.id, user)
|
||||||
{:error, _} = CommonAPI.repeat(activity.id, user)
|
{:ok, ^activity, ^object} = CommonAPI.repeat(activity.id, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "favoriting a status twice returns an error" do
|
test "favoriting a status twice returns the status" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
|
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
|
||||||
{:ok, %Activity{}, _object} = CommonAPI.favorite(activity.id, user)
|
{:ok, %Activity{} = activity, object} = CommonAPI.favorite(activity.id, user)
|
||||||
{:error, _} = CommonAPI.favorite(activity.id, user)
|
{:ok, ^activity, ^object} = CommonAPI.favorite(activity.id, user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -307,7 +307,7 @@ test "for private posts, not a reply" do
|
||||||
|
|
||||||
{to, cc} = Utils.get_to_and_cc(user, mentions, nil, "private", nil)
|
{to, cc} = Utils.get_to_and_cc(user, mentions, nil, "private", nil)
|
||||||
assert length(to) == 2
|
assert length(to) == 2
|
||||||
assert length(cc) == 0
|
assert Enum.empty?(cc)
|
||||||
|
|
||||||
assert mentioned_user.ap_id in to
|
assert mentioned_user.ap_id in to
|
||||||
assert user.follower_address in to
|
assert user.follower_address in to
|
||||||
|
@ -323,7 +323,7 @@ test "for private posts, a reply" do
|
||||||
{to, cc} = Utils.get_to_and_cc(user, mentions, activity, "private", nil)
|
{to, cc} = Utils.get_to_and_cc(user, mentions, activity, "private", nil)
|
||||||
|
|
||||||
assert length(to) == 3
|
assert length(to) == 3
|
||||||
assert length(cc) == 0
|
assert Enum.empty?(cc)
|
||||||
|
|
||||||
assert mentioned_user.ap_id in to
|
assert mentioned_user.ap_id in to
|
||||||
assert third_user.ap_id in to
|
assert third_user.ap_id in to
|
||||||
|
@ -338,7 +338,7 @@ test "for direct posts, not a reply" do
|
||||||
{to, cc} = Utils.get_to_and_cc(user, mentions, nil, "direct", nil)
|
{to, cc} = Utils.get_to_and_cc(user, mentions, nil, "direct", nil)
|
||||||
|
|
||||||
assert length(to) == 1
|
assert length(to) == 1
|
||||||
assert length(cc) == 0
|
assert Enum.empty?(cc)
|
||||||
|
|
||||||
assert mentioned_user.ap_id in to
|
assert mentioned_user.ap_id in to
|
||||||
end
|
end
|
||||||
|
@ -353,7 +353,7 @@ test "for direct posts, a reply" do
|
||||||
{to, cc} = Utils.get_to_and_cc(user, mentions, activity, "direct", nil)
|
{to, cc} = Utils.get_to_and_cc(user, mentions, activity, "direct", nil)
|
||||||
|
|
||||||
assert length(to) == 2
|
assert length(to) == 2
|
||||||
assert length(cc) == 0
|
assert Enum.empty?(cc)
|
||||||
|
|
||||||
assert mentioned_user.ap_id in to
|
assert mentioned_user.ap_id in to
|
||||||
assert third_user.ap_id in to
|
assert third_user.ap_id in to
|
||||||
|
|
|
@ -638,6 +638,13 @@ test "favs a status and returns it", %{conn: conn} do
|
||||||
assert to_string(activity.id) == id
|
assert to_string(activity.id) == id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "favoriting twice will just return 200", %{conn: conn} do
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
|
post(conn, "/api/v1/statuses/#{activity.id}/favourite")
|
||||||
|
assert post(conn, "/api/v1/statuses/#{activity.id}/favourite") |> json_response(200)
|
||||||
|
end
|
||||||
|
|
||||||
test "returns 400 error for a wrong id", %{conn: conn} do
|
test "returns 400 error for a wrong id", %{conn: conn} do
|
||||||
conn = post(conn, "/api/v1/statuses/1/favourite")
|
conn = post(conn, "/api/v1/statuses/1/favourite")
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ test "it returns HTTP 200", %{conn: conn} do
|
||||||
|
|
||||||
user = refresh_record(user)
|
user = refresh_record(user)
|
||||||
assert Comeonin.Pbkdf2.checkpw("test", user.password_hash)
|
assert Comeonin.Pbkdf2.checkpw("test", user.password_hash)
|
||||||
assert length(Token.get_user_tokens(user)) == 0
|
assert Enum.empty?(Token.get_user_tokens(user))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it sets password_reset_pending to false", %{conn: conn} do
|
test "it sets password_reset_pending to false", %{conn: conn} do
|
||||||
|
|
Loading…
Reference in New Issue