FIX: better filter for groups search (#14262)
Follow up of https://github.com/discourse/discourse/pull/14216 Allow plugins to register custom filter with block
This commit is contained in:
parent
cddba50570
commit
e3793e6d7c
|
@ -83,9 +83,10 @@ import { replaceTagRenderer } from "discourse/lib/render-tag";
|
|||
import { setNewCategoryDefaultColors } from "discourse/routes/new-category";
|
||||
import { addSearchResultsCallback } from "discourse/lib/search";
|
||||
import { addSearchSuggestion } from "discourse/widgets/search-menu-results";
|
||||
import { CUSTOM_USER_SEARCH_OPTIONS } from "select-kit/components/user-chooser";
|
||||
|
||||
// If you add any methods to the API ensure you bump up this number
|
||||
const PLUGIN_API_VERSION = "0.12.2";
|
||||
const PLUGIN_API_VERSION = "0.12.3";
|
||||
|
||||
// This helper prevents us from applying the same `modifyClass` over and over in test mode.
|
||||
function canModify(klass, type, resolverName, changes) {
|
||||
|
@ -1407,6 +1408,29 @@ class PluginApi {
|
|||
addSearchSuggestion(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom user search options.
|
||||
* It is heavily correlated with `register_groups_callback_for_users_search_controller_action` which allows defining custom filter.
|
||||
* Example usage:
|
||||
* ```
|
||||
* api.addUserSearchOption("adminsOnly");
|
||||
|
||||
* register_groups_callback_for_users_search_controller_action(:admins_only) do |groups, user|
|
||||
* groups.where(name: "admins")
|
||||
* end
|
||||
*
|
||||
* {{email-group-user-chooser
|
||||
* options=(hash
|
||||
* includeGroups=true
|
||||
* adminsOnly=true
|
||||
* )
|
||||
* }}
|
||||
* ```
|
||||
*/
|
||||
addUserSearchOption(value) {
|
||||
CUSTOM_USER_SEARCH_OPTIONS.push(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a method on a mounted widget whenever an app event happens.
|
||||
*
|
||||
|
|
|
@ -20,14 +20,18 @@ export function resetUserSearchCache() {
|
|||
oldSearch = null;
|
||||
}
|
||||
|
||||
export function camelCaseToSnakeCase(text) {
|
||||
return text.replace(/([a-zA-Z])(?=[A-Z])/g, "$1_").toLowerCase();
|
||||
}
|
||||
|
||||
function performSearch(
|
||||
term,
|
||||
topicId,
|
||||
categoryId,
|
||||
includeGroups,
|
||||
customGroupsScope,
|
||||
includeMentionableGroups,
|
||||
includeMessageableGroups,
|
||||
customUserSearchOptions,
|
||||
allowedUsers,
|
||||
groupMembersOf,
|
||||
includeStagedUsers,
|
||||
|
@ -50,22 +54,29 @@ function performSearch(
|
|||
return;
|
||||
}
|
||||
|
||||
let data = {
|
||||
term: term,
|
||||
topic_id: topicId,
|
||||
category_id: categoryId,
|
||||
include_groups: includeGroups,
|
||||
include_mentionable_groups: includeMentionableGroups,
|
||||
include_messageable_groups: includeMessageableGroups,
|
||||
groups: groupMembersOf,
|
||||
topic_allowed_users: allowedUsers,
|
||||
include_staged_users: includeStagedUsers,
|
||||
last_seen_users: lastSeenUsers,
|
||||
limit: limit,
|
||||
};
|
||||
|
||||
if (customUserSearchOptions) {
|
||||
Object.keys(customUserSearchOptions).forEach((key) => {
|
||||
data[camelCaseToSnakeCase(key)] = customUserSearchOptions[key];
|
||||
});
|
||||
}
|
||||
|
||||
// need to be able to cancel this
|
||||
oldSearch = $.ajax(userPath("search/users"), {
|
||||
data: {
|
||||
term: term,
|
||||
topic_id: topicId,
|
||||
category_id: categoryId,
|
||||
include_groups: includeGroups,
|
||||
custom_groups_scope: customGroupsScope,
|
||||
include_mentionable_groups: includeMentionableGroups,
|
||||
include_messageable_groups: includeMessageableGroups,
|
||||
groups: groupMembersOf,
|
||||
topic_allowed_users: allowedUsers,
|
||||
include_staged_users: includeStagedUsers,
|
||||
last_seen_users: lastSeenUsers,
|
||||
limit: limit,
|
||||
},
|
||||
data,
|
||||
});
|
||||
|
||||
let returnVal = CANCELLED_STATUS;
|
||||
|
@ -102,9 +113,9 @@ let debouncedSearch = function (
|
|||
topicId,
|
||||
categoryId,
|
||||
includeGroups,
|
||||
customGroupsScope,
|
||||
includeMentionableGroups,
|
||||
includeMessageableGroups,
|
||||
customUserSearchOptions,
|
||||
allowedUsers,
|
||||
groupMembersOf,
|
||||
includeStagedUsers,
|
||||
|
@ -119,9 +130,9 @@ let debouncedSearch = function (
|
|||
topicId,
|
||||
categoryId,
|
||||
includeGroups,
|
||||
customGroupsScope,
|
||||
includeMentionableGroups,
|
||||
includeMessageableGroups,
|
||||
customUserSearchOptions,
|
||||
allowedUsers,
|
||||
groupMembersOf,
|
||||
includeStagedUsers,
|
||||
|
@ -211,9 +222,9 @@ export default function userSearch(options) {
|
|||
|
||||
let term = options.term || "",
|
||||
includeGroups = options.includeGroups,
|
||||
customGroupsScope = options.customGroupsScope,
|
||||
includeMentionableGroups = options.includeMentionableGroups,
|
||||
includeMessageableGroups = options.includeMessageableGroups,
|
||||
customUserSearchOptions = options.customUserSearchOptions,
|
||||
allowedUsers = options.allowedUsers,
|
||||
topicId = options.topicId,
|
||||
categoryId = options.categoryId,
|
||||
|
@ -253,9 +264,9 @@ export default function userSearch(options) {
|
|||
topicId,
|
||||
categoryId,
|
||||
includeGroups,
|
||||
customGroupsScope,
|
||||
includeMentionableGroups,
|
||||
includeMessageableGroups,
|
||||
customUserSearchOptions,
|
||||
allowedUsers,
|
||||
groupMembersOf,
|
||||
includeStagedUsers,
|
||||
|
|
|
@ -6,6 +6,8 @@ import MultiSelectComponent from "select-kit/components/multi-select";
|
|||
import { computed } from "@ember/object";
|
||||
import { makeArray } from "discourse-common/lib/helpers";
|
||||
|
||||
export const CUSTOM_USER_SEARCH_OPTIONS = [];
|
||||
|
||||
export default MultiSelectComponent.extend({
|
||||
pluginApiIdentifiers: ["user-chooser"],
|
||||
classNames: ["user-chooser"],
|
||||
|
@ -64,19 +66,29 @@ export default MultiSelectComponent.extend({
|
|||
return;
|
||||
}
|
||||
|
||||
let customUserSearchOptions = CUSTOM_USER_SEARCH_OPTIONS.reduce(
|
||||
(obj, option) => {
|
||||
return {
|
||||
...obj,
|
||||
[option]: options[option],
|
||||
};
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
return userSearch({
|
||||
term: filter,
|
||||
topicId: options.topicId,
|
||||
categoryId: options.categoryId,
|
||||
exclude: this.excludedUsers,
|
||||
includeGroups: options.includeGroups,
|
||||
customGroupsScope: options.customGroupsScope,
|
||||
allowedUsers: options.allowedUsers,
|
||||
includeMentionableGroups: options.includeMentionableGroups,
|
||||
includeMessageableGroups: options.includeMessageableGroups,
|
||||
groupMembersOf: options.groupMembersOf,
|
||||
allowEmails: options.allowEmails,
|
||||
includeStagedUsers: this.includeStagedUsers,
|
||||
customUserSearchOptions,
|
||||
}).then((result) => {
|
||||
if (typeof result === "string") {
|
||||
// do nothing promise probably got cancelled
|
||||
|
|
|
@ -1123,12 +1123,13 @@ class UsersController < ApplicationController
|
|||
end
|
||||
|
||||
if groups
|
||||
groups = Group.search_groups(term,
|
||||
groups: groups,
|
||||
custom_scope: {
|
||||
name: params["custom_groups_scope"]&.to_sym,
|
||||
arguments: [current_user]
|
||||
})
|
||||
DiscoursePluginRegistry.groups_callback_for_users_search_controller_action.each do |param_name, block|
|
||||
if params[param_name.to_s]
|
||||
groups = block.call(groups, current_user)
|
||||
end
|
||||
end
|
||||
|
||||
groups = Group.search_groups(term, groups: groups)
|
||||
groups = groups.order('groups.name asc')
|
||||
|
||||
to_render[:groups] = groups.map do |m|
|
||||
|
|
|
@ -560,10 +560,6 @@ class Group < ActiveRecord::Base
|
|||
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}%"
|
||||
)
|
||||
|
|
|
@ -68,6 +68,7 @@ class DiscoursePluginRegistry
|
|||
define_register :vendored_core_pretty_text, Set
|
||||
define_register :seedfu_filter, Set
|
||||
define_register :demon_processes, Set
|
||||
define_register :groups_callback_for_users_search_controller_action, Hash
|
||||
|
||||
define_filtered_register :staff_user_custom_fields
|
||||
define_filtered_register :public_user_custom_fields
|
||||
|
@ -77,7 +78,6 @@ class DiscoursePluginRegistry
|
|||
|
||||
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
|
||||
|
||||
|
|
|
@ -375,9 +375,19 @@ class Plugin::Instance
|
|||
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)
|
||||
# Add a custom callback for search to Group
|
||||
# Callback is called in UsersController#search_users
|
||||
# Block takes groups and optional current_user
|
||||
# For example:
|
||||
# plugin.register_groups_callback_for_users_search_controller_action(:admins_filter) do |groups, user|
|
||||
# groups.where(name: "admins")
|
||||
# end
|
||||
def register_groups_callback_for_users_search_controller_action(callback, &block)
|
||||
if DiscoursePluginRegistry.groups_callback_for_users_search_controller_action.key?(callback)
|
||||
raise "groups_callback_for_users_search_controller_action callback already registered"
|
||||
end
|
||||
|
||||
DiscoursePluginRegistry.groups_callback_for_users_search_controller_action[callback] = block
|
||||
end
|
||||
|
||||
# Add validation method but check that the plugin is enabled
|
||||
|
|
|
@ -935,18 +935,6 @@ 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
|
||||
plugin.register_group_scope_for_search(:messageable)
|
||||
expect(Group.search_groups('es', custom_scope: { name: :messageable, arguments: [user] }).sort).to eq([messageable_group].sort)
|
||||
|
||||
DiscoursePluginRegistry.reset!
|
||||
end
|
||||
end
|
||||
|
||||
describe '#bulk_add' do
|
||||
|
|
|
@ -4083,6 +4083,25 @@ describe UsersController do
|
|||
.to_not include(private_group.name)
|
||||
end
|
||||
|
||||
it 'allows plugins to register custom groups filter' do
|
||||
get "/u/search/users.json", params: { include_groups: "true", term: "a" }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
groups = response.parsed_body["groups"]
|
||||
expect(groups.count).to eq(6)
|
||||
|
||||
plugin = Plugin::Instance.new
|
||||
plugin.register_groups_callback_for_users_search_controller_action(:admins_filter) do |original_groups, user|
|
||||
original_groups.where(name: "admins")
|
||||
end
|
||||
get "/u/search/users.json", params: { include_groups: "true", admins_filter: "true", term: "a" }
|
||||
expect(response.status).to eq(200)
|
||||
groups = response.parsed_body["groups"]
|
||||
expect(groups).to eq([{ "name" => "admins", "full_name" => nil }])
|
||||
|
||||
DiscoursePluginRegistry.reset!
|
||||
end
|
||||
|
||||
it "doesn't search for groups" do
|
||||
get "/u/search/users.json", params: {
|
||||
include_mentionable_groups: 'false',
|
||||
|
|
Loading…
Reference in New Issue