FEATURE: allow admin to delete all posts by a user irrespectively (#14128)

FEATURE: allow admin to delete all posts by a user irrespectively (#14128)

This commit allows admin to delete all posts by a user irrespective of site settings delete_user_max_post_age and delete_all_posts_max.

diff --git a/app/assets/javascripts/admin/addon/controllers/admin-user-index.js b/app/assets/javascripts/admin/addon/controllers/admin-user-index.js
index b9492e3..93ed9c3 100644
--- a/app/assets/javascripts/admin/addon/controllers/admin-user-index.js
+++ b/app/assets/javascripts/admin/addon/controllers/admin-user-index.js
@@ -11,7 +11,7 @@ import discourseComputed from "discourse-common/utils/decorators";
 import getURL from "discourse-common/lib/get-url";
 import { htmlSafe } from "@ember/template";
 import { iconHTML } from "discourse-common/lib/icon-library";
-import { popupAjaxError } from "discourse/lib/ajax-error";
+import { extractError, popupAjaxError } from "discourse/lib/ajax-error";
 import { inject as service } from "@ember/service";
 import showModal from "discourse/lib/show-modal";
 
@@ -272,73 +272,6 @@ export default Controller.extend(CanCheckEmails, {
     silence() {
       return this.model.silence();
     },
-    deleteAllPosts() {
-      let deletedPosts = 0;
-      let deletedPercentage = 0;
-      const user = this.model;
-      const message = I18n.messageFormat(
-        "admin.user.delete_all_posts_confirm_MF",
-        {
-          POSTS: user.get("post_count"),
-          TOPICS: user.get("topic_count"),
-        }
-      );
-
-      const performDelete = (progressModal) => {
-        this.model
-          .deleteAllPosts()
-          .then(({ posts_deleted }) => {
-            if (posts_deleted === 0) {
-              user.set("post_count", 0);
-              progressModal.send("closeModal");
-            } else {
-              deletedPosts += posts_deleted;
-              deletedPercentage = Math.floor(
-                (deletedPosts * 100) / user.get("post_count")
-              );
-              progressModal.setProperties({
-                deletedPercentage: deletedPercentage,
-              });
-              performDelete(progressModal);
-            }
-          })
-          .catch((e) => {
-            progressModal.send("closeModal");
-            let error;
-            AdminUser.find(user.get("id")).then((u) => user.setProperties(u));
-            if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) {
-              error = e.jqXHR.responseJSON.errors[0];
-            }
-            error = error || I18n.t("admin.user.delete_posts_failed");
-            bootbox.alert(error);
-          });
-      };
-
-      const buttons = [
-        {
-          label: I18n.t("composer.cancel"),
-          class: "d-modal-cancel",
-          link: true,
-        },
-        {
-          icon: iconHTML("exclamation-triangle"),
-          label: I18n.t("admin.user.delete_all_posts"),
-          class: "btn btn-danger",
-          callback: () => {
-            const progressModal = openProgressModal();
-            performDelete(progressModal);
-          },
-        },
-      ];
-
-      const openProgressModal = () => {
-        return showModal("admin-delete-user-posts-progress", {
-          admin: true,
-        });
-      };
-
-      bootbox.dialog(message, buttons, { classes: "delete-all-posts" });
-    },
 
     anonymize() {
       const user = this.model;
@@ -626,5 +559,50 @@ export default Controller.extend(CanCheckEmails, {
         }
       });
     },
+
+    showDeletePostsConfirmation() {
+      showModal("admin-delete-posts-confirmation", {
+        admin: true,
+        model: this.model,
+      });
+    },
+
+    deleteAllPosts() {
+      let deletedPosts = 0;
+      let deletedPercentage = 0;
+      const user = this.model;
+
+      const performDelete = (progressModal) => {
+        this.model
+          .deleteAllPosts()
+          .then(({ posts_deleted }) => {
+            if (posts_deleted === 0) {
+              user.set("post_count", 0);
+              progressModal.send("closeModal");
+            } else {
+              deletedPosts += posts_deleted;
+              deletedPercentage = Math.floor(
+                (deletedPosts * 100) / user.get("post_count")
+              );
+              progressModal.setProperties({
+                deletedPercentage: deletedPercentage,
+              });
+              performDelete(progressModal);
+            }
+          })
+          .catch((e) => {
+            progressModal.send("closeModal");
+            let error;
+            AdminUser.find(user.get("id")).then((u) => user.setProperties(u));
+            error = extractError(e) || I18n.t("admin.user.delete_posts_failed");
+            bootbox.alert(error);
+          });
+      };
+
+      const progressModal = showModal("admin-delete-user-posts-progress", {
+        admin: true,
+      });
+      performDelete(progressModal);
+    },
   },
 });
diff --git a/app/assets/javascripts/admin/addon/controllers/modals/admin-delete-posts-confirmation.js b/app/assets/javascripts/admin/addon/controllers/modals/admin-delete-posts-confirmation.js
new file mode 100644
index 0000000..106b459
--- /dev/null
+++ b/app/assets/javascripts/admin/addon/controllers/modals/admin-delete-posts-confirmation.js
@@ -0,0 +1,46 @@
+import Controller, { inject as controller } from "@ember/controller";
+import I18n from "I18n";
+import ModalFunctionality from "discourse/mixins/modal-functionality";
+import { action } from "@ember/object";
+import { alias } from "@ember/object/computed";
+import discourseComputed from "discourse-common/utils/decorators";
+
+export default Controller.extend(ModalFunctionality, {
+  adminUserIndex: controller(),
+  username: alias("model.username"),
+  postCount: alias("model.post_count"),
+
+  onShow() {
+    this.set("value", null);
+  },
+
+  @discourseComputed("username", "postCount")
+  text(username, postCount) {
+    return I18n.t(`admin.user.delete_posts.confirmation.text`, {
+      username,
+      postCount,
+    });
+  },
+
+  @discourseComputed("username")
+  deleteButtonText(username) {
+    return I18n.t(`admin.user.delete_posts.confirmation.delete`, {
+      username,
+    });
+  },
+
+  @discourseComputed("value", "text")
+  deleteDisabled(value, text) {
+    return !value || text !== value;
+  },
+
+  @action
+  confirm() {
+    this.adminUserIndex.send("deleteAllPosts");
+  },
+
+  @action
+  close() {
+    this.send("closeModal");
+  },
+});
diff --git a/app/assets/javascripts/admin/addon/templates/modal/admin-delete-posts-confirmation.hbs b/app/assets/javascripts/admin/addon/templates/modal/admin-delete-posts-confirmation.hbs
new file mode 100644
index 0000000..9c5cb27
--- /dev/null
+++ b/app/assets/javascripts/admin/addon/templates/modal/admin-delete-posts-confirmation.hbs
@@ -0,0 +1,20 @@
+<div>
+  {{#d-modal-body rawTitle=(i18n "admin.user.delete_posts.confirmation.title" username=username)}}
+    <p>{{html-safe (i18n "admin.user.delete_posts.confirmation.description" username=username post_count=postCount text=text)}}</p>
+    {{input type="text" value=value}}
+  {{/d-modal-body}}
+
+  <div class="modal-footer">
+    {{d-button
+      class="btn-danger"
+      action=(action "confirm")
+      icon="trash-alt"
+      disabled=deleteDisabled
+      translatedLabel=deleteButtonText
+    }}
+    {{d-button
+      action=(action "close")
+      label="admin.user.delete_posts.confirmation.cancel"
+    }}
+  </div>
+</div>
diff --git a/app/assets/javascripts/admin/addon/templates/modal/admin-delete-user-posts-progress.hbs b/app/assets/javascripts/admin/addon/templates/modal/admin-delete-user-posts-progress.hbs
index 8f381db..1f96268 100644
--- a/app/assets/javascripts/admin/addon/templates/modal/admin-delete-user-posts-progress.hbs
+++ b/app/assets/javascripts/admin/addon/templates/modal/admin-delete-user-posts-progress.hbs
@@ -1,4 +1,4 @@
 {{#d-modal-body title="admin.user.delete_posts.progress.title" dismissable=false}}
-  <p>{{I18n "admin.user.delete_posts_progress"}}</p>
+  <p>{{I18n "admin.user.delete_posts.progress.description"}}</p>
   <div class="progress-bar"><span style={{html-safe (concat "width: " deletedPercentage "%")}}></span></div>
 {{/d-modal-body}}

[... diff too long, it was truncated ...]

GitHub sha: 419d71abcb814b14c15918190405428bfb543548

This commit appears in #14128 which was approved by eviltrout. It was merged by techAPJ.