* improve performance on sendNotifications * Fix SQL queries * Remove get direct profiles, not needed anymore * Add raw data to error details if AppError fails to decode * men * Fix decode (#4052) * Fixing json decode * Adding unit test * Initial work for client scaling (#4051) * Begin adding paging to profiles API * Added more paging functionality * Finish hooking up admin console user lists * Add API for searching users and add searching to all user lists * Add lazy loading of profiles * Revert config.json * Fix unit tests and some style issues * Add GetProfilesFromList to Go driver and fix web unit test * Update etag for GetProfiles * Updating ui for filters and pagination (#4044) * Updating UI for pagination * Adjusting margins for filter row * Adjusting margin for specific modals * Adding relative padding to system console * Adjusting responsive view * Update client user tests * Minor fixes for direct messages modal (#4056) * Remove some unneeded initial load calls (#4057) * UX updates to user lists, added smart counts and bug fixes (#4059) * Improved getExplicitMentions and unit tests (#4064) * Refactor getting posts to lazy load profiles correctly (#4062) * Comment out SetActiveChannel test (#4066) * Profiler cpu, block, and memory profiler. (#4081) * Fix TestSetActiveChannel unit test (#4071) * Fixing build failure caused by dependancies updating (#4076) * Adding profiler * Fix admin_team_member_dropdown eslint errors * Bumping session cache size (#4077) * Bumping session cache size * Bumping status cache * Refactor how the client handles channel members to be large team friendly (#4106) * Refactor how the client handles channel members to be large team friendly * Change Id to ChannelId in ChannelStats model * Updated getChannelMember and getProfilesByIds routes to match proposal * Performance improvements (#4100) * Performance improvements * Fixing re-connect issue * Fixing error message * Some other minor perf tweaks * Some other minor perf tweaks * Fixing config file * Fixing buffer size * Fixing web socket send message * adding some error logging * fix getMe to be user required * Fix websocket event for new user * Fixing shutting down * Reverting web socket changes * Fixing logging lvl * Adding caching to GetMember * Adding some logging * Fixing caching * Fixing caching invalidate * Fixing direct message caching * Fixing caching * Fixing caching * Remove GetDirectProfiles from initial load * Adding logging and fixing websocket client * Adding back caching from bad merge. * Explicitly close go driver requests (#4162) * Refactored how the client handles team members to be more large team friendly (#4159) * Refactor getProfilesForDirectMessageList API into getAllProfiles API * Refactored how the client handles team members to be more large team friendly * Fix js error when receiving a notification * Fix JS error caused by current user being overwritten with sanitized version (#4165) * Adding error message to status failure (#4167) * Fix a few bugs caused by client scaling refactoring (#4170) * When there is no read replica, don't open a second set of connections to the master database (#4173) * Adding connection tacking to stats (#4174) * Reduce DB writes for statuses and other status related changes (#4175) * Fix bug preventing opening of DM channels from more modal (#4181) * Fixing socket timing error (#4183) * Fixing ping/pong handler * Fixing socket timing error * Commenting out status broadcasting * Removing user status changes * Removing user status changes * Removing user status changes * Removing user status changes * Adding DoPreComputeJson() * Performance improvements (#4194) * * Fix System Console Analytics queries * Add db.SetConnMaxLifetime to 15 minutes * Add "net/http/pprof" for profiling * Add FreeOSMemory() to manually release memory on reload config * Add flag to enable http profiler * Fix memory leak (#4197) * Fix memory leak * removed unneeded nil assignment * Fixing go routine leak (#4208) * Merge fixes * Merge fix * Refactored statuses to be queried by the client rather than broadcast by the server (#4212) * Refactored server code to reduce status broadcasts and to allow getting statuses by IDs * Refactor client code to periodically fetch statuses * Add store unit test for getting statuses by ids * Fix status unit test * Add getStatusesByIds REST API and move the client over to use that instead of the WebSocket * Adding multiple threads to websocket hub (#4230) * Adding multiple threads to websocket hub * Fixing unit tests * Fixing so websocket connections from the same user end up in the same… (#4240) * Fixing so websocket connections from the same user end up in the same list * Removing old comment * Refactor user autocomplete to query the server (#4239) * Add API for autocompleting users * Converted at mention autocomplete to query server * Converted user search autocomplete to query server * Switch autocomplete API naming to use term instead of username * Split autocomplete API into two, one for channels and for teams * Fix copy/paste error * Some final client scaling fixes (#4246) * Add lazy loading of profiles to integration pages * Add lazy loading of profiles to emoji page * Fix JS error when receiving post in select team menu and also clean up channel store
273 строки
9.5 KiB
JavaScript
273 строки
9.5 KiB
JavaScript
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
|
// See License.txt for license information.
|
|
|
|
import SearchableUserList from 'components/searchable_user_list.jsx';
|
|
import AdminTeamMembersDropdown from './admin_team_members_dropdown.jsx';
|
|
import ResetPasswordModal from './reset_password_modal.jsx';
|
|
import FormError from 'components/form_error.jsx';
|
|
|
|
import AdminStore from 'stores/admin_store.jsx';
|
|
import TeamStore from 'stores/team_store.jsx';
|
|
import UserStore from 'stores/user_store.jsx';
|
|
|
|
import {searchUsers, loadProfilesAndTeamMembers, loadTeamMembersForProfilesList} from 'actions/user_actions.jsx';
|
|
import {getTeamStats} from 'utils/async_client.jsx';
|
|
|
|
import Constants from 'utils/constants.jsx';
|
|
import * as Utils from 'utils/utils.jsx';
|
|
|
|
import React from 'react';
|
|
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
|
|
|
|
const USERS_PER_PAGE = 50;
|
|
|
|
export default class UserList extends React.Component {
|
|
static get propTypes() {
|
|
return {
|
|
params: React.PropTypes.object.isRequired
|
|
};
|
|
}
|
|
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.onAllTeamsChange = this.onAllTeamsChange.bind(this);
|
|
this.onStatsChange = this.onStatsChange.bind(this);
|
|
this.onUsersChange = this.onUsersChange.bind(this);
|
|
this.onTeamChange = this.onTeamChange.bind(this);
|
|
|
|
this.doPasswordReset = this.doPasswordReset.bind(this);
|
|
this.doPasswordResetDismiss = this.doPasswordResetDismiss.bind(this);
|
|
this.doPasswordResetSubmit = this.doPasswordResetSubmit.bind(this);
|
|
this.nextPage = this.nextPage.bind(this);
|
|
this.search = this.search.bind(this);
|
|
this.loadComplete = this.loadComplete.bind(this);
|
|
|
|
const stats = TeamStore.getStats(this.props.params.team);
|
|
|
|
this.state = {
|
|
team: AdminStore.getTeam(this.props.params.team),
|
|
users: [],
|
|
teamMembers: TeamStore.getMembersInTeam(this.props.params.team),
|
|
total: stats.member_count,
|
|
serverError: null,
|
|
showPasswordModal: false,
|
|
loading: true,
|
|
user: null
|
|
};
|
|
}
|
|
|
|
componentDidMount() {
|
|
AdminStore.addAllTeamsChangeListener(this.onAllTeamsChange);
|
|
UserStore.addInTeamChangeListener(this.onUsersChange);
|
|
TeamStore.addChangeListener(this.onTeamChange);
|
|
TeamStore.addStatsChangeListener(this.onStatsChange);
|
|
|
|
loadProfilesAndTeamMembers(0, Constants.PROFILE_CHUNK_SIZE, this.props.params.team, this.loadComplete);
|
|
getTeamStats(this.props.params.team);
|
|
}
|
|
|
|
componentWillReceiveProps(nextProps) {
|
|
if (nextProps.params.team !== this.props.params.team) {
|
|
const stats = TeamStore.getStats(nextProps.params.team);
|
|
this.setState({
|
|
team: AdminStore.getTeam(nextProps.params.team),
|
|
users: [],
|
|
teamMembers: TeamStore.getMembersInTeam(nextProps.params.team),
|
|
total: stats.member_count
|
|
});
|
|
|
|
this.getTeamProfiles(nextProps.params.team);
|
|
}
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
AdminStore.removeAllTeamsChangeListener(this.onAllTeamsChange);
|
|
UserStore.removeInTeamChangeListener(this.onUsersChange);
|
|
TeamStore.removeChangeListener(this.onTeamChange);
|
|
TeamStore.removeStatsChangeListener(this.onStatsChange);
|
|
}
|
|
|
|
loadComplete() {
|
|
this.setState({loading: false});
|
|
}
|
|
|
|
onAllTeamsChange() {
|
|
this.setState({
|
|
team: AdminStore.getTeam(this.props.params.team)
|
|
});
|
|
}
|
|
|
|
onStatsChange() {
|
|
const stats = TeamStore.getStats(this.props.params.team);
|
|
this.setState({total: stats.member_count});
|
|
}
|
|
|
|
onUsersChange() {
|
|
this.setState({users: UserStore.getProfileListInTeam(this.props.params.team)});
|
|
}
|
|
|
|
onTeamChange() {
|
|
this.setState({teamMembers: TeamStore.getMembersInTeam(this.props.params.team)});
|
|
}
|
|
|
|
nextPage(page) {
|
|
loadProfilesAndTeamMembers((page + 1) * USERS_PER_PAGE, USERS_PER_PAGE, this.props.params.team);
|
|
}
|
|
|
|
doPasswordReset(user) {
|
|
this.setState({
|
|
showPasswordModal: true,
|
|
user
|
|
});
|
|
}
|
|
|
|
doPasswordResetDismiss() {
|
|
this.setState({
|
|
showPasswordModal: false,
|
|
user: null
|
|
});
|
|
}
|
|
|
|
doPasswordResetSubmit() {
|
|
this.getCurrentTeamProfiles();
|
|
this.setState({
|
|
showPasswordModal: false,
|
|
user: null
|
|
});
|
|
}
|
|
|
|
search(term) {
|
|
if (term === '') {
|
|
this.setState({search: false, users: UserStore.getProfileListInTeam(this.props.params.team)});
|
|
return;
|
|
}
|
|
|
|
searchUsers(
|
|
term,
|
|
this.props.params.team,
|
|
{},
|
|
(users) => {
|
|
this.setState({loading: true, search: true, users});
|
|
loadTeamMembersForProfilesList(users, this.props.params.team, this.loadComplete);
|
|
}
|
|
);
|
|
}
|
|
|
|
render() {
|
|
if (!this.state.team) {
|
|
return null;
|
|
}
|
|
|
|
const teamMembers = this.state.teamMembers;
|
|
const users = this.state.users;
|
|
const actionUserProps = {};
|
|
const extraInfo = {};
|
|
const mfaEnabled = global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.MFA === 'true' && global.window.mm_config.EnableMultifactorAuthentication === 'true';
|
|
|
|
let usersToDisplay;
|
|
if (this.state.loading) {
|
|
usersToDisplay = null;
|
|
} else {
|
|
usersToDisplay = [];
|
|
|
|
for (let i = 0; i < users.length; i++) {
|
|
const user = users[i];
|
|
|
|
if (teamMembers[user.id]) {
|
|
usersToDisplay.push(user);
|
|
actionUserProps[user.id] = {
|
|
teamMember: teamMembers[user.id]
|
|
};
|
|
|
|
const info = [];
|
|
|
|
if (user.auth_service) {
|
|
const service = (user.auth_service === Constants.LDAP_SERVICE || user.auth_service === Constants.SAML_SERVICE) ? user.auth_service.toUpperCase() : Utils.toTitleCase(user.auth_service);
|
|
info.push(
|
|
<FormattedHTMLMessage
|
|
id='admin.user_item.authServiceNotEmail'
|
|
defaultMessage='<strong>Sign-in Method:</strong> {service}'
|
|
values={{
|
|
service
|
|
}}
|
|
/>
|
|
);
|
|
} else {
|
|
info.push(
|
|
<FormattedHTMLMessage
|
|
id='admin.user_item.authServiceEmail'
|
|
defaultMessage='<strong>Sign-in Method:</strong> Email'
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (mfaEnabled) {
|
|
if (user.mfa_active) {
|
|
info.push(
|
|
<FormattedHTMLMessage
|
|
id='admin.user_item.mfaYes'
|
|
defaultMessage='<strong>MFA</strong>: Yes'
|
|
/>
|
|
);
|
|
} else {
|
|
info.push(
|
|
<FormattedHTMLMessage
|
|
id='admin.user_item.mfaNo'
|
|
defaultMessage='<strong>MFA</strong>: No'
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
extraInfo[user.id] = info;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className='wrapper--fixed'>
|
|
<h3>
|
|
<FormattedMessage
|
|
id='admin.userList.title2'
|
|
defaultMessage='Users for {team} ({count})'
|
|
values={{
|
|
team: this.state.team.name,
|
|
count: this.state.total
|
|
}}
|
|
/>
|
|
</h3>
|
|
<FormError error={this.state.serverError}/>
|
|
<form
|
|
className='form-horizontal'
|
|
role='form'
|
|
>
|
|
<div className='more-modal__list member-list-holder'>
|
|
<SearchableUserList
|
|
users={usersToDisplay}
|
|
usersPerPage={USERS_PER_PAGE}
|
|
total={this.state.total}
|
|
extraInfo={extraInfo}
|
|
nextPage={this.nextPage}
|
|
search={this.search}
|
|
actions={[AdminTeamMembersDropdown]}
|
|
actionProps={{
|
|
refreshProfiles: this.getCurrentTeamProfiles,
|
|
doPasswordReset: this.doPasswordReset
|
|
}}
|
|
actionUserProps={actionUserProps}
|
|
/>
|
|
</div>
|
|
</form>
|
|
<ResetPasswordModal
|
|
user={this.state.user}
|
|
show={this.state.showPasswordModal}
|
|
team={this.state.team}
|
|
onModalSubmit={this.doPasswordResetSubmit}
|
|
onModalDismissed={this.doPasswordResetDismiss}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
}
|