2020-04-27 14:46:41 +08:00
|
|
|
import React, { Component, Fragment } from 'react';
|
2023-09-13 08:40:50 +08:00
|
|
|
import PropTypes from 'prop-types';
|
2022-12-29 12:21:47 +08:00
|
|
|
import { navigate } from '@gatsbyjs/reach-router';
|
2020-04-27 14:46:41 +08:00
|
|
|
import Nav from './org-users-nav';
|
|
|
|
import OrgUsersList from './org-users-list';
|
|
|
|
import MainPanelTopbar from './main-panel-topbar';
|
|
|
|
import ModalPortal from '../../components/modal-portal';
|
2022-11-10 13:27:55 +08:00
|
|
|
import ImportOrgUsersDialog from '../../components/dialog/org-import-users-dialog';
|
2020-11-02 13:56:35 +08:00
|
|
|
import AddOrgUserDialog from '../../components/dialog/org-add-user-dialog';
|
2020-04-27 14:46:41 +08:00
|
|
|
import InviteUserDialog from '../../components/dialog/org-admin-invite-user-dialog';
|
2023-12-07 22:50:39 +08:00
|
|
|
import InviteUserViaWeiXinDialog from '../../components/dialog/org-admin-invite-user-via-weixin-dialog';
|
2020-04-27 14:46:41 +08:00
|
|
|
import toaster from '../../components/toast';
|
|
|
|
import { seafileAPI } from '../../utils/seafile-api';
|
|
|
|
import OrgUserInfo from '../../models/org-user';
|
2023-12-07 22:50:39 +08:00
|
|
|
import { gettext, invitationLink, orgID, siteRoot, orgEnableAdminInviteUser} from '../../utils/constants';
|
2020-04-27 14:46:41 +08:00
|
|
|
import { Utils } from '../../utils/utils';
|
|
|
|
|
2021-07-22 11:54:10 +08:00
|
|
|
class Search extends React.Component {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
value: ''
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
handleInputChange = (e) => {
|
|
|
|
this.setState({
|
|
|
|
value: e.target.value
|
|
|
|
});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2021-07-22 11:54:10 +08:00
|
|
|
|
2023-11-14 20:25:25 +08:00
|
|
|
handleKeyDown = (e) => {
|
2021-07-22 11:54:10 +08:00
|
|
|
if (e.key == 'Enter') {
|
|
|
|
e.preventDefault();
|
|
|
|
this.handleSubmit();
|
|
|
|
}
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2021-07-22 11:54:10 +08:00
|
|
|
|
|
|
|
handleSubmit = () => {
|
|
|
|
const value = this.state.value.trim();
|
|
|
|
if (!value) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
this.props.submit(value);
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2021-07-22 11:54:10 +08:00
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<div className="input-icon">
|
|
|
|
<i className="d-flex input-icon-addon fas fa-search"></i>
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
className="form-control search-input h-6 mr-1"
|
|
|
|
style={{width: '15rem'}}
|
|
|
|
placeholder={this.props.placeholder}
|
|
|
|
value={this.state.value}
|
|
|
|
onChange={this.handleInputChange}
|
2023-11-14 20:25:25 +08:00
|
|
|
onKeyDown={this.handleKeyDown}
|
2021-07-22 11:54:10 +08:00
|
|
|
autoComplete="off"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-13 08:40:50 +08:00
|
|
|
Search.propTypes = {
|
|
|
|
placeholder: PropTypes.string.isRequired,
|
|
|
|
submit: PropTypes.func.isRequired,
|
|
|
|
};
|
|
|
|
|
2020-04-27 14:46:41 +08:00
|
|
|
class OrgUsers extends Component {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
orgUsers: [],
|
|
|
|
page: 1,
|
|
|
|
pageNext: false,
|
2020-11-02 13:56:35 +08:00
|
|
|
sortBy: '',
|
2020-04-27 14:46:41 +08:00
|
|
|
sortOrder: 'asc',
|
|
|
|
isShowAddOrgUserDialog: false,
|
2022-11-10 13:27:55 +08:00
|
|
|
isImportOrgUsersDialogOpen: false,
|
2023-12-07 22:50:39 +08:00
|
|
|
isInviteUserDialogOpen: false,
|
|
|
|
isInviteUserViaWeiXinDialogOpen: false
|
2020-04-27 14:46:41 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
let urlParams = (new URL(window.location)).searchParams;
|
|
|
|
const { page, sortBy, sortOrder } = this.state;
|
|
|
|
this.setState({
|
|
|
|
/*
|
|
|
|
perPage: parseInt(urlParams.get('per_page') || perPage),
|
|
|
|
currentPage: parseInt(urlParams.get('page') || currentPage),
|
|
|
|
*/
|
|
|
|
page: parseInt(urlParams.get('page') || page),
|
|
|
|
sortBy: urlParams.get('order_by') || sortBy,
|
|
|
|
sortOrder: urlParams.get('direction') || sortOrder
|
|
|
|
}, () => {
|
|
|
|
this.initOrgUsersData(this.state.page);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
sortByQuotaUsage = () => {
|
|
|
|
this.setState({
|
|
|
|
sortBy: 'quota_usage',
|
|
|
|
sortOrder: this.state.sortOrder == 'asc' ? 'desc' : 'asc',
|
|
|
|
page: 1
|
|
|
|
}, () => {
|
|
|
|
let url = new URL(location.href);
|
|
|
|
let searchParams = new URLSearchParams(url.search);
|
|
|
|
const { page, sortBy, sortOrder } = this.state;
|
|
|
|
searchParams.set('page', page);
|
|
|
|
searchParams.set('order_by', sortBy);
|
|
|
|
searchParams.set('direction', sortOrder);
|
|
|
|
url.search = searchParams.toString();
|
|
|
|
navigate(url.toString());
|
|
|
|
this.initOrgUsersData(page);
|
2020-11-02 13:56:35 +08:00
|
|
|
});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2020-04-27 14:46:41 +08:00
|
|
|
|
2022-11-10 13:27:55 +08:00
|
|
|
toggleImportOrgUsersDialog = () => {
|
|
|
|
this.setState({isImportOrgUsersDialogOpen: !this.state.isImportOrgUsersDialogOpen});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2022-11-10 13:27:55 +08:00
|
|
|
|
2020-04-27 14:46:41 +08:00
|
|
|
toggleAddOrgUser = () => {
|
|
|
|
this.setState({isShowAddOrgUserDialog: !this.state.isShowAddOrgUserDialog});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2020-04-27 14:46:41 +08:00
|
|
|
|
|
|
|
toggleInviteUserDialog = () => {
|
|
|
|
this.setState({isInviteUserDialogOpen: !this.state.isInviteUserDialogOpen});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2020-04-27 14:46:41 +08:00
|
|
|
|
2023-12-07 22:50:39 +08:00
|
|
|
toggleInviteUserViaWeiXinDialog = () => {
|
|
|
|
this.setState({isInviteUserViaWeiXinDialogOpen: !this.state.isInviteUserViaWeiXinDialogOpen});
|
|
|
|
}
|
|
|
|
|
2020-04-27 14:46:41 +08:00
|
|
|
initOrgUsersData = (page) => {
|
|
|
|
const { sortBy, sortOrder } = this.state;
|
|
|
|
seafileAPI.orgAdminListOrgUsers(orgID, '', page, sortBy, sortOrder).then(res => {
|
|
|
|
let userList = res.data.user_list.map(item => {
|
|
|
|
return new OrgUserInfo(item);
|
|
|
|
});
|
|
|
|
this.setState({
|
|
|
|
orgUsers: userList,
|
|
|
|
pageNext: res.data.page_next,
|
|
|
|
page: res.data.page,
|
|
|
|
});
|
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
|
|
|
});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2020-04-27 14:46:41 +08:00
|
|
|
|
2022-11-10 13:27:55 +08:00
|
|
|
importOrgUsers = (file) => {
|
|
|
|
toaster.notify(gettext('It may take some time, please wait.'));
|
|
|
|
seafileAPI.orgAdminImportUsersViaFile(orgID, file).then((res) => {
|
|
|
|
if (res.data.success.length) {
|
|
|
|
const users = res.data.success.map(item => {
|
|
|
|
if (item.institution == undefined) {
|
|
|
|
item.institution = '';
|
|
|
|
}
|
|
|
|
return new OrgUserInfo(item);
|
|
|
|
});
|
|
|
|
this.setState({
|
|
|
|
orgUsers: users.concat(this.state.orgUsers)
|
|
|
|
});
|
|
|
|
}
|
2023-09-13 08:40:50 +08:00
|
|
|
res.data.failed.forEach(item => {
|
2022-11-10 13:27:55 +08:00
|
|
|
const msg = `${item.email}: ${item.error_msg}`;
|
|
|
|
toaster.danger(msg);
|
|
|
|
});
|
|
|
|
}).catch((error) => {
|
|
|
|
let errMsg = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMsg);
|
|
|
|
});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2022-11-10 13:27:55 +08:00
|
|
|
|
2020-04-27 14:46:41 +08:00
|
|
|
addOrgUser = (email, name, password) => {
|
|
|
|
seafileAPI.orgAdminAddOrgUser(orgID, email, name, password).then(res => {
|
|
|
|
let userInfo = new OrgUserInfo(res.data);
|
|
|
|
this.state.orgUsers.unshift(userInfo);
|
|
|
|
this.setState({
|
2020-11-02 13:56:35 +08:00
|
|
|
orgUsers: this.state.orgUsers
|
2020-04-27 14:46:41 +08:00
|
|
|
});
|
|
|
|
this.toggleAddOrgUser();
|
|
|
|
let msg = gettext('successfully added user %s.');
|
|
|
|
msg = msg.replace('%s', email);
|
|
|
|
toaster.success(msg);
|
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
|
|
|
this.toggleAddOrgUser();
|
|
|
|
});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2020-04-27 14:46:41 +08:00
|
|
|
|
2023-09-19 14:42:15 +08:00
|
|
|
toggleOrgUsersDelete = (email, username) => {
|
2020-04-27 14:46:41 +08:00
|
|
|
seafileAPI.orgAdminDeleteOrgUser(orgID, email).then(res => {
|
|
|
|
let users = this.state.orgUsers.filter(item => item.email != email);
|
|
|
|
this.setState({orgUsers: users});
|
2023-09-19 14:42:15 +08:00
|
|
|
let msg = gettext('Deleted user %s');
|
|
|
|
msg = msg.replace('%s', username);
|
2020-04-27 14:46:41 +08:00
|
|
|
toaster.success(msg);
|
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
|
|
|
});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2020-04-27 14:46:41 +08:00
|
|
|
|
2023-12-07 22:50:39 +08:00
|
|
|
inviteOrgUser = (emails) => {
|
|
|
|
seafileAPI.orgAdminInviteOrgUsers(orgID, emails.split(',')).then(res => {
|
|
|
|
this.toggleInviteUserDialog();
|
|
|
|
let users = res.data.success.map(user => {
|
|
|
|
return new OrgUserInfo(user);
|
|
|
|
});
|
|
|
|
this.setState({
|
|
|
|
orgUsers: users.concat(this.state.orgUsers)
|
|
|
|
});
|
|
|
|
|
|
|
|
res.data.success.map(item => {
|
|
|
|
let msg = gettext('successfully sent email to %s.');
|
|
|
|
msg = msg.replace('%s', item.email);
|
|
|
|
toaster.success(msg);
|
|
|
|
});
|
|
|
|
|
|
|
|
res.data.failed.map(item => {
|
|
|
|
const msg = `${item.email}: ${item.error_msg}`;
|
|
|
|
toaster.danger(msg);
|
|
|
|
});
|
|
|
|
}).catch(error => {
|
|
|
|
this.toggleInviteUserDialog();
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2022-10-19 19:08:02 +08:00
|
|
|
changeStatus= (email, isActive) => {
|
|
|
|
seafileAPI.orgAdminChangeOrgUserStatus(orgID, email, isActive).then(res => {
|
|
|
|
let users = this.state.orgUsers.map(item => {
|
|
|
|
if (item.email == email) {
|
|
|
|
item['is_active']= res.data['is_active'];
|
|
|
|
}
|
|
|
|
return item;
|
|
|
|
});
|
|
|
|
this.setState({orgUsers: users});
|
|
|
|
toaster.success(gettext('Edit succeeded.'));
|
|
|
|
}).catch(error => {
|
|
|
|
let errMessage = Utils.getErrorMsg(error);
|
|
|
|
toaster.danger(errMessage);
|
|
|
|
});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2022-10-19 19:08:02 +08:00
|
|
|
|
2021-07-22 11:54:10 +08:00
|
|
|
searchItems = (keyword) => {
|
|
|
|
navigate(`${siteRoot}org/useradmin/search-users/?query=${encodeURIComponent(keyword)}`);
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2021-07-22 11:54:10 +08:00
|
|
|
|
|
|
|
getSearch = () => {
|
|
|
|
return <Search
|
|
|
|
placeholder={gettext('Search users')}
|
|
|
|
submit={this.searchItems}
|
|
|
|
/>;
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2021-07-22 11:54:10 +08:00
|
|
|
|
2020-04-27 14:46:41 +08:00
|
|
|
render() {
|
|
|
|
const topBtn = 'btn btn-secondary operation-item';
|
|
|
|
let topbarChildren;
|
|
|
|
topbarChildren = (
|
|
|
|
<Fragment>
|
2023-12-07 22:50:39 +08:00
|
|
|
<button className="btn btn-secondary operation-item" onClick={this.toggleImportOrgUsersDialog}>{gettext('Import users')}</button>
|
|
|
|
<button className={topBtn} title={gettext('Add user')} onClick={this.toggleAddOrgUser}>
|
|
|
|
<i className="fas fa-plus-square text-secondary mr-1"></i>{gettext('Add user')}</button>
|
|
|
|
{orgEnableAdminInviteUser &&
|
|
|
|
<button className={topBtn} title={gettext('Invite users')} onClick={this.toggleInviteUserDialog}>
|
|
|
|
<i className="fas fa-plus-square text-secondary mr-1"></i>{gettext('Invite users')}</button>
|
|
|
|
}
|
2020-04-27 14:46:41 +08:00
|
|
|
{invitationLink &&
|
2023-12-07 22:50:39 +08:00
|
|
|
<button className={topBtn} title={'通过微信邀请用户'} onClick={this.toggleInviteUserViaWeiXinDialog}>
|
|
|
|
<i className="fas fa-plus-square text-secondary mr-1"></i>{'通过微信邀请用户'}</button>
|
2020-04-27 14:46:41 +08:00
|
|
|
}
|
2022-11-10 13:27:55 +08:00
|
|
|
{this.state.isImportOrgUsersDialogOpen &&
|
|
|
|
<ModalPortal>
|
|
|
|
<ImportOrgUsersDialog importUsersInBatch={this.importOrgUsers} toggle={this.toggleImportOrgUsersDialog}/>
|
|
|
|
</ModalPortal>
|
|
|
|
}
|
2020-04-27 14:46:41 +08:00
|
|
|
{this.state.isShowAddOrgUserDialog &&
|
|
|
|
<ModalPortal>
|
|
|
|
<AddOrgUserDialog handleSubmit={this.addOrgUser} toggle={this.toggleAddOrgUser}/>
|
|
|
|
</ModalPortal>
|
|
|
|
}
|
|
|
|
{this.state.isInviteUserDialogOpen &&
|
|
|
|
<ModalPortal>
|
2023-12-07 22:50:39 +08:00
|
|
|
<InviteUserDialog handleSubmit={this.inviteOrgUser} toggle={this.toggleInviteUserDialog}/>
|
|
|
|
</ModalPortal>
|
|
|
|
}
|
|
|
|
{this.state.isInviteUserViaWeiXinDialogOpen &&
|
|
|
|
<ModalPortal>
|
|
|
|
<InviteUserViaWeiXinDialog invitationLink={invitationLink} toggle={this.toggleInviteUserViaWeiXinDialog}/>
|
2020-04-27 14:46:41 +08:00
|
|
|
</ModalPortal>
|
|
|
|
}
|
|
|
|
</Fragment>
|
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Fragment>
|
2021-07-22 11:54:10 +08:00
|
|
|
<MainPanelTopbar children={topbarChildren} search={this.getSearch()}/>
|
2020-04-27 14:46:41 +08:00
|
|
|
<div className="main-panel-center flex-row">
|
|
|
|
<div className="cur-view-container">
|
|
|
|
<Nav currentItem="all" />
|
|
|
|
<OrgUsersList
|
|
|
|
initOrgUsersData={this.initOrgUsersData}
|
|
|
|
toggleDelete={this.toggleOrgUsersDelete}
|
2022-10-19 19:08:02 +08:00
|
|
|
changeStatus={this.changeStatus}
|
2020-04-27 14:46:41 +08:00
|
|
|
orgUsers={this.state.orgUsers}
|
|
|
|
page={this.state.page}
|
|
|
|
pageNext={this.state.pageNext}
|
|
|
|
sortBy={this.state.sortBy}
|
|
|
|
sortOrder={this.state.sortOrder}
|
|
|
|
sortByQuotaUsage={this.sortByQuotaUsage}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</Fragment>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default OrgUsers;
|