From 0f412cf6e68fcebda3e94b71b7f182af689748bf Mon Sep 17 00:00:00 2001
From: Maxim Filippov <colixer@gmail.com>
Date: Mon, 24 Dec 2018 02:25:36 +0300
Subject: [PATCH] Create tombstone instead of object deletion

---
 lib/pleroma/activity.ex                       | 20 ++++++++++++++-
 lib/pleroma/object.ex                         | 22 ++++++++++++++--
 lib/pleroma/web/common_api/utils.ex           |  7 +++++-
 test/activity_test.exs                        | 25 +++++++++++++++++++
 test/user_test.exs                            |  2 +-
 test/web/activity_pub/activity_pub_test.exs   |  2 +-
 test/web/activity_pub/transmogrifier_test.exs |  2 +-
 .../mastodon_api_controller_test.exs          |  2 +-
 8 files changed, 74 insertions(+), 8 deletions(-)

diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index 200addd6e..0845233ee 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -1,7 +1,7 @@
 defmodule Pleroma.Activity do
   use Ecto.Schema
   alias Pleroma.{Repo, Activity, Notification}
-  import Ecto.Query
+  import Ecto.{Query, Changeset}
 
   # https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19
   @mastodon_notification_types %{
@@ -103,4 +103,22 @@ defmodule Pleroma.Activity do
   end
 
   def mastodon_notification_type(%Activity{}), do: nil
+
+  def get_tombstone(%Activity{data: data}, deleted \\ DateTime.utc_now()) do
+    %{
+      id: data["id"],
+      context: data["context"],
+      type: "tombstone",
+      published: data["published"],
+      deleted: deleted
+    }
+  end
+
+  def swap_data_with_tombstone(activity) do
+    tombstone = get_tombstone(activity)
+
+    activity
+    |> change(%{data: tombstone})
+    |> Repo.update()
+  end
 end
diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex
index 31c8dd5bd..436cf6d5d 100644
--- a/lib/pleroma/object.ex
+++ b/lib/pleroma/object.ex
@@ -62,9 +62,27 @@ defmodule Pleroma.Object do
     Object.change(%Object{}, %{data: %{"id" => context}})
   end
 
+  def get_tombstone(%Object{data: data}, deleted \\ DateTime.utc_now()) do
+    %{
+      id: data["id"],
+      type: "tombstone",
+      deleted: deleted
+    }
+  end
+
+  def swap_data_with_tombstone(object) do
+    tombstone = get_tombstone(object)
+
+    object
+    |> Object.change(%{data: tombstone})
+    |> Repo.update()
+  end
+
   def delete(%Object{data: %{"id" => id}} = object) do
-    with Repo.delete(object),
-         Repo.delete_all(Activity.all_non_create_by_object_ap_id_q(id)),
+    with swap_data_with_tombstone(object),
+         Activity.all_non_create_by_object_ap_id_q(id)
+         |> Repo.all()
+         |> Enum.each(&Activity.swap_data_with_tombstone/1),
          {:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do
       {:ok, object}
     end
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 142283684..d25fef6bc 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -69,7 +69,12 @@ defmodule Pleroma.Web.CommonAPI.Utils do
     mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end)
 
     if inReplyTo do
-      {Enum.uniq([inReplyTo.data["actor"] | mentioned_users]), []}
+      to =
+        [inReplyTo.data["actor"] | mentioned_users]
+        |> Enum.uniq()
+        |> Enum.reject(&is_nil/1)
+
+      {to, []}
     else
       {mentioned_users, []}
     end
diff --git a/test/activity_test.exs b/test/activity_test.exs
index 55849c522..87c9ddc50 100644
--- a/test/activity_test.exs
+++ b/test/activity_test.exs
@@ -1,5 +1,6 @@
 defmodule Pleroma.ActivityTest do
   use Pleroma.DataCase
+  alias Pleroma.Activity
   import Pleroma.Factory
 
   test "returns an activity by it's AP id" do
@@ -24,4 +25,28 @@ defmodule Pleroma.ActivityTest do
 
     assert activity == found_activity
   end
+
+  test "returns tombstone" do
+    activity = insert(:note_activity)
+    deleted = DateTime.utc_now()
+
+    assert Pleroma.Activity.get_tombstone(activity, deleted) == %{
+             id: activity.data["object"]["id"],
+             context: activity.data["context"],
+             type: "tombstone",
+             published: activity.data["published"],
+             deleted: deleted
+           }
+  end
+
+  test "swaps data with tombstone" do
+    activity = insert(:note_activity)
+
+    {:ok, deleted} = Pleroma.Activity.swap_data_with_tombstone(activity)
+    assert deleted.data.type == "tombstone"
+
+    found_activity = Repo.get(Activity, activity.id)
+
+    assert deleted.data.type == found_activity.data["type"]
+  end
 end
diff --git a/test/user_test.exs b/test/user_test.exs
index b4d8174c6..43a3687ec 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -625,7 +625,7 @@ defmodule Pleroma.UserTest do
 
     # TODO: Remove favorites, repeats, delete activities.
 
-    refute Repo.get(Activity, activity.id)
+    assert Repo.get(Activity, activity.id).data["type"] == "tombstone"
   end
 
   test "get_public_key_for_ap_id fetches a user that's not in the db" do
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index 470ed08b2..95731a868 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -478,7 +478,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
 
       assert Repo.get(Activity, delete.id) != nil
 
-      assert Repo.get(Object, object.id) == nil
+      assert Repo.get(Object, object.id).data["type"] == "tombstone"
     end
   end
 
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
index 0428e052d..18a5ad3d0 100644
--- a/test/web/activity_pub/transmogrifier_test.exs
+++ b/test/web/activity_pub/transmogrifier_test.exs
@@ -363,7 +363,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
 
-      refute Repo.get(Activity, activity.id)
+      assert Repo.get(Activity, activity.id).data["type"] == "tombstone"
     end
 
     test "it fails for incoming deletes with spoofed origin" do
diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs
index aec0f851c..6c6cc2a00 100644
--- a/test/web/mastodon_api/mastodon_api_controller_test.exs
+++ b/test/web/mastodon_api/mastodon_api_controller_test.exs
@@ -292,7 +292,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
 
       assert %{} = json_response(conn, 200)
 
-      assert Repo.get(Activity, activity.id) == nil
+      assert Repo.get(Activity, activity.id).data["type"] == "tombstone"
     end
 
     test "when you didn't create it", %{conn: conn} do