aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/mix/tasks/pleroma/database.ex2
-rw-r--r--lib/pleroma/activity.ex2
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex2
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex31
-rw-r--r--priv/repo/migrations/20200527163635_delete_notifications_from_invisible_users.exs2
-rw-r--r--priv/repo/migrations/20200914105638_delete_notification_without_activity.exs2
-rw-r--r--priv/repo/migrations/20211218181647_add_activity_fields_to_objects.exs138
-rw-r--r--test/pleroma/activity_test.exs23
-rw-r--r--test/pleroma/user_test.exs20
-rw-r--r--test/pleroma/web/activity_pub/activity_pub_controller_test.exs2
-rw-r--r--test/pleroma/web/activity_pub/activity_pub_test.exs7
-rw-r--r--test/pleroma/web/admin_api/controllers/report_controller_test.exs2
12 files changed, 195 insertions, 38 deletions
diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex
index a973beaa9..41a36851e 100644
--- a/lib/mix/tasks/pleroma/database.ex
+++ b/lib/mix/tasks/pleroma/database.ex
@@ -33,7 +33,7 @@ defmodule Mix.Tasks.Pleroma.Database do
Logger.info("Removing embedded objects")
Repo.query!(
- "update activities set data = safe_jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;",
+ "update objects set data = safe_jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;",
[],
timeout: :infinity
)
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index 4106feef6..30d8f7092 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -26,7 +26,7 @@ defmodule Pleroma.Activity do
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
- schema "activities" do
+ schema "objects" do
field(:data, :map)
field(:local, :boolean, default: true)
field(:actor, :string)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 9f51a3062..3b7ff0554 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -1161,7 +1161,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_instance(query, %{instance: instance}) when is_binary(instance) do
from(
activity in query,
- where: fragment("split_part(actor::text, '/'::text, 3) = ?", ^instance)
+ where: fragment("split_part(?::text, '/'::text, 3) = ?", activity.actor, ^instance)
)
end
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index c1f6b2b49..d5f0a3245 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -712,9 +712,10 @@ defmodule Pleroma.Web.ActivityPub.Utils do
defp build_flag_object(%{statuses: statuses}) do
Enum.map(statuses || [], &build_flag_object/1)
+ |> Enum.reject(&is_nil/1)
end
- defp build_flag_object(%Activity{data: %{"id" => id}, object: %{data: data}}) do
+ defp build_flag_object(%Activity{data: %{"id" => id, "type" => "Create"}, object: %{data: data}}) do
activity_actor = User.get_by_ap_id(data["actor"])
%{
@@ -730,28 +731,26 @@ defmodule Pleroma.Web.ActivityPub.Utils do
}
end
- defp build_flag_object(act) when is_map(act) or is_binary(act) do
- id =
- case act do
- %Activity{} = act -> act.data["id"]
- act when is_map(act) -> act["id"]
- act when is_binary(act) -> act
- end
+ defp build_flag_object(%{data: %{"id" => id}}), do: build_flag_object(id)
+ defp build_flag_object(%{"id" => id}), do: build_flag_object(id)
- case Activity.get_by_ap_id_with_object(id) do
- %Activity{} = activity ->
+ defp build_flag_object(ap_id) when is_binary(ap_id) do
+ case Activity.get_by_ap_id_with_object(ap_id) do
+ %Activity{data: %{"type" => "Create"}} = activity ->
build_flag_object(activity)
- nil ->
- if activity = Activity.get_by_object_ap_id_with_object(id) do
- build_flag_object(activity)
- else
- %{"id" => id, "deleted" => true}
+ _ ->
+ case Activity.get_by_object_ap_id_with_object(ap_id) do
+ %Activity{data: %{"type" => "Create"}} = activity ->
+ build_flag_object(activity)
+
+ _ ->
+ %{"id" => ap_id, "deleted" => true}
end
end
end
- defp build_flag_object(_), do: []
+ defp build_flag_object(_), do: nil
#### Report-related helpers
def get_reports(params, page, page_size) do
diff --git a/priv/repo/migrations/20200527163635_delete_notifications_from_invisible_users.exs b/priv/repo/migrations/20200527163635_delete_notifications_from_invisible_users.exs
index 9e95a8111..a2010e188 100644
--- a/priv/repo/migrations/20200527163635_delete_notifications_from_invisible_users.exs
+++ b/priv/repo/migrations/20200527163635_delete_notifications_from_invisible_users.exs
@@ -6,7 +6,7 @@ defmodule Pleroma.Repo.Migrations.DeleteNotificationsFromInvisibleUsers do
def up do
Pleroma.Notification
- |> join(:inner, [n], activity in assoc(n, :activity))
+ |> join(:inner, [n], activity in "activities")
|> where(
[n, a],
fragment("? in (SELECT ap_id FROM users WHERE invisible = true)", a.actor)
diff --git a/priv/repo/migrations/20200914105638_delete_notification_without_activity.exs b/priv/repo/migrations/20200914105638_delete_notification_without_activity.exs
index 9333fc5a1..252eb0716 100644
--- a/priv/repo/migrations/20200914105638_delete_notification_without_activity.exs
+++ b/priv/repo/migrations/20200914105638_delete_notification_without_activity.exs
@@ -7,7 +7,7 @@ defmodule Pleroma.Repo.Migrations.DeleteNotificationWithoutActivity do
def up do
from(
q in Pleroma.Notification,
- left_join: c in assoc(q, :activity),
+ left_join: c in "activities",
select: %{id: type(q.id, :integer)},
where: is_nil(c.id)
)
diff --git a/priv/repo/migrations/20211218181647_add_activity_fields_to_objects.exs b/priv/repo/migrations/20211218181647_add_activity_fields_to_objects.exs
new file mode 100644
index 000000000..d53e514f7
--- /dev/null
+++ b/priv/repo/migrations/20211218181647_add_activity_fields_to_objects.exs
@@ -0,0 +1,138 @@
+defmodule Pleroma.Repo.Migrations.AddActivityFieldsToObjects do
+ use Ecto.Migration
+
+ @function_name "update_status_visibility_counter_cache"
+ @trigger_name "status_visibility_counter_cache_trigger"
+
+ def up do
+ alter table(:objects) do
+ add(:local, :boolean)
+ add(:actor, :string)
+ add(:recipients, {:array, :string})
+ end
+
+ create_if_not_exists(index(:objects, [:local]))
+ create_if_not_exists(index(:objects, [:actor, "id DESC NULLS LAST"]))
+ create_if_not_exists(index(:objects, [:recipients], using: :gin))
+
+ create_if_not_exists(
+ index(:objects, ["(data->'to')"], name: :activities_to_index, using: :gin)
+ )
+
+ create_if_not_exists(
+ index(:objects, ["(data->'cc')"], name: :activities_cc_index, using: :gin)
+ )
+
+ # Copy all activities into the newly formatted objects table
+ execute("INSERT INTO objects (SELECT * FROM activities)")
+
+ # Update notifications foreign key
+ execute("alter table notifications drop constraint notifications_activity_id_fkey")
+
+ execute(
+ "alter table notifications add constraint notifications_object_id_fkey foreign key (activity_id) references objects(id) on delete cascade"
+ )
+
+ # Update bookmarks foreign key
+ execute("alter table bookmarks drop constraint bookmarks_activity_id_fkey")
+
+ execute(
+ "alter table bookmarks add constraint bookmarks_object_id_fkey foreign key (activity_id) references objects(id) on delete cascade"
+ )
+
+ # Update report notes foreign key
+ execute("alter table report_notes drop constraint report_notes_activity_id_fkey")
+
+ execute(
+ "alter table report_notes add constraint report_notes_object_id_fkey foreign key (activity_id) references objects(id)"
+ )
+
+ # Nuke the old activities table
+ execute("drop table activities")
+
+ # Update triggers
+ """
+ CREATE TRIGGER #{@trigger_name}
+ BEFORE
+ INSERT
+ OR UPDATE of recipients, data
+ OR DELETE
+ ON objects
+ FOR EACH ROW
+ EXECUTE PROCEDURE #{@function_name}();
+ """
+ |> execute()
+
+ execute("drop function if exists thread_visibility(actor varchar, activity_id varchar)")
+ execute(update_thread_visibility())
+ end
+
+ def down do
+ raise "Lol, there's no going back from this."
+ end
+
+ # It acts upon objects instead of activities now
+ def update_thread_visibility do
+ """
+ CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, object_id varchar) RETURNS boolean AS $$
+ DECLARE
+ public varchar := 'https://www.w3.org/ns/activitystreams#Public';
+ child objects%ROWTYPE;
+ object objects%ROWTYPE;
+ author_fa varchar;
+ valid_recipients varchar[];
+ actor_user_following varchar[];
+ BEGIN
+ --- Fetch actor following
+ SELECT array_agg(following.follower_address) INTO actor_user_following FROM following_relationships
+ JOIN users ON users.id = following_relationships.follower_id
+ JOIN users AS following ON following.id = following_relationships.following_id
+ WHERE users.ap_id = actor;
+
+ --- Fetch our initial object.
+ SELECT * INTO object FROM objects WHERE objects.data->>'id' = object_id;
+
+ LOOP
+ --- Ensure that we have an object before continuing.
+ --- If we don't, the thread is not satisfiable.
+ IF object IS NULL THEN
+ RETURN false;
+ END IF;
+
+ --- We only care about Create objects.
+ IF object.data->>'type' != 'Create' THEN
+ RETURN true;
+ END IF;
+
+ --- Normalize the child object into child.
+ SELECT * INTO child FROM objects
+ WHERE COALESCE(object.data->'object'->>'id', object.data->>'object') = objects.data->>'id';
+
+ --- Fetch the author's AS2 following collection.
+ SELECT COALESCE(users.follower_address, '') INTO author_fa FROM users WHERE users.ap_id = object.actor;
+
+ --- Prepare valid recipients array.
+ valid_recipients := ARRAY[actor, public];
+ IF ARRAY[author_fa] && actor_user_following THEN
+ valid_recipients := valid_recipients || author_fa;
+ END IF;
+
+ --- Check visibility.
+ IF NOT valid_recipients && object.recipients THEN
+ --- object not visible, break out of the loop
+ RETURN false;
+ END IF;
+
+ --- If there's a parent, load it and do this all over again.
+ IF (child.data->'inReplyTo' IS NOT NULL) AND (child.data->'inReplyTo' != 'null'::jsonb) THEN
+ SELECT * INTO object FROM objects
+ WHERE child.data->>'inReplyTo' = objects.data->>'id';
+ ELSE
+ RETURN true;
+ END IF;
+ END LOOP;
+ END;
+ $$ LANGUAGE plpgsql IMMUTABLE;
+ """
+ end
+end
diff --git a/test/pleroma/activity_test.exs b/test/pleroma/activity_test.exs
index 4f9144f91..7d0e427fa 100644
--- a/test/pleroma/activity_test.exs
+++ b/test/pleroma/activity_test.exs
@@ -51,7 +51,8 @@ defmodule Pleroma.ActivityTest do
{:ok, bookmark3} = Bookmark.create(user3.id, activity.id)
queried_activity =
- Ecto.Query.from(Pleroma.Activity)
+ Activity
+ |> Ecto.Query.where(id: ^activity.id)
|> Activity.with_preloaded_bookmark(user3)
|> Repo.one()
@@ -64,17 +65,19 @@ defmodule Pleroma.ActivityTest do
annoyed_user = insert(:user)
{:ok, _} = ThreadMute.add_mute(annoyed_user.id, activity.data["context"])
+ query = Ecto.Query.where(Activity, id: ^activity.id)
+
activity_with_unset_thread_muted_field =
- Ecto.Query.from(Activity)
+ query
|> Repo.one()
activity_for_user =
- Ecto.Query.from(Activity)
+ query
|> Activity.with_set_thread_muted_field(user)
|> Repo.one()
activity_for_annoyed_user =
- Ecto.Query.from(Activity)
+ query
|> Activity.with_set_thread_muted_field(annoyed_user)
|> Repo.one()
@@ -90,7 +93,7 @@ defmodule Pleroma.ActivityTest do
{:ok, bookmark} = Bookmark.create(user.id, activity.id)
queried_activity =
- Ecto.Query.from(Pleroma.Activity)
+ Ecto.Query.where(Activity, id: ^activity.id)
|> Activity.with_preloaded_bookmark(user)
|> Repo.one()
@@ -103,7 +106,7 @@ defmodule Pleroma.ActivityTest do
{:ok, bookmark} = Bookmark.create(user.id, activity.id)
queried_activity =
- Ecto.Query.from(Pleroma.Activity)
+ Ecto.Query.where(Activity, id: ^activity.id)
|> Repo.one()
assert Activity.get_bookmark(queried_activity, user) == bookmark
@@ -266,7 +269,11 @@ defmodule Pleroma.ActivityTest do
insert(:add_activity, user: user, note: note)
insert(:add_activity, user: user)
- assert Repo.aggregate(Activity, :count, :id) == 4
+ activities_query =
+ Activity
+ |> Ecto.Query.where(fragment("data->>'type' IN ('Create', 'Add')"))
+
+ assert Repo.aggregate(activities_query, :count, :id) == 4
add_query =
Activity.add_by_params_query(note.data["object"], user.ap_id, user.featured_address)
@@ -276,6 +283,6 @@ defmodule Pleroma.ActivityTest do
Repo.delete_all(add_query)
assert Repo.aggregate(add_query, :count, :id) == 0
- assert Repo.aggregate(Activity, :count, :id) == 2
+ assert Repo.aggregate(activities_query, :count, :id) == 2
end
end
diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs
index 6cd93c34c..607d81470 100644
--- a/test/pleroma/user_test.exs
+++ b/test/pleroma/user_test.exs
@@ -19,6 +19,8 @@ defmodule Pleroma.UserTest do
import ExUnit.CaptureLog
import Swoosh.TestAssertions
+ require Ecto.Query
+
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
@@ -436,7 +438,11 @@ defmodule Pleroma.UserTest do
{:ok, registered_user} = User.register(cng)
ObanHelpers.perform_all()
- activity = Repo.one(Pleroma.Activity)
+ activity =
+ Activity
+ |> Ecto.Query.where(fragment("data->>'type' = 'Create'"))
+ |> Repo.one()
+
assert registered_user.ap_id in activity.recipients
assert Object.normalize(activity, fetch: false).data["content"] =~ "direct message"
assert activity.actor == welcome_user.ap_id
@@ -452,7 +458,11 @@ defmodule Pleroma.UserTest do
{:ok, registered_user} = User.register(cng)
ObanHelpers.perform_all()
- activity = Repo.one(Pleroma.Activity)
+ activity =
+ Activity
+ |> Ecto.Query.where(fragment("data->>'type' = 'Create'"))
+ |> Repo.one()
+
assert registered_user.ap_id in activity.recipients
assert Object.normalize(activity, fetch: false).data["content"] =~ "chat message"
assert activity.actor == welcome_user.ap_id
@@ -491,7 +501,11 @@ defmodule Pleroma.UserTest do
{:ok, registered_user} = User.register(cng)
ObanHelpers.perform_all()
- activity = Repo.one(Pleroma.Activity)
+ activity =
+ Activity
+ |> Ecto.Query.where(fragment("data->>'type' = 'Create'"))
+ |> Repo.one()
+
assert registered_user.ap_id in activity.recipients
assert Object.normalize(activity, fetch: false).data["content"] =~ "chat message"
assert activity.actor == welcome_user.ap_id
diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
index 50315e21f..30ea6be50 100644
--- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
+++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
@@ -1143,7 +1143,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
- assert Pleroma.Repo.aggregate(Activity, :count, :id) == 2
+ assert Pleroma.Repo.aggregate(Object, :count, :id) == 4
ObanHelpers.perform_all()
diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs
index 574ef0d71..7313ed42e 100644
--- a/test/pleroma/web/activity_pub/activity_pub_test.exs
+++ b/test/pleroma/web/activity_pub/activity_pub_test.exs
@@ -1394,8 +1394,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{user: user1})
|> Enum.map(fn a -> a.id end)
- assert [public_activity.id, private_activity_1.id] == activities
- assert length(activities) == 2
+ assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
+ assert length(activities) == 3
end
end
@@ -1514,8 +1514,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
content: content
})
- assert Repo.aggregate(Activity, :count, :id) == 1
- assert Repo.aggregate(Object, :count, :id) == 2
+ assert Repo.aggregate(Object, :count, :id) == 3
assert Repo.aggregate(Notification, :count, :id) == 0
end
end
diff --git a/test/pleroma/web/admin_api/controllers/report_controller_test.exs b/test/pleroma/web/admin_api/controllers/report_controller_test.exs
index 99cc7bbd0..e480a0118 100644
--- a/test/pleroma/web/admin_api/controllers/report_controller_test.exs
+++ b/test/pleroma/web/admin_api/controllers/report_controller_test.exs
@@ -363,7 +363,7 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
[note, _] = notes
assert note["user"]["nickname"] == admin.nickname
- assert note["content"] == "this is disgusting!"
+ assert note["content"] =~ "this is disgusting"
assert note["created_at"]
assert response["total"] == 1
end