Add support to search for all users in directory (#13803)

pull/13851/head
Diego Sampaio 6 years ago committed by GitHub
parent 58584d486f
commit eba8598482
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      app/models/server/models/Users.js
  2. 20
      app/ui/client/views/app/directory.html
  3. 18
      app/ui/client/views/app/directory.js
  4. 4
      packages/rocketchat-i18n/i18n/en.i18n.json
  5. 46
      server/methods/browseChannels.js

@ -18,6 +18,7 @@ export class Users extends Base {
this.tryEnsureIndex({ statusConnection: 1 }, { sparse: 1 });
this.tryEnsureIndex({ type: 1 });
this.tryEnsureIndex({ 'visitorEmails.address': 1 });
this.tryEnsureIndex({ federation: 1 }, { sparse: true });
}
getLoginTokensByUserId(userId) {
@ -480,7 +481,7 @@ export class Users extends Base {
return this.find(query, options);
}
findByActiveUsersExcept(searchTerm, exceptions, options, forcedSearchFields) {
findByActiveUsersExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery = []) {
if (exceptions == null) { exceptions = []; }
if (options == null) { options = {}; }
if (!_.isArray(exceptions)) {
@ -504,6 +505,7 @@ export class Users extends Base {
{
username: { $exists: true, $nin: exceptions },
},
...extraQuery,
],
};
@ -511,6 +513,26 @@ export class Users extends Base {
return this._db.find(query, options);
}
findByActiveLocalUsersExcept(searchTerm, exceptions, options, forcedSearchFields, localPeer) {
const extraQuery = [
{
$or: [
{ federation: { $exists: false } },
{ 'federation.peer': localPeer },
],
},
];
return this.findByActiveUsersExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery);
}
findByActiveExternalUsersExcept(searchTerm, exceptions, options, forcedSearchFields, localPeer) {
const extraQuery = [
{ federation: { $exists: true } },
{ 'federation.peer': { $ne: localPeer } },
];
return this.findByActiveUsersExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery);
}
findUsersByNameOrUsername(nameOrUsername, options) {
const query = {
username: {

@ -5,21 +5,21 @@
{{>tabs tabs=tabsData}}
<div class="rc-directory-fields">
<div class="rc-input rc-input--small rc-directory-search">
{{> icon icon="magnifier" block="rc-input__icon" }}
<input type="text" class="rc-input__element rc-input__element--small js-search" name="message-search" id="message-search" placeholder={{#if $eq searchType 'channels'}}{{_ "Search_Channels"}}{{/if}}{{#if $eq searchType 'users'}}{{_ "Search_Users"}}{{/if}} autocomplete="off">
</div>
{{#if $eq searchType 'users'}}
{{#if federationEnabled}}
<label class="rc-select rc-directory-dropdown rc-directory-search">
<select class="rc-select__element js-setting-data js-workspace" name="search-type">
<option class="rc-select__option" value="local" selected="true">{{_ "Local_Workspace"}}</option>
<option class="rc-select__option" value="all">{{_ "Every_Workspace"}}</option>
<option class="rc-select__option" value="local" selected="true">{{_ "Local_Domains"}}</option>
<option class="rc-select__option" value="external">{{_ "External_Domains"}}</option>
</select>
{{> icon block="rc-select__arrow" icon="arrow-down" }}
</label>
{{/if}}
{{/if}}
<div class="rc-input rc-input--small rc-directory-search">
{{> icon icon="magnifier" block="rc-input__icon" }}
<input type="text" class="rc-input__element rc-input__element--small js-search" name="message-search" id="message-search" placeholder={{#if $eq searchType 'channels'}}{{_ "Search_Channels"}}{{/if}}{{#if $eq searchType 'users'}}{{_ "Search_Users"}}{{/if}} autocomplete="off">
</div>
</div>
{{#if $eq searchType 'channels'}}
@ -91,10 +91,12 @@
<th class="js-sort {{#if searchSortBy 'username'}}is-sorting{{/if}}" data-sort="username">
<div class="table-fake-th"><span>{{_ "Username"}}</span> {{> icon icon=(sortIcon 'username') }}</div>
</th>
{{#if canViewOtherUserInfo}}
<th class="js-sort {{#if searchSortBy 'email'}}is-sorting{{/if}}" data-sort="email">
<div class="table-fake-th"><span>{{_ "Email"}}</span> {{> icon icon=(sortIcon 'email') }}</div>
</th>
{{#if $eq searchWorkspace 'all'}}
{{/if}}
{{#if $eq searchWorkspace 'external'}}
<th class="js-sort {{#if searchSortBy 'domain'}}is-sorting{{/if}}" data-sort="domain">
<div class="table-fake-th"><span>{{_ "Domain"}}</span> {{> icon icon=(sortIcon 'domain') }}</div>
</th>
@ -118,8 +120,10 @@
</div>
</td>
<td>{{username}}</td>
{{#if canViewOtherUserInfo}}
<td>{{email}}</td>
{{#if $eq searchWorkspace 'all'}}
{{/if}}
{{#if $eq searchWorkspace 'external'}}
<td>{{domain}}</td>
{{/if}}
<td>{{createdAt}}</td>

@ -1,6 +1,6 @@
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Tracker } from 'meteor/tracker';
import { hasAllPermission } from '../../../../authorization/client';
import { Template } from 'meteor/templating';
import _ from 'underscore';
import { timeAgo } from './helpers';
@ -187,6 +187,11 @@ Template.directory.helpers({
sortDirection.set('asc');
};
},
canViewOtherUserInfo() {
const { canViewOtherUserInfo } = Template.instance();
return canViewOtherUserInfo.get();
},
});
Template.directory.events({
@ -217,8 +222,7 @@ Template.directory.onRendered(function() {
return this.results.set(result);
}
Tracker.autorun(() => {
this.autorun(() => {
const searchConfig = {
text: this.searchText.get(),
workspace: this.searchWorkspace.get(),
@ -243,7 +247,7 @@ Template.directory.onRendered(function() {
// If there is no result, searching every workspace and
// the search text is an email address, try to find a federated user
if (this.searchWorkspace.get() === 'all' && this.searchText.get().indexOf('@') !== -1) {
if (this.searchWorkspace.get() === 'external' && this.searchText.get().indexOf('@') !== -1) {
const email = this.searchText.get();
Meteor.call('federationSearchUsers', email, (error, federatedUsers) => {
@ -297,6 +301,12 @@ Template.directory.onCreated(function() {
this.results = new ReactiveVar([]);
this.isLoading = new ReactiveVar(false);
this.canViewOtherUserInfo = new ReactiveVar(false);
this.autorun(() => {
this.canViewOtherUserInfo.set(hasAllPermission('view-full-other-user-info'));
});
});
Template.directory.onRendered(function() {

@ -1253,7 +1253,6 @@
"every_hour": "Once every hour",
"every_six_hours": "Once every six hours",
"every_day": "Once every day",
"Every_Workspace": "Every Workspace",
"Everyone_can_access_this_channel": "Everyone can access this channel",
"Example_s": "Example: <code class=\"inline\">%s</code>",
"Exclude_Botnames": "Exclude Bots",
@ -1265,6 +1264,7 @@
"Export_My_Data": "Export My Data",
"expression": "Expression",
"Extended": "Extended",
"External_Domains": "External Domains",
"External_Queue_Service_URL": "External Queue Service URL",
"External_Service": "External Service",
"Facebook_Page": "Facebook Page",
@ -1833,8 +1833,8 @@
"Loading...": "Loading...",
"Loading_more_from_history": "Loading more from history",
"Loading_suggestion": "Loading suggestions",
"Local_Domains": "Local Domains",
"Local_Password": "Local Password",
"Local_Workspace": "Local Workspace",
"Localization": "Localization",
"Log_Exceptions_to_Channel_Description": "A channel that will receive all captured exceptions. Leave empty to ignore exceptions.",
"Log_Exceptions_to_Channel": "Log Exceptions to Channel",

@ -52,7 +52,7 @@ Meteor.methods({
limit = limit > 0 ? limit : 10;
const options = {
const pagination = {
skip,
limit,
};
@ -66,7 +66,7 @@ Meteor.methods({
}
const result = Rooms.findByNameAndType(regex, 'c', {
...options,
...pagination,
sort,
fields: {
description: 1,
@ -90,36 +90,13 @@ Meteor.methods({
return;
}
let exceptions = [user.username];
// Get exceptions
if (type === 'users' && workspace === 'all') {
const nonFederatedUsers = Users.find({
$or: [
{ federation: { $exists: false } },
{ 'federation.peer': Federation.localIdentifier },
],
}, { fields: { username: 1 } }).map((u) => u.username);
exceptions = exceptions.concat(nonFederatedUsers);
} else if (type === 'users' && workspace === 'local') {
const federatedUsers = Users.find({
$and: [
{ federation: { $exists: true } },
{ 'federation.peer': { $ne: Federation.localIdentifier } },
],
}, { fields: { username: 1 } }).map((u) => u.username);
exceptions = exceptions.concat(federatedUsers);
}
const sort = sortUsers(sortBy, sortDirection);
const exceptions = [user.username];
const forcedSearchFields = workspace === 'all' && ['username', 'name', 'emails.address'];
const result = Users.findByActiveUsersExcept(text, exceptions, {
...options,
sort,
const options = {
...pagination,
sort: sortUsers(sortBy, sortDirection),
fields: {
username: 1,
name: 1,
@ -127,7 +104,16 @@ Meteor.methods({
emails: 1,
federation: 1,
},
}, forcedSearchFields);
};
let result;
if (workspace === 'all') {
result = Users.findByActiveUsersExcept(text, exceptions, options, forcedSearchFields);
} else if (workspace === 'external') {
result = Users.findByActiveExternalUsersExcept(text, exceptions, options, forcedSearchFields, Federation.localIdentifier);
} else {
result = Users.findByActiveLocalUsersExcept(text, exceptions, options, forcedSearchFields, Federation.localIdentifier);
}
return {
total: result.count(), // count ignores the `skip` and `limit` options

Loading…
Cancel
Save