FEATURE: reason to reject user signup (#11700)

FEATURE: reason to reject user signup (#11700)

Feature for Must Approve Users setup. When a user is rejected, a staff member can optionally set a reason for audit purposes. In addition, feedback email can be sent to the user.

Meta: Account rejection email - #8 by kris.kotlarek - feature - Discourse Meta

diff --git a/app/assets/javascripts/discourse/app/components/reviewable-item.js b/app/assets/javascripts/discourse/app/components/reviewable-item.js
index 585a54f..bd77ff1 100644
--- a/app/assets/javascripts/discourse/app/components/reviewable-item.js
+++ b/app/assets/javascripts/discourse/app/components/reviewable-item.js
@@ -110,6 +110,10 @@ export default Component.extend({
         `/review/${reviewable.id}/perform/${action.id}?version=${version}`,
         {
           type: "PUT",
+          data: {
+            send_email: reviewable.sendEmail,
+            reject_reason: reviewable.rejectReason,
+          },
         }
       )
         .then((result) => {
@@ -222,12 +226,21 @@ export default Component.extend({
       }
 
       let msg = action.get("confirm_message");
+      let requireRejectReason = action.get("require_reject_reason");
       if (msg) {
         bootbox.confirm(msg, (answer) => {
           if (answer) {
             return this._performConfirmed(action);
           }
         });
+      } else if (requireRejectReason) {
+        showModal("reject-reason-reviewable", {
+          title: "review.reject_reason.title",
+          model: this.reviewable,
+        }).setProperties({
+          performConfirmed: this._performConfirmed.bind(this),
+          action,
+        });
       } else {
         return this._performConfirmed(action);
       }
diff --git a/app/assets/javascripts/discourse/app/controllers/reject-reason-reviewable.js b/app/assets/javascripts/discourse/app/controllers/reject-reason-reviewable.js
new file mode 100644
index 0000000..c1b4b72
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/controllers/reject-reason-reviewable.js
@@ -0,0 +1,22 @@
+import Controller from "@ember/controller";
+import ModalFunctionality from "discourse/mixins/modal-functionality";
+import { action } from "@ember/object";
+
+export default Controller.extend(ModalFunctionality, {
+  rejectReason: null,
+  sendEmail: false,
+
+  onShow() {
+    this.setProperties({ rejectReason: null, sendEmail: false });
+  },
+
+  @action
+  perform() {
+    this.model.setProperties({
+      rejectReason: this.rejectReason,
+      sendEmail: this.sendEmail,
+    });
+    this.send("closeModal");
+    this.performConfirmed(this.action);
+  },
+});
diff --git a/app/assets/javascripts/discourse/app/templates/components/reviewable-user.hbs b/app/assets/javascripts/discourse/app/templates/components/reviewable-user.hbs
index f747691..9e246ed 100644
--- a/app/assets/javascripts/discourse/app/templates/components/reviewable-user.hbs
+++ b/app/assets/javascripts/discourse/app/templates/components/reviewable-user.hbs
@@ -34,6 +34,10 @@
       </div>
     {{/if}}
 
+    {{reviewable-field classes="reviewable-user-details reject-reason"
+                       name=(i18n "review.user.reject_reason")
+                       value=reviewable.reject_reason}}
+
     {{#each userFields as |f|}}
       {{reviewable-field classes="reviewable-user-details user-field"
                          name=f.name
diff --git a/app/assets/javascripts/discourse/app/templates/modal/reject-reason-reviewable.hbs b/app/assets/javascripts/discourse/app/templates/modal/reject-reason-reviewable.hbs
new file mode 100644
index 0000000..c5f5c63
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/templates/modal/reject-reason-reviewable.hbs
@@ -0,0 +1,19 @@
+{{#d-modal-body class="explain-reviewable"}}
+  {{textarea value=rejectReason}}
+  <div class="control-group">
+    <label>
+      {{input type="checkbox"
+        class="inline"
+        checked=sendEmail
+      }}
+      {{i18n "review.reject_reason.send_email"}}
+    </label>
+  </div>
+{{/d-modal-body}}
+
+<div class="modal-footer">
+  {{d-button action=(route-action "closeModal") label="close"}}
+  <div class="pull-right">
+    {{d-button icon="trash-alt" class="btn-danger" action=(action "perform")}}
+  </div>
+</div>
diff --git a/app/assets/javascripts/discourse/tests/acceptance/review-test.js b/app/assets/javascripts/discourse/tests/acceptance/review-test.js
index a8dfcd1..8a205de 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/review-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/review-test.js
@@ -1,5 +1,6 @@
 import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
 import { click, fillIn, visit } from "@ember/test-helpers";
+import I18n from "I18n";
 import selectKit from "discourse/tests/helpers/select-kit-helper";
 import { test } from "qunit";
 
@@ -35,6 +36,33 @@ acceptance("Review", function (needs) {
     );
   });
 
+  test("Reject user", async function (assert) {
+    await visit("/review");
+    await click(
+      `${user} .reviewable-actions button[data-name="Delete User..."]`
+    );
+    await click(`${user} li[data-value="reject_user_delete"]`);
+    assert.ok(
+      queryAll(".reject-reason-reviewable-modal:visible .title")
+        .html()
+        .includes(I18n.t("review.reject_reason.title")),
+      "it opens reject reason modal when user is rejected"
+    );
+
+    await click(".modal-footer button[aria-label='Close']");
+
+    await click(
+      `${user} .reviewable-actions button[data-name="Delete User..."]`
+    );
+    await click(`${user} li[data-value="reject_user_block"]`);
+    assert.ok(
+      queryAll(".reject-reason-reviewable-modal:visible .title")
+        .html()
+        .includes(I18n.t("review.reject_reason.title")),
+      "it opens reject reason modal when user is rejected and blocked"
+    );
+  });
+
   test("Settings", async function (assert) {
     await visit("/review/settings");
 
diff --git a/app/assets/javascripts/discourse/tests/helpers/review-pretender.js b/app/assets/javascripts/discourse/tests/helpers/review-pretender.js
index dd7f925..0af2e90 100644
--- a/app/assets/javascripts/discourse/tests/helpers/review-pretender.js
+++ b/app/assets/javascripts/discourse/tests/helpers/review-pretender.js
@@ -24,7 +24,7 @@ export default function (helpers) {
           created_at: "2019-01-14T19:49:53.571Z",
           username: "newbie",
           email: "newbie@example.com",
-          bundled_action_ids: ["approve", "reject"],
+          bundled_action_ids: ["approve", "reject", "reject_user"],
         },
         {
           id: 4321,
@@ -58,6 +58,12 @@ export default function (helpers) {
           id: "reject",
           action_ids: ["reject"],
         },
+        {
+          id: "reject_user",
+          icon: "user-times",
+          label: "Delete User...",
+          action_ids: ["reject_user_delete", "reject_user_block"],
+        },
       ],
       actions: [
         {
@@ -70,6 +76,23 @@ export default function (helpers) {
           label: "Reject",
           icon: "far-thumbs-down",
         },
+        {
+          id: "reject_user_delete",
+          icon: "user-times",
+          button_class: null,
+          label: "Delete User",
+          description: "The user will be deleted from the forum.",
+          require_reject_reason: true,
+        },
+        {
+          id: "reject_user_block",
+          icon: "ban",
+          button_class: null,
+          label: "Delete and Block User",
+          description:
+            "The user will be deleted, and we'll block their IP and email address.",
+          require_reject_reason: true,
+        },
       ],
       reviewable_scores: [{ id: 1 }, { id: 2 }],
       users: [{ id: 1, username: "eviltrout" }],
diff --git a/app/controllers/reviewables_controller.rb b/app/controllers/reviewables_controller.rb
index 874a678..35474d8 100644
--- a/app/controllers/reviewables_controller.rb
+++ b/app/controllers/reviewables_controller.rb
@@ -189,6 +189,8 @@ class ReviewablesController < ApplicationController
         return render_json_error(error)
       end
 
+      args.merge!(reject_reason: params[:reject_reason], send_email: params[:send_email] == "true") if reviewable.type == 'ReviewableUser'
+

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

GitHub sha: 06b7c445

1 Like

This commit appears in #11700 which was approved by eviltrout. It was merged by lis2.

This commit has been mentioned on Discourse Meta. There might be relevant details there: