FEATURE: Assign bulk actions for topics lists (#110)

FEATURE: Assign bulk actions for topics lists (#110)

  • Allows unassigning and reassigning topics using bulk actions
  • Adds bulk actions UI to the group assigned page
diff --git a/assets/javascripts/discourse-assign/controllers/assign-user.js.es6 b/assets/javascripts/discourse-assign/controllers/assign-user.js.es6
index 5d5800a..d41c0ef 100644
--- a/assets/javascripts/discourse-assign/controllers/assign-user.js.es6
+++ b/assets/javascripts/discourse-assign/controllers/assign-user.js.es6
@@ -1,7 +1,9 @@
 import { ajax } from "discourse/lib/ajax";
 import { popupAjaxError } from "discourse/lib/ajax-error";
+import { inject as controller } from "@ember/controller";
 
 export default Ember.Controller.extend({
+  topicBulkActions: controller(),
   assignSuggestions: null,
   allowedGroups: null,
   taskActions: Ember.inject.service(),
@@ -22,8 +24,19 @@ export default Ember.Controller.extend({
     }
   },
 
+  bulkAction(username) {
+    this.topicBulkActions.performAndRefresh({
+      type: "assign",
+      username,
+    });
+  },
+
   actions: {
     assignUser(user) {
+      if (this.isBulkAction) {
+        this.bulkAction(user.username);
+        return;
+      }
       this.setProperties({
         "model.username": user.username,
         "model.allowedGroups": this.taskActions.allowedGroups,
@@ -32,6 +45,10 @@ export default Ember.Controller.extend({
     },
 
     assign() {
+      if (this.isBulkAction) {
+        this.bulkAction(this.model.username);
+        return;
+      }
       let path = "/assign/assign";
 
       if (Ember.isEmpty(this.get("model.username"))) {
diff --git a/assets/javascripts/discourse-assign/controllers/group-assigned-show.js.es6 b/assets/javascripts/discourse-assign/controllers/group-assigned-show.js.es6
index 5ea1aaf..27a841a 100644
--- a/assets/javascripts/discourse-assign/controllers/group-assigned-show.js.es6
+++ b/assets/javascripts/discourse-assign/controllers/group-assigned-show.js.es6
@@ -1,4 +1,5 @@
 import UserTopicsList from "discourse/controllers/user-topics-list";
+import { alias } from "@ember/object/computed";
 import { debounce } from "@ember/runloop";
 import discourseComputed from "discourse-common/utils/decorators";
 import { INPUT_DELAY } from "discourse-common/config/environment";
@@ -9,6 +10,9 @@ export default UserTopicsList.extend({
   order: null,
   ascending: false,
   q: "",
+  bulkSelectEnabled: false,
+  selected: [],
+  canBulkSelect: alias("currentUser.staff"),
 
   queryParams: ["order", "ascending", "q"],
 
@@ -61,5 +65,11 @@ export default UserTopicsList.extend({
     onChangeFilter(value) {
       debounce(this, this._setSearchTerm, value, INPUT_DELAY * 2);
     },
+    toggleBulkSelect() {
+      this.toggleProperty("bulkSelectEnabled");
+    },
+    refresh() {
+      this.refreshModel();
+    },
   },
 });
diff --git a/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6 b/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6
index f67eb8e..5ce0e70 100644
--- a/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6
+++ b/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6
@@ -7,6 +7,9 @@ import { queryRegistry } from "discourse/widgets/widget";
 import { getOwner } from "discourse-common/lib/get-owner";
 import { htmlSafe } from "@ember/template";
 import getURL from "discourse-common/lib/get-url";
+import { addBulkButton } from "discourse/controllers/topic-bulk-actions";
+import TopicButtonAction from "discourse/controllers/topic-bulk-actions";
+import { inject } from "@ember/controller";
 import I18n from "I18n";
 
 function titleForState(user) {
@@ -320,6 +323,30 @@ export default {
       return;
     }
 
+    const currentUser = container.lookup("current-user:main");
+    if (currentUser.can_assign) {
+      TopicButtonAction.reopen({
+        assignUser: inject("assign-user"),
+        actions: {
+          showReAssign() {
+            this.set("assignUser.isBulkAction", true);
+            this.set("assignUser.model", { username: "" });
+            this.send("changeBulkTemplate", "modal/assign-user");
+          },
+          unassignTopics() {
+            this.performAndRefresh({ type: "unassign" });
+          },
+        },
+      });
+      addBulkButton("showReAssign", "assign", {
+        icon: "user-plus",
+        class: "btn-default",
+      });
+      addBulkButton("unassignTopics", "unassign", {
+        icon: "user-times",
+        class: "btn-default",
+      });
+    }
     withPluginApi("0.8.11", (api) => initialize(api, container));
     withPluginApi("0.8.28", (api) =>
       registerTopicFooterButtons(api, container)
diff --git a/assets/javascripts/discourse/templates/components/assigned-topic-list-item.hbs b/assets/javascripts/discourse/templates/components/assigned-topic-list-item.hbs
index 44c956f..798e711 100644
--- a/assets/javascripts/discourse/templates/components/assigned-topic-list-item.hbs
+++ b/assets/javascripts/discourse/templates/components/assigned-topic-list-item.hbs
@@ -5,8 +5,13 @@
   This causes the topic-post-badge to be considered the same word as "text"
   at the end of the link, preventing it from line wrapping onto its own line.
 --}}
-<td class="main-link clearfix" colspan="1">
-  <span class="link-top-line">
+{{#if bulkSelectEnabled}}
+  <td class="bulk-select">
+    <input type="checkbox" class="bulk-select">
+  </td>
+{{/if}}
+<td class='main-link clearfix' colspan="1">
+  <span class='link-top-line'>
     {{~raw "topic-status" topic=topic}}
     {{~#if isPrivateMessage}}
       {{~d-icon "envelope" class="private-message-icon"}}
diff --git a/assets/javascripts/discourse/templates/components/assigned-topic-list.hbs b/assets/javascripts/discourse/templates/components/assigned-topic-list.hbs
index c09697b..26dcd79 100644
--- a/assets/javascripts/discourse/templates/components/assigned-topic-list.hbs
+++ b/assets/javascripts/discourse/templates/components/assigned-topic-list.hbs
@@ -1,6 +1,7 @@
 {{#unless skipHeader}}
   <thead>
     {{raw "topic-list-header"
+      canBulkSelect=canBulkSelect
       toggleInTitle=toggleInTitle
       hideCategory=hideCategory
       showPosters=showPosters
@@ -17,6 +18,7 @@
 <tbody>
   {{#each filteredTopics as |topic|}}
     {{assigned-topic-list-item topic=topic
+                               bulkSelectEnabled=bulkSelectEnabled
                                showTopicPostBadges=showTopicPostBadges
                                hideCategory=hideCategory
                                showPosters=showPosters
diff --git a/assets/javascripts/discourse/templates/components/basic-assigned-topic-list.hbs b/assets/javascripts/discourse/templates/components/basic-assigned-topic-list.hbs
index 4b8b01d..8013463 100644
--- a/assets/javascripts/discourse/templates/components/basic-assigned-topic-list.hbs
+++ b/assets/javascripts/discourse/templates/components/basic-assigned-topic-list.hbs
@@ -14,10 +14,13 @@
                           hideCategory=hideCategory
                           topics=topics
                           expandExcerpts=expandExcerpts
+                          bulkSelectEnabled=bulkSelectEnabled
+                          canBulkSelect=canBulkSelect
                           selected=selected
                           skipHeader=skipHeader
                           tagsForUser=tagsForUser
                           changeSort=changeSort
+                          toggleBulkSelect=toggleBulkSelect
                           unassign=unassign
                           reassign=reassign
                           onScroll=onScroll
diff --git a/assets/javascripts/discourse/templates/group-topics-list.hbs b/assets/javascripts/discourse/templates/group-topics-list.hbs
index 9c7d662..9f668d5 100644
--- a/assets/javascripts/discourse/templates/group-topics-list.hbs
+++ b/assets/javascripts/discourse/templates/group-topics-list.hbs
@@ -8,17 +8,23 @@
   </div>
 </div>
 {{#load-more class="paginated-topics-list" selector=".paginated-topics-list .topic-list tr" action=(action "loadMore")}}
+  {{bulk-select-button
+    selected=selected

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

GitHub sha: 3e3dc381

This commit appears in #110 which was merged by davidtaylorhq.

You shouldn’t default to an array in Ember, as it means every instance of your object has a pointer to the same default instance. Instead you should default to null and set [] in an init method.

This is less of a concern here because it’s a controller which are effectively singletons, but it’s still the right thing to do when considering things like tests.

1 Like