FEATURE: new assignable group option instead of messageable (#195)

FEATURE: new assignable group option instead of messageable (#195)

V1 of group assign was using “who can message” to determine if group can be assigned.

This PR is introducing new separate setting “who can assign”

diff --git a/app/controllers/discourse_assign/assign_controller.rb b/app/controllers/discourse_assign/assign_controller.rb
index fe73720..7df479a 100644
--- a/app/controllers/discourse_assign/assign_controller.rb
+++ b/app/controllers/discourse_assign/assign_controller.rb
@@ -25,7 +25,7 @@ module DiscourseAssign
 
       render json: {
         assign_allowed_on_groups: Group.visible_groups(current_user).assign_allowed_groups.pluck(:name),
-        assign_allowed_for_groups: Group.visible_groups(current_user).messageable(current_user).pluck(:name),
+        assign_allowed_for_groups: Group.visible_groups(current_user).assignable(current_user).pluck(:name),
         suggestions: ActiveModel::ArraySerializer.new(users, scope: guardian, each_serializer: BasicUserSerializer),
       }
     end
diff --git a/assets/javascripts/discourse-assign/connectors/groups-interaction-custom-options/assignable-interaction-fields.hbs b/assets/javascripts/discourse-assign/connectors/groups-interaction-custom-options/assignable-interaction-fields.hbs
new file mode 100644
index 0000000..b3647a8
--- /dev/null
+++ b/assets/javascripts/discourse-assign/connectors/groups-interaction-custom-options/assignable-interaction-fields.hbs
@@ -0,0 +1,13 @@
+<div class="control-group">
+  <label class="control-label">{{i18n "discourse_assign.admin.groups.manage.interaction.assign"}}</label>
+  <label for="visiblity">{{i18n "discourse_assign.admin.groups.manage.interaction.assignable_levels.title"}}</label>
+
+  {{combo-box
+    name="alias"
+    valueProperty="value"
+    value=assignableLevel
+    content=assignableLevelOptions
+    class="groups-form-assignable-level"
+    onChange=(action (mut model.assignable_level))
+  }}
+</div>
diff --git a/assets/javascripts/discourse-assign/connectors/groups-interaction-custom-options/assignable-interaction-fields.js.es6 b/assets/javascripts/discourse-assign/connectors/groups-interaction-custom-options/assignable-interaction-fields.js.es6
new file mode 100644
index 0000000..ddbcf30
--- /dev/null
+++ b/assets/javascripts/discourse-assign/connectors/groups-interaction-custom-options/assignable-interaction-fields.js.es6
@@ -0,0 +1,24 @@
+import I18n from "I18n";
+import { or } from "@ember/object/computed";
+import { defineProperty } from "@ember/object";
+
+export default {
+  name: "assignable-interaction-fields",
+
+  setupComponent(args, component) {
+    this.assignableLevelOptions = [
+      { name: I18n.t("groups.alias_levels.nobody"), value: 0 },
+      { name: I18n.t("groups.alias_levels.only_admins"), value: 1 },
+      { name: I18n.t("groups.alias_levels.mods_and_admins"), value: 2 },
+      { name: I18n.t("groups.alias_levels.members_mods_and_admins"), value: 3 },
+      { name: I18n.t("groups.alias_levels.owners_mods_and_admins"), value: 4 },
+      { name: I18n.t("groups.alias_levels.everyone"), value: 99 },
+    ];
+
+    defineProperty(
+      component,
+      "assignableLevel",
+      or("model.assignable_level", "assignableLevelOptions.firstObject.value")
+    );
+  },
+};
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 6c45c96..e361a7b 100644
--- a/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6
+++ b/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6
@@ -273,6 +273,14 @@ function initialize(api) {
     },
   });
 
+  api.modifyClass("model:group", {
+    asJSON() {
+      return Object.assign({}, this._super(...arguments), {
+        assignable_level: this.assignable_level,
+      });
+    },
+  });
+
   api.modifyClass("controller:topic", {
     pluginId: PLUGIN_ID,
 
@@ -442,5 +450,8 @@ export default {
       api.addGroupPostSmallActionCode("assigned_group");
       api.addGroupPostSmallActionCode("unassigned_group");
     });
+    withPluginApi("0.12.3", (api) => {
+      api.addUserSearchOption("assignableGroups");
+    });
   },
 };
diff --git a/assets/javascripts/discourse/templates/modal/assign-user.hbs b/assets/javascripts/discourse/templates/modal/assign-user.hbs
index dcf847c..4a89eca 100644
--- a/assets/javascripts/discourse/templates/modal/assign-user.hbs
+++ b/assets/javascripts/discourse/templates/modal/assign-user.hbs
@@ -9,8 +9,8 @@
       options=(hash
         placementStrategy="absolute"
         filterPlaceholder=placeholderKey
-        includeGroups=false
-        includeMessageableGroups=true
+        includeGroups=true
+        assignableGroups=true
         groupMembersOf=allowedGroups
         maximum=1
         autofocus=autofocus
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index aa5b216..ede5320 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -47,6 +47,13 @@ en:
         weekly: "Weekly"
         monthly: "Monthly"
         quarterly: "Quarterly"
+      admin:
+        groups:
+          manage:
+            interaction:
+              assign: "Assign"
+              assignable_levels:
+                title: "Who can assign this group"
     user:
       messages:
         assigned_title: "Assigned (%{count})"
diff --git a/db/migrate/20210830024453_add_assignable_level_to_groups.rb b/db/migrate/20210830024453_add_assignable_level_to_groups.rb
new file mode 100644
index 0000000..01118ef
--- /dev/null
+++ b/db/migrate/20210830024453_add_assignable_level_to_groups.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddAssignableLevelToGroups < ActiveRecord::Migration[6.1]
+  def change
+    add_column :groups, :assignable_level, :integer, default: 0, null: false
+  end
+end
diff --git a/lib/topic_assigner.rb b/lib/topic_assigner.rb
index 13067a9..c0d914b 100644
--- a/lib/topic_assigner.rb
+++ b/lib/topic_assigner.rb
@@ -126,7 +126,7 @@ class ::TopicAssigner
   end
 
   def allowed_group_ids
-    @allowed_group_ids ||= Group.messageable(@assigned_by).pluck(:id)
+    @allowed_group_ids ||= Group.assignable(@assigned_by).pluck(:id)
   end
 
   def can_assign_to?(assign_to)
diff --git a/plugin.rb b/plugin.rb
index 3d0ff41..413bbb4 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -36,10 +36,33 @@ after_initialize do
   require 'topic_assigner'
   require 'pending_assigns_reminder'
 
+  # TODO: Drop when Discourse stable 2.8.0 is released
+  if respond_to?(:register_group_param)
+    register_group_param(:assignable_level)
+  end
+  if respond_to?(:register_groups_callback_for_users_search_controller_action)
+    register_groups_callback_for_users_search_controller_action(:assignable_groups) do |groups, user|
+      groups.assignable(user)
+    end
+  end
+
   class ::Topic
     has_one :assignment, dependent: :destroy
   end
 
+  class ::Group
+    scope :assignable, ->(user) {
+      where("assignable_level in (:levels) OR
+          (
+            assignable_level = #{ALIAS_LEVELS[:members_mods_and_admins]} AND id in (
+            SELECT group_id FROM group_users WHERE user_id = :user_id)
+          ) OR (
+            assignable_level = #{ALIAS_LEVELS[:owners_mods_and_admins]} AND id in (
+            SELECT group_id FROM group_users WHERE user_id = :user_id AND owner IS TRUE)
+          )", levels: alias_levels(user), user_id: user && user.id)
+    }
+  end
+
   frequency_field = PendingAssignsReminder::REMINDERS_FREQUENCY
   register_editable_user_custom_field frequency_field
   User.register_custom_field_type frequency_field, :integer
@@ -73,6 +96,10 @@ after_initialize do
     scope.can_assign?
   end
 
+  add_to_serializer(:group_show, :assignable_level) do
+    object.assignable_level
+  end
+
   add_to_serializer(:group_show, :can_show_assigned_tab?) do
     object.can_show_assigned_tab?
   end
diff --git a/spec/serializers/group_show_serializer_spec.rb b/spec/serializers/group_show_serializer_spec.rb
index 0a1a23d..1e2aaff 100644
--- a/spec/serializers/group_show_serializer_spec.rb
+++ b/spec/serializers/group_show_serializer_spec.rb

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

GitHub sha: 375f7ba78d90ba3eab1c08334e0f5f995bb40281

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