From 8acdafd26044e50a4d6ee451bf24ad66003c5bb3 Mon Sep 17 00:00:00 2001
From: Yassine Doghri <yassine@doghri.fr>
Date: Thu, 21 Jul 2022 13:53:29 +0000
Subject: [PATCH] fix(episode-unpublish): set consistent posts_counts'
 increments/decrements for actors and episodes

Some episodes could not be unpublished because of out of range error when removing posts.

fixes #233
---
 app/Config/Events.php                         | 32 +++++---------
 app/Controllers/PostController.php            |  6 ++-
 app/Models/PostModel.php                      |  1 -
 .../Admin/Controllers/EpisodeController.php   |  2 +
 modules/Fediverse/Models/PostModel.php        | 44 +++++++++----------
 5 files changed, 40 insertions(+), 45 deletions(-)

diff --git a/app/Config/Events.php b/app/Config/Events.php
index f8567024b6..a36233f8be 100644
--- a/app/Config/Events.php
+++ b/app/Config/Events.php
@@ -122,23 +122,9 @@ Events::on('on_undo_follow', function ($actor, $targetActor): void {
  * @param Post $post
  */
 Events::on('on_post_add', function ($post): void {
-    $isReply = $post->in_reply_to_id !== null;
-
-    if ($isReply) {
-        $post = $post->reply_to_post;
-    }
-
-    if ($post->episode_id !== null) {
-        if ($isReply) {
-            model(EpisodeModel::class, false)->builder()
-                ->where('id', $post->episode_id)
-                ->increment('comments_count');
-        } else {
-            model(EpisodeModel::class, false)->builder()
-                ->where('id', $post->episode_id)
-                ->increment('posts_count');
-        }
-    }
+    model(EpisodeModel::class, false)->builder()
+        ->where('id', $post->episode_id)
+        ->increment('posts_count');
 
     if ($post->actor->is_podcast) {
         // Removing all of the podcast pages is a bit overkill, but works to avoid caching bugs
@@ -154,10 +140,6 @@ Events::on('on_post_add', function ($post): void {
  * @param Post $post
  */
 Events::on('on_post_remove', function ($post): void {
-    if ($post->in_reply_to_id !== null) {
-        Events::trigger('on_post_remove', $post->reply_to_post);
-    }
-
     if ($episodeId = $post->episode_id) {
         model(EpisodeModel::class, false)->builder()
             ->where('id', $episodeId)
@@ -237,6 +219,10 @@ Events::on('on_post_undo_reblog', function ($reblogPost): void {
 Events::on('on_post_reply', function ($reply): void {
     $post = $reply->reply_to_post;
 
+    model(EpisodeModel::class, false)->builder()
+        ->where('id', $post->episode_id)
+        ->increment('comments_count');
+
     if ($post->actor->is_podcast) {
         cache()
             ->deleteMatching("podcast#{$post->actor->podcast->id}*");
@@ -254,6 +240,10 @@ Events::on('on_post_reply', function ($reply): void {
 Events::on('on_reply_remove', function ($reply): void {
     $post = $reply->reply_to_post;
 
+    model(EpisodeModel::class, false)->builder()
+        ->where('id', $post->episode_id)
+        ->decrement('comments_count');
+
     if ($post->actor->is_podcast) {
         cache()
             ->deleteMatching("page_podcast#{$post->actor->podcast->id}*");
diff --git a/app/Controllers/PostController.php b/app/Controllers/PostController.php
index 78510e23a2..6e6f033e69 100644
--- a/app/Controllers/PostController.php
+++ b/app/Controllers/PostController.php
@@ -74,6 +74,10 @@ class PostController extends FediversePostController
             $this->registerPodcastWebpageHit($this->podcast->id);
         }
 
+        if ($this->post === null) {
+            throw PageNotFoundException::forPageNotFound();
+        }
+
         $cacheName = implode(
             '_',
             array_filter([
@@ -177,7 +181,7 @@ class PostController extends FediversePostController
             'created_by' => user_id(),
         ]);
 
-        if ($this->post->in_reply_to_id === null && $this->post->episode_id !== null) {
+        if ($this->post->episode_id !== null) {
             $newPost->episode_id = $this->post->episode_id;
         }
 
diff --git a/app/Models/PostModel.php b/app/Models/PostModel.php
index f100a85368..6f8df615cd 100644
--- a/app/Models/PostModel.php
+++ b/app/Models/PostModel.php
@@ -62,7 +62,6 @@ class PostModel extends FediversePostModel
             ->join(config('Fediverse')->tablesPrefix . 'posts as p2', 'p1.id = p2.in_reply_to_id')
             ->select('p2.id, p1.episode_id')
             ->where([
-                'p1.in_reply_to_id' => null,
                 'p2.in_reply_to_id IS NOT' => null,
                 'p2.episode_id' => null,
                 'p1.episode_id IS NOT' => null,
diff --git a/modules/Admin/Controllers/EpisodeController.php b/modules/Admin/Controllers/EpisodeController.php
index 18c961c2ad..86684bad46 100644
--- a/modules/Admin/Controllers/EpisodeController.php
+++ b/modules/Admin/Controllers/EpisodeController.php
@@ -714,6 +714,8 @@ class EpisodeController extends BaseController
         $allPostsLinkedToEpisode = (new PostModel())
             ->where([
                 'episode_id' => $this->episode->id,
+                'in_reply_to_id' => null,
+                'reblog_of_id' => null,
             ])
             ->findAll();
         foreach ($allPostsLinkedToEpisode as $post) {
diff --git a/modules/Fediverse/Models/PostModel.php b/modules/Fediverse/Models/PostModel.php
index e25e6c6298..94f2130c9d 100644
--- a/modules/Fediverse/Models/PostModel.php
+++ b/modules/Fediverse/Models/PostModel.php
@@ -277,10 +277,12 @@ class PostModel extends BaseUuidModel
         }
 
         if ($post->in_reply_to_id === null) {
-            // increment posts_count only if not reply
+            // post is not a reply
             model('ActorModel', false)
                 ->where('id', $post->actor_id)
                 ->increment('posts_count');
+
+            Events::trigger('on_post_add', $post);
         }
 
         if ($registerActivity) {
@@ -314,8 +316,6 @@ class PostModel extends BaseUuidModel
                 ]);
         }
 
-        Events::trigger('on_post_add', $post);
-
         $this->clearCache($post);
 
         $this->db->transComplete();
@@ -365,26 +365,13 @@ class PostModel extends BaseUuidModel
     {
         $this->db->transStart();
 
-        model('ActorModel', false)
-            ->where('id', $post->actor_id)
-            ->decrement('posts_count');
-
-        if ($post->in_reply_to_id !== null) {
-            // Post to remove is a reply
-            model('PostModel', false)
-                ->where('id', $this->uuid->fromString($post->in_reply_to_id) ->getBytes())
-                ->decrement('replies_count');
-
-            Events::trigger('on_reply_remove', $post);
-        }
-
         // remove all post reblogs
         foreach ($post->reblogs as $reblog) {
             // FIXME: issue when actor is not local, can't get actor information
-            $this->removePost($reblog);
+            $this->undoReblog($reblog);
         }
 
-        // remove all post replies
+        // remove all replies
         foreach ($post->replies as $reply) {
             $this->removePost($reply);
         }
@@ -427,11 +414,24 @@ class PostModel extends BaseUuidModel
                 ]);
         }
 
+        if ($post->in_reply_to_id === null && $post->reblog_of_id === null) {
+            model('ActorModel', false)
+                ->where('id', $post->actor_id)
+                ->decrement('posts_count');
+
+            Events::trigger('on_post_remove', $post);
+        } elseif ($post->in_reply_to_id !== null) {
+            // Post to remove is a reply
+            model('PostModel', false)
+                ->where('id', $this->uuid->fromString($post->in_reply_to_id) ->getBytes())
+                ->decrement('replies_count');
+
+            Events::trigger('on_reply_remove', $post);
+        }
+
         $result = model('PostModel', false)
             ->delete($post->id);
 
-        Events::trigger('on_post_remove', $post);
-
         $this->clearCache($post);
 
         $this->db->transComplete();
@@ -574,11 +574,11 @@ class PostModel extends BaseUuidModel
                 ]);
         }
 
+        Events::trigger('on_post_undo_reblog', $reblogPost);
+
         $result = model('PostModel', false)
             ->delete($reblogPost->id);
 
-        Events::trigger('on_post_undo_reblog', $reblogPost);
-
         $this->clearCache($reblogPost);
 
         $this->db->transComplete();
-- 
GitLab