diff --git a/config/config.exs b/config/config.exs index e58454d68..d34589370 100644 --- a/config/config.exs +++ b/config/config.exs @@ -250,7 +250,6 @@ config :pleroma, :instance, safe_dm_mentions: false, healthcheck: false, remote_post_retention_days: 90, - skip_thread_containment: true, limit_to_local_content: :unauthenticated, dynamic_configuration: false, user_bio_length: 5000, @@ -259,7 +258,8 @@ config :pleroma, :instance, max_remote_account_fields: 20, account_field_name_length: 512, account_field_value_length: 512, - external_user_synchronization: true + external_user_synchronization: true, + default_reply_visibility: "public" config :pleroma, :markup, # XXX - unfortunately, inline images must be enabled by default right now, because diff --git a/docs/config.md b/docs/config.md index 414b54660..8406d75bb 100644 --- a/docs/config.md +++ b/docs/config.md @@ -129,7 +129,6 @@ config :pleroma, Pleroma.Emails.Mailer, * `remote_post_retention_days`: The default amount of days to retain remote posts when pruning the database. * `user_bio_length`: A user bio maximum length (default: `5000`) * `user_name_length`: A user name maximum length (default: `100`) -* `skip_thread_containment`: Skip filter out broken threads. The default is `false`. * `limit_to_local_content`: Limit unauthenticated users to search for local statutes and users only. Possible values: `:unauthenticated`, `:all` and `false`. The default is `:unauthenticated`. * `dynamic_configuration`: Allow transferring configuration to DB with the subsequent customization from Admin api. * `max_account_fields`: The maximum number of custom fields in the user profile (default: `10`) @@ -137,7 +136,11 @@ config :pleroma, Pleroma.Emails.Mailer, * `account_field_name_length`: An account field name maximum length (default: `512`) * `account_field_value_length`: An account field value maximum length (default: `512`) * `external_user_synchronization`: Enabling following/followers counters synchronization for external users. - +* `default_reply_visibility`: The default reply visibility filter: + * "none": show all replies in timelines (like GNU Social) + * "public": show public replies in timelines (default) + * "following": show replies involving only people the user follows (like Mastodon) + * "self": show replies only from the user ## :logger diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index dba9560ab..3df377e30 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -527,7 +527,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> maybe_preload_bookmarks(opts) |> maybe_set_thread_muted_field(opts) |> restrict_blocked(opts) - |> restrict_recipients(recipients, opts["user"]) + |> restrict_recipients(recipients, opts) |> where( [activity], fragment( @@ -696,13 +696,62 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_tag(query, _), do: query - defp restrict_recipients(query, [], _user), do: query - - defp restrict_recipients(query, recipients, nil) do - from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients)) + defp get_friend_ap_ids(%User{} = user) do + from(u in User.get_friends_query(user), select: u.ap_id) + |> Repo.all() end - defp restrict_recipients(query, recipients, user) do + defp restrict_recipients(query, [], _opts), do: query + + defp restrict_recipients(query, recipients, %{ + "user" => %User{} = user, + "reply_visibility" => visibility + }) + when visibility in ["self", "following"] do + reply_recipients = + case visibility do + "self" -> [user.ap_id] + "following" -> [user.ap_id] ++ get_friend_ap_ids(user) + end + + from( + [activity, object] in query, + where: + fragment( + "? && ? and (?->>'inReplyTo' is null or ? <@ ?)", + ^recipients, + activity.recipients, + object.data, + activity.recipient_users, + ^reply_recipients + ) + ) + end + + defp restrict_recipients(query, recipients, %{ + "user" => %User{} = user, + "reply_visibility" => "public" + }) do + reply_recipients = [user.ap_id] ++ get_friend_ap_ids(user) + public_recipients = [Pleroma.Constants.as_public()] + + from( + [activity, object] in query, + where: + fragment( + "? && ? and (?->>'inReplyTo' is null or ? && ? or ? <@ ?)", + ^recipients, + activity.recipients, + object.data, + activity.recipients, + ^public_recipients, + activity.recipient_users, + ^reply_recipients + ) + ) + end + + defp restrict_recipients(query, recipients, %{"user" => %User{} = user}) do from( activity in query, where: fragment("? && ?", ^recipients, activity.recipients), @@ -710,6 +759,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ) end + defp restrict_recipients(query, recipients, _opts) do + from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients)) + end + defp restrict_local(query, %{"local_only" => true}) do from(activity in query, where: activity.local == true) end @@ -907,12 +960,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp maybe_order(query, _), do: query def fetch_activities_query(recipients, opts \\ %{}) do + opts = + if !Map.has_key?(opts, "user") do + opts + else + default_vis = Pleroma.Config.get([:instance, :default_reply_visibility]) + Map.put_new(opts, "reply_visibility", default_vis) + end + Activity |> maybe_preload_objects(opts) |> maybe_preload_bookmarks(opts) |> maybe_set_thread_muted_field(opts) |> maybe_order(opts) - |> restrict_recipients(recipients, opts["user"]) + |> restrict_recipients(recipients, opts) |> restrict_tag(opts) |> restrict_tag_reject(opts) |> restrict_tag_all(opts)