@valid_visibilities ~w[direct unlisted public private]
- defp restrict_visibility(query, %{visibility: "direct"}) do
- public = "https://www.w3.org/ns/activitystreams#Public"
+ defp restrict_visibility(query, %{visibility: visibility})
+ when visibility in @valid_visibilities do
+ query =
+ from(
+ a in query,
+ where:
+ fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
+ )
- from(
- activity in query,
- join: sender in User,
- on: sender.ap_id == activity.actor,
- # Are non-direct statuses with no to/cc possible?
- where:
- fragment(
- "not (? && ?)",
- [^public, sender.follower_address],
- activity.recipients
- )
- )
+ Ecto.Adapters.SQL.to_sql(:all, Repo, query)
+
+ query
end
defp restrict_visibility(_query, %{visibility: visibility})
--- /dev/null
+defmodule Pleroma.Repo.Migrations.AddVisibilityFunction do
+ use Ecto.Migration
+
+ def up do
+ definition = """
+ create or replace function activity_visibility(actor varchar, recipients varchar[], data jsonb) returns varchar as $$
+ DECLARE
+ fa varchar;
+ public varchar := 'https://www.w3.org/ns/activitystreams#Public';
+ BEGIN
+ SELECT COALESCE(users.follower_address, '') into fa from users where users.ap_id = actor;
+
+ IF data->'to' ? public THEN
+ RETURN 'public';
+ ELSIF data->'cc' ? public THEN
+ RETURN 'unlisted';
+ ELSIF ARRAY[fa] && recipients THEN
+ RETURN 'private';
+ ELSIF not(ARRAY[fa, public] && recipients) THEN
+ RETURN 'direct';
+ ELSE
+ RETURN 'unknown';
+ END IF;
+ END;
+ $$ LANGUAGE plpgsql IMMUTABLE;
+ """
+
+ execute(definition)
+
+ create(
+ index(:activities, ["activity_visibility(actor, recipients, data)"],
+ name: :activities_visibility_index
+ )
+ )
+ end
+
+ def down do
+ drop(
+ index(:activities, ["activity_visibility(actor, recipients, data)"],
+ name: :activities_visibility_index
+ )
+ )
+
+ execute("drop function activity_visibility(actor varchar, recipients varchar[], data jsonb)")
+ end
+end
:ok
end
+ describe "fetching restricted by visibility" do
+ test "it restricts by the appropriate visibility" do
+ user = insert(:user)
+
+ {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
+
+ {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
+
+ {:ok, unlisted_activity} =
+ CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
+
+ {:ok, private_activity} =
+ CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
+
+ activities =
+ ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id})
+
+ assert activities == [direct_activity]
+
+ activities =
+ ActivityPub.fetch_activities([], %{:visibility => "unlisted", "actor_id" => user.ap_id})
+
+ assert activities == [unlisted_activity]
+
+ activities =
+ ActivityPub.fetch_activities([], %{:visibility => "private", "actor_id" => user.ap_id})
+
+ assert activities == [private_activity]
+
+ activities =
+ ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id})
+
+ assert activities == [public_activity]
+ end
+ end
+
describe "building a user from his ap id" do
test "it returns a user" do
user_id = "http://mastodon.example.org/users/admin"