FEATURE: allow plugins to extend Groups (#14216)

FEATURE: allow plugins to extend Groups (#14216)

  • add_permitted_group_param API for plugins
  • add groups-interaction-custom-options outlet
  • custom search can use custom group scope
diff --git a/app/assets/javascripts/discourse/app/lib/user-search.js b/app/assets/javascripts/discourse/app/lib/user-search.js
index 7cde342..c16e74b 100644
--- a/app/assets/javascripts/discourse/app/lib/user-search.js
+++ b/app/assets/javascripts/discourse/app/lib/user-search.js
@@ -25,6 +25,7 @@ function performSearch(
   topicId,
   categoryId,
   includeGroups,
+  customGroupsScope,
   includeMentionableGroups,
   includeMessageableGroups,
   allowedUsers,
@@ -56,6 +57,7 @@ function performSearch(
       topic_id: topicId,
       category_id: categoryId,
       include_groups: includeGroups,
+      custom_groups_scope: customGroupsScope,
       include_mentionable_groups: includeMentionableGroups,
       include_messageable_groups: includeMessageableGroups,
       groups: groupMembersOf,
@@ -100,6 +102,7 @@ let debouncedSearch = function (
   topicId,
   categoryId,
   includeGroups,
+  customGroupsScope,
   includeMentionableGroups,
   includeMessageableGroups,
   allowedUsers,
@@ -116,6 +119,7 @@ let debouncedSearch = function (
     topicId,
     categoryId,
     includeGroups,
+    customGroupsScope,
     includeMentionableGroups,
     includeMessageableGroups,
     allowedUsers,
@@ -207,6 +211,7 @@ export default function userSearch(options) {
 
   let term = options.term || "",
     includeGroups = options.includeGroups,
+    customGroupsScope = options.customGroupsScope,
     includeMentionableGroups = options.includeMentionableGroups,
     includeMessageableGroups = options.includeMessageableGroups,
     allowedUsers = options.allowedUsers,
@@ -248,6 +253,7 @@ export default function userSearch(options) {
       topicId,
       categoryId,
       includeGroups,
+      customGroupsScope,
       includeMentionableGroups,
       includeMessageableGroups,
       allowedUsers,
diff --git a/app/assets/javascripts/discourse/app/templates/components/groups-form-interaction-fields.hbs b/app/assets/javascripts/discourse/app/templates/components/groups-form-interaction-fields.hbs
index df6d0ac..7681b30 100644
--- a/app/assets/javascripts/discourse/app/templates/components/groups-form-interaction-fields.hbs
+++ b/app/assets/javascripts/discourse/app/templates/components/groups-form-interaction-fields.hbs
@@ -104,3 +104,5 @@
     onChange=(action (mut model.default_notification_level))
   }}
 </div>
+
+{{plugin-outlet name="groups-interaction-custom-options" args=(hash model=model)}}
diff --git a/app/assets/javascripts/select-kit/addon/components/user-chooser.js b/app/assets/javascripts/select-kit/addon/components/user-chooser.js
index a6b1db0..9d3f90c 100644
--- a/app/assets/javascripts/select-kit/addon/components/user-chooser.js
+++ b/app/assets/javascripts/select-kit/addon/components/user-chooser.js
@@ -70,6 +70,7 @@ export default MultiSelectComponent.extend({
       categoryId: options.categoryId,
       exclude: this.excludedUsers,
       includeGroups: options.includeGroups,
+      customGroupsScope: options.customGroupsScope,
       allowedUsers: options.allowedUsers,
       includeMentionableGroups: options.includeMentionableGroups,
       includeMessageableGroups: options.includeMessageableGroups,
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 3c84ff9..f80dfe2 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -180,6 +180,8 @@ class Admin::GroupsController < Admin::AdminController
     custom_fields = DiscoursePluginRegistry.editable_group_custom_fields
     permitted << { custom_fields: custom_fields } unless custom_fields.blank?
 
+    permitted = permitted | DiscoursePluginRegistry.group_params
+
     params.require(:group).permit(permitted)
   end
 end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 828bb11..8bc8905 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -736,6 +736,8 @@ class GroupsController < ApplicationController
       end
     end
 
+    permitted_params = permitted_params | DiscoursePluginRegistry.group_params
+
     params.require(:group).permit(*permitted_params)
   end
 
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 98b8d28..0835db6 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1123,7 +1123,12 @@ class UsersController < ApplicationController
       end
 
     if groups
-      groups = Group.search_groups(term, groups: groups)
+      groups = Group.search_groups(term,
+                                   groups: groups,
+                                   custom_scope: {
+                                     name: params["custom_groups_scope"]&.to_sym,
+                                     arguments: [current_user]
+                                   })
       groups = groups.order('groups.name asc')
 
       to_render[:groups] = groups.map do |m|
diff --git a/app/models/group.rb b/app/models/group.rb
index 41ca0a3..d863b43 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -557,8 +557,14 @@ class Group < ActiveRecord::Base
     lookup_group(name) || refresh_automatic_group!(name)
   end
 
-  def self.search_groups(name, groups: nil)
-    (groups || Group).where(
+  def self.search_groups(name, groups: nil, custom_scope: {})
+    groups ||= Group
+
+    if custom_scope.present? && DiscoursePluginRegistry.group_scope_for_search.include?(custom_scope[:name])
+      groups = groups.send(custom_scope[:name], *custom_scope[:arguments])
+    end
+
+    groups.where(
       "name ILIKE :term_like OR full_name ILIKE :term_like", term_like: "%#{name}%"
     )
   end
diff --git a/lib/discourse_plugin_registry.rb b/lib/discourse_plugin_registry.rb
index de86a25..625e7fe 100644
--- a/lib/discourse_plugin_registry.rb
+++ b/lib/discourse_plugin_registry.rb
@@ -76,6 +76,8 @@ class DiscoursePluginRegistry
   define_filtered_register :staff_editable_user_custom_fields
 
   define_filtered_register :editable_group_custom_fields
+  define_filtered_register :group_params
+  define_filtered_register :group_scope_for_search
 
   define_filtered_register :topic_thumbnail_sizes
 
diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb
index 6e9f59c..28fac37 100644
--- a/lib/plugin/instance.rb
+++ b/lib/plugin/instance.rb
@@ -369,6 +369,17 @@ class Plugin::Instance
     end
   end
 
+  # Add a permitted_param to Group, respecting if the plugin is enabled
+  # Used in GroupsController#update and Admin::GroupsController#create
+  def register_group_param(param)
+    DiscoursePluginRegistry.register_group_param(param, self)
+  end
+
+  # Add a custom scopes for search to Group, respecting if the plugin is enabled
+  def register_group_scope_for_search(scope_name)
+    DiscoursePluginRegistry.register_group_scope_for_search(scope_name, self)
+  end
+
   # Add validation method but check that the plugin is enabled
   def validate(klass, name, &block)
     klass = klass.to_s.classify.constantize
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 4a83995..9e3dc1b 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -920,6 +920,7 @@ describe Group do
 
   describe '.search_groups' do
     fab!(:group) { Fabricate(:group, name: 'tEsT_more_things', full_name: 'Abc something awesome') }
+    let(:messageable_group) { Fabricate(:group, name: "MessageableGroup", messageable_level: Group::ALIAS_LEVELS[:everyone]) }
 
     it 'should return the right groups' do
       group
@@ -934,6 +935,18 @@ describe Group do
       expect(Group.search_groups('sOmEthi')).to eq([group])
       expect(Group.search_groups('test2')).to eq([])
     end
+
+    it 'allows to filter with additional scope' do
+      messageable_group
+
+      expect(Group.search_groups('es', custom_scope: { name: :messageable, arguments: [user] }).sort).to eq([messageable_group, group].sort)
+
+      plugin = Plugin::Instance.new

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

GitHub sha: f859fd6bde348dd725c736d5a0b6a1ee091364ae

This commit appears in #14216 which was approved by martin. It was merged by lis2.