Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into chat-federation-information

This commit is contained in:
lain 2020-07-06 11:27:06 +02:00
commit 74b88c0a8b
20 changed files with 107 additions and 23 deletions

View file

@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- **Breaking:** Emoji API: changed methods and renamed routes. - **Breaking:** Emoji API: changed methods and renamed routes.
- Streaming: Repeats of a user's posts will no longer be pushed to the user's stream. - Streaming: Repeats of a user's posts will no longer be pushed to the user's stream.
- Mastodon API: Added `pleroma.metadata.fields_limits` to /api/v1/instance - Mastodon API: Added `pleroma.metadata.fields_limits` to /api/v1/instance
- Mastodon API: On deletion, returns the original post text.
</details> </details>
<details> <details>

View file

@ -12,6 +12,11 @@ defmodule Pleroma.Config.Loader do
:swarm :swarm
] ]
@reject_groups [
:postgrex,
:tesla
]
if Code.ensure_loaded?(Config.Reader) do if Code.ensure_loaded?(Config.Reader) do
@reader Config.Reader @reader Config.Reader
@ -47,7 +52,8 @@ defp filter(configs) do
@spec filter_group(atom(), keyword()) :: keyword() @spec filter_group(atom(), keyword()) :: keyword()
def filter_group(group, configs) do def filter_group(group, configs) do
Enum.reject(configs[group], fn {key, _v} -> Enum.reject(configs[group], fn {key, _v} ->
key in @reject_keys or (group == :phoenix and key == :serve_endpoints) or group == :postgrex key in @reject_keys or group in @reject_groups or
(group == :phoenix and key == :serve_endpoints)
end) end)
end end
end end

View file

@ -3,7 +3,6 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.MigrationHelper.NotificationBackfill do defmodule Pleroma.MigrationHelper.NotificationBackfill do
alias Pleroma.Notification
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
@ -25,18 +24,27 @@ def fill_in_notification_types do
|> type_from_activity() |> type_from_activity()
notification notification
|> Notification.changeset(%{type: type}) |> Ecto.Changeset.change(%{type: type})
|> Repo.update() |> Repo.update()
end) end)
end end
defp get_by_ap_id(ap_id) do
q =
from(u in User,
select: u.id
)
Repo.get_by(q, ap_id: ap_id)
end
# This is copied over from Notifications to keep this stable. # This is copied over from Notifications to keep this stable.
defp type_from_activity(%{data: %{"type" => type}} = activity) do defp type_from_activity(%{data: %{"type" => type}} = activity) do
case type do case type do
"Follow" -> "Follow" ->
accepted_function = fn activity -> accepted_function = fn activity ->
with %User{} = follower <- User.get_by_ap_id(activity.data["actor"]), with %User{} = follower <- get_by_ap_id(activity.data["actor"]),
%User{} = followed <- User.get_by_ap_id(activity.data["object"]) do %User{} = followed <- get_by_ap_id(activity.data["object"]) do
Pleroma.FollowingRelationship.following?(follower, followed) Pleroma.FollowingRelationship.following?(follower, followed)
end end
end end

View file

@ -52,6 +52,7 @@ defp search_query(query_string, for_user, following) do
|> base_query(following) |> base_query(following)
|> filter_blocked_user(for_user) |> filter_blocked_user(for_user)
|> filter_invisible_users() |> filter_invisible_users()
|> filter_internal_users()
|> filter_blocked_domains(for_user) |> filter_blocked_domains(for_user)
|> fts_search(query_string) |> fts_search(query_string)
|> trigram_rank(query_string) |> trigram_rank(query_string)
@ -109,6 +110,10 @@ defp filter_invisible_users(query) do
from(q in query, where: q.invisible == false) from(q in query, where: q.invisible == false)
end end
defp filter_internal_users(query) do
from(q in query, where: q.actor_type != "Application")
end
defp filter_blocked_user(query, %User{} = blocker) do defp filter_blocked_user(query, %User{} = blocker) do
query query
|> join(:left, [u], b in Pleroma.UserRelationship, |> join(:left, [u], b in Pleroma.UserRelationship,

View file

@ -84,7 +84,7 @@ def delete_operation do
operationId: "StatusController.delete", operationId: "StatusController.delete",
parameters: [id_param()], parameters: [id_param()],
responses: %{ responses: %{
200 => empty_object_response(), 200 => status_response(),
403 => Operation.response("Forbidden", "application/json", ApiError), 403 => Operation.response("Forbidden", "application/json", ApiError),
404 => Operation.response("Not Found", "application/json", ApiError) 404 => Operation.response("Not Found", "application/json", ApiError)
} }

View file

@ -62,6 +62,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
} }
}, },
content: %Schema{type: :string, format: :html, description: "HTML-encoded status content"}, content: %Schema{type: :string, format: :html, description: "HTML-encoded status content"},
text: %Schema{
type: :string,
description: "Original unformatted content in plain text",
nullable: true
},
created_at: %Schema{ created_at: %Schema{
type: :string, type: :string,
format: "date-time", format: "date-time",

View file

@ -186,6 +186,7 @@ defp object(draft) do
draft.poll draft.poll
) )
|> Map.put("emoji", emoji) |> Map.put("emoji", emoji)
|> Map.put("source", draft.status)
%__MODULE__{draft | object: object} %__MODULE__{draft | object: object}
end end

View file

@ -44,6 +44,7 @@ def search2(conn, params), do: do_search(:v2, conn, params)
def search(conn, params), do: do_search(:v1, conn, params) def search(conn, params), do: do_search(:v1, conn, params)
defp do_search(version, %{assigns: %{user: user}} = conn, %{q: query} = params) do defp do_search(version, %{assigns: %{user: user}} = conn, %{q: query} = params) do
query = String.trim(query)
options = search_options(params, user) options = search_options(params, user)
timeout = Keyword.get(Repo.config(), :timeout, 15_000) timeout = Keyword.get(Repo.config(), :timeout, 15_000)
default_values = %{"statuses" => [], "accounts" => [], "hashtags" => []} default_values = %{"statuses" => [], "accounts" => [], "hashtags" => []}

View file

@ -200,11 +200,18 @@ def show(%{assigns: %{user: user}} = conn, %{id: id}) do
@doc "DELETE /api/v1/statuses/:id" @doc "DELETE /api/v1/statuses/:id"
def delete(%{assigns: %{user: user}} = conn, %{id: id}) do def delete(%{assigns: %{user: user}} = conn, %{id: id}) do
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do with %Activity{} = activity <- Activity.get_by_id_with_object(id),
json(conn, %{}) render <-
try_render(conn, "show.json",
activity: activity,
for: user,
with_direct_conversation_id: true,
with_source: true
),
{:ok, %Activity{}} <- CommonAPI.delete(id, user) do
render
else else
{:error, :not_found} = e -> e _e -> {:error, :not_found}
_e -> render_error(conn, :forbidden, "Can't delete this post")
end end
end end

View file

@ -333,6 +333,7 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
reblog: nil, reblog: nil,
card: card, card: card,
content: content_html, content: content_html,
text: opts[:with_source] && object.data["source"],
created_at: created_at, created_at: created_at,
reblogs_count: announcement_count, reblogs_count: announcement_count,
replies_count: object.data["repliesCount"] || 0, replies_count: object.data["repliesCount"] || 0,

View file

@ -104,7 +104,9 @@ def stream(topics, items) do
:ok :ok
end end
def filtered_by_user?(%User{} = user, %Activity{} = item) do def filtered_by_user?(user, item, streamed_type \\ :activity)
def filtered_by_user?(%User{} = user, %Activity{} = item, streamed_type) do
%{block: blocked_ap_ids, mute: muted_ap_ids, reblog_mute: reblog_muted_ap_ids} = %{block: blocked_ap_ids, mute: muted_ap_ids, reblog_mute: reblog_muted_ap_ids} =
User.outgoing_relationships_ap_ids(user, [:block, :mute, :reblog_mute]) User.outgoing_relationships_ap_ids(user, [:block, :mute, :reblog_mute])
@ -116,7 +118,9 @@ def filtered_by_user?(%User{} = user, %Activity{} = item) do
true <- true <-
Enum.all?([blocked_ap_ids, muted_ap_ids], &(item.actor not in &1)), Enum.all?([blocked_ap_ids, muted_ap_ids], &(item.actor not in &1)),
true <- item.data["type"] != "Announce" || item.actor not in reblog_muted_ap_ids, true <- item.data["type"] != "Announce" || item.actor not in reblog_muted_ap_ids,
true <- !(item.data["type"] == "Announce" && parent.data["actor"] == user.ap_id), true <-
!(streamed_type == :activity && item.data["type"] == "Announce" &&
parent.data["actor"] == user.ap_id),
true <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(parent.data["actor"] not in &1)), true <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(parent.data["actor"] not in &1)),
true <- MapSet.disjoint?(recipients, recipient_blocks), true <- MapSet.disjoint?(recipients, recipient_blocks),
%{host: item_host} <- URI.parse(item.actor), %{host: item_host} <- URI.parse(item.actor),
@ -131,8 +135,8 @@ def filtered_by_user?(%User{} = user, %Activity{} = item) do
end end
end end
def filtered_by_user?(%User{} = user, %Notification{activity: activity}) do def filtered_by_user?(%User{} = user, %Notification{activity: activity}, _) do
filtered_by_user?(user, activity) filtered_by_user?(user, activity, :notification)
end end
defp do_stream("direct", item) do defp do_stream("direct", item) do

View file

@ -0,0 +1,10 @@
defmodule Pleroma.Repo.Migrations.RemoveTeslaFromConfig do
use Ecto.Migration
def up do
execute("DELETE FROM config WHERE config.group = ':tesla'")
end
def down do
end
end

View file

@ -10,7 +10,6 @@ defmodule Pleroma.Config.HolderTest do
test "default_config/0" do test "default_config/0" do
config = Holder.default_config() config = Holder.default_config()
assert config[:pleroma][Pleroma.Uploaders.Local][:uploads] == "test/uploads" assert config[:pleroma][Pleroma.Uploaders.Local][:uploads] == "test/uploads"
assert config[:tesla][:adapter] == Tesla.Mock
refute config[:pleroma][Pleroma.Repo] refute config[:pleroma][Pleroma.Repo]
refute config[:pleroma][Pleroma.Web.Endpoint] refute config[:pleroma][Pleroma.Web.Endpoint]
@ -18,17 +17,15 @@ test "default_config/0" do
refute config[:pleroma][:configurable_from_database] refute config[:pleroma][:configurable_from_database]
refute config[:pleroma][:database] refute config[:pleroma][:database]
refute config[:phoenix][:serve_endpoints] refute config[:phoenix][:serve_endpoints]
refute config[:tesla][:adapter]
end end
test "default_config/1" do test "default_config/1" do
pleroma_config = Holder.default_config(:pleroma) pleroma_config = Holder.default_config(:pleroma)
assert pleroma_config[Pleroma.Uploaders.Local][:uploads] == "test/uploads" assert pleroma_config[Pleroma.Uploaders.Local][:uploads] == "test/uploads"
tesla_config = Holder.default_config(:tesla)
assert tesla_config[:adapter] == Tesla.Mock
end end
test "default_config/2" do test "default_config/2" do
assert Holder.default_config(:pleroma, Pleroma.Uploaders.Local) == [uploads: "test/uploads"] assert Holder.default_config(:pleroma, Pleroma.Uploaders.Local) == [uploads: "test/uploads"]
assert Holder.default_config(:tesla, :adapter) == Tesla.Mock
end end
end end

View file

@ -67,6 +67,7 @@ def note_factory(attrs \\ %{}) do
data = %{ data = %{
"type" => "Note", "type" => "Note",
"content" => text, "content" => text,
"source" => text,
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(), "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
"actor" => user.ap_id, "actor" => user.ap_id,
"to" => ["https://www.w3.org/ns/activitystreams#Public"], "to" => ["https://www.w3.org/ns/activitystreams#Public"],

View file

@ -17,7 +17,7 @@ defmodule Pleroma.UserSearchTest do
describe "User.search" do describe "User.search" do
setup do: clear_config([:instance, :limit_to_local_content]) setup do: clear_config([:instance, :limit_to_local_content])
test "excluded invisible users from results" do test "excludes invisible users from results" do
user = insert(:user, %{nickname: "john t1000"}) user = insert(:user, %{nickname: "john t1000"})
insert(:user, %{invisible: true, nickname: "john t800"}) insert(:user, %{invisible: true, nickname: "john t800"})
@ -25,6 +25,15 @@ test "excluded invisible users from results" do
assert found_user.id == user.id assert found_user.id == user.id
end end
test "excludes service actors from results" do
insert(:user, actor_type: "Application", nickname: "user1")
service = insert(:user, actor_type: "Service", nickname: "user2")
person = insert(:user, actor_type: "Person", nickname: "user3")
assert [found_user1, found_user2] = User.search("user")
assert [found_user1.id, found_user2.id] -- [service.id, person.id] == []
end
test "accepts limit parameter" do test "accepts limit parameter" do
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"})) Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
assert length(User.search("john", limit: 3)) == 3 assert length(User.search("john", limit: 3)) == 3

View file

@ -491,6 +491,7 @@ test "it filters out obviously bad tags when accepting a post as HTML" do
object = Object.normalize(activity) object = Object.normalize(activity)
assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)" assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
assert object.data["source"] == post
end end
test "it filters out obviously bad tags when accepting a post as Markdown" do test "it filters out obviously bad tags when accepting a post as Markdown" do
@ -507,6 +508,7 @@ test "it filters out obviously bad tags when accepting a post as Markdown" do
object = Object.normalize(activity) object = Object.normalize(activity)
assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)" assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
assert object.data["source"] == post
end end
test "it does not allow replies to direct messages that are not direct messages themselves" do test "it does not allow replies to direct messages that are not direct messages themselves" do

View file

@ -79,6 +79,7 @@ test "search", %{conn: conn} do
assert status["id"] == to_string(activity.id) assert status["id"] == to_string(activity.id)
end end
@tag capture_log: true
test "constructs hashtags from search query", %{conn: conn} do test "constructs hashtags from search query", %{conn: conn} do
results = results =
conn conn
@ -318,11 +319,13 @@ test "search doesn't show statuses that it shouldn't", %{conn: conn} do
test "search fetches remote accounts", %{conn: conn} do test "search fetches remote accounts", %{conn: conn} do
user = insert(:user) user = insert(:user)
query = URI.encode_query(%{q: " mike@osada.macgirvin.com ", resolve: true})
results = results =
conn conn
|> assign(:user, user) |> assign(:user, user)
|> assign(:token, insert(:oauth_token, user: user, scopes: ["read"])) |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"]))
|> get("/api/v1/search?q=mike@osada.macgirvin.com&resolve=true") |> get("/api/v1/search?#{query}")
|> json_response_and_validate_schema(200) |> json_response_and_validate_schema(200)
[account] = results["accounts"] [account] = results["accounts"]

View file

@ -760,13 +760,18 @@ test "if user is authenticated", %{local: local, remote: remote} do
test "when you created it" do test "when you created it" do
%{user: author, conn: conn} = oauth_access(["write:statuses"]) %{user: author, conn: conn} = oauth_access(["write:statuses"])
activity = insert(:note_activity, user: author) activity = insert(:note_activity, user: author)
object = Object.normalize(activity)
conn = content = object.data["content"]
source = object.data["source"]
result =
conn conn
|> assign(:user, author) |> assign(:user, author)
|> delete("/api/v1/statuses/#{activity.id}") |> delete("/api/v1/statuses/#{activity.id}")
|> json_response_and_validate_schema(200)
assert %{} = json_response_and_validate_schema(conn, 200) assert match?(%{"content" => ^content, "text" => ^source}, result)
refute Activity.get_by_id(activity.id) refute Activity.get_by_id(activity.id)
end end
@ -789,7 +794,7 @@ test "when you didn't create it" do
conn = delete(conn, "/api/v1/statuses/#{activity.id}") conn = delete(conn, "/api/v1/statuses/#{activity.id}")
assert %{"error" => _} = json_response_and_validate_schema(conn, 403) assert %{"error" => "Record not found"} == json_response_and_validate_schema(conn, 404)
assert Activity.get_by_id(activity.id) == activity assert Activity.get_by_id(activity.id) == activity
end end

View file

@ -183,6 +183,7 @@ test "a note activity" do
card: nil, card: nil,
reblog: nil, reblog: nil,
content: HTML.filter_tags(object_data["content"]), content: HTML.filter_tags(object_data["content"]),
text: nil,
created_at: created_at, created_at: created_at,
reblogs_count: 0, reblogs_count: 0,
replies_count: 0, replies_count: 0,

View file

@ -128,6 +128,23 @@ test "it does not stream announces of the user's own posts in the 'user' stream"
assert Streamer.filtered_by_user?(user, announce) assert Streamer.filtered_by_user?(user, announce)
end end
test "it does stream notifications announces of the user's own posts in the 'user' stream", %{
user: user
} do
Streamer.get_topic_and_add_socket("user", user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
{:ok, announce} = CommonAPI.repeat(activity.id, other_user)
notification =
Pleroma.Notification
|> Repo.get_by(%{user_id: user.id, activity_id: announce.id})
|> Repo.preload(:activity)
refute Streamer.filtered_by_user?(user, notification)
end
test "it streams boosts of mastodon user in the 'user' stream", %{user: user} do test "it streams boosts of mastodon user in the 'user' stream", %{user: user} do
Streamer.get_topic_and_add_socket("user", user) Streamer.get_topic_and_add_socket("user", user)