diff --git a/frontend/src/pages/sys-admin/index.js b/frontend/src/pages/sys-admin/index.js
index 97600abcbb..eb5eaeef92 100644
--- a/frontend/src/pages/sys-admin/index.js
+++ b/frontend/src/pages/sys-admin/index.js
@@ -16,6 +16,7 @@ import Users from './users/users';
import AdminUsers from './users/admin-users';
import LDAPImportedUsers from './users/ldap-imported-users';
import LDAPUsers from './users/ldap-users';
+import SearchUsers from './users/search-users';
import User from './users/user-info';
import UserOwnedRepos from './users/user-repos';
import UserSharedRepos from './users/user-shared-repos';
@@ -108,7 +109,7 @@ class SysAdmin extends React.Component {
},
{
tab: 'users',
- urlPartList: ['users/']
+ urlPartList: ['users/', 'search-users/']
},
{
tab: 'groups',
@@ -206,6 +207,7 @@ class SysAdmin extends React.Component {
+
diff --git a/frontend/src/pages/sys-admin/users/search-users.js b/frontend/src/pages/sys-admin/users/search-users.js
new file mode 100644
index 0000000000..d852d6dc60
--- /dev/null
+++ b/frontend/src/pages/sys-admin/users/search-users.js
@@ -0,0 +1,341 @@
+import React, { Component, Fragment } from 'react';
+import { Button, Form, FormGroup, Input, Col } from 'reactstrap';
+import { Utils } from '../../../utils/utils';
+import { seafileAPI } from '../../../utils/seafile-api';
+import { gettext, loginUrl } from '../../../utils/constants';
+import toaster from '../../../components/toast';
+import SysAdminUserSetQuotaDialog from '../../../components/dialog/sysadmin-dialog/set-quota';
+import CommonOperationConfirmationDialog from '../../../components/dialog/common-operation-confirmation-dialog';
+import MainPanelTopbar from '../main-panel-topbar';
+import Content from './users-content';
+
+
+class SearchUsers extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ query: '',
+ isSubmitBtnActive: false,
+ loading: true,
+ errorMsg: '',
+ userList: [],
+ hasUserSelected: false,
+ selectedUserList: [],
+ isAllUsersSelected: false,
+ isBatchSetQuotaDialogOpen: false,
+ isBatchDeleteUserDialogOpen: false
+ };
+ }
+
+ componentDidMount () {
+ let params = (new URL(document.location)).searchParams;
+ this.setState({
+ query: params.get('query') || ''
+ }, this.getItems);
+ }
+
+ toggleBatchSetQuotaDialog = () => {
+ this.setState({isBatchSetQuotaDialogOpen: !this.state.isBatchSetQuotaDialogOpen});
+ }
+
+ toggleBatchDeleteUserDialog = () => {
+ this.setState({isBatchDeleteUserDialogOpen: !this.state.isBatchDeleteUserDialogOpen});
+ }
+
+ onUserSelected = (item) => {
+ let hasUserSelected = false;
+ let selectedUserList = [];
+ // traverse all users, toggle its selected status
+ let users = this.state.userList.map(user => {
+ // toggle status
+ if (user.email === item.email) {
+ user.isSelected = !user.isSelected;
+ }
+ // update selectedUserList
+ // if current user is now selected, push it to selectedUserList
+ // if current user is now not selected, drop it from selectedUserList
+ if (user.isSelected == true) {
+ hasUserSelected = true;
+ selectedUserList.push(user);
+ } else {
+ selectedUserList = selectedUserList.filter(thisuser => {
+ return thisuser.email != user.email;
+ });
+ }
+ return user;
+ });
+ // finally update state
+ this.setState({
+ userList: users,
+ hasUserSelected: hasUserSelected,
+ selectedUserList: selectedUserList,
+ });
+ }
+
+ toggleSelectAllUsers = () => {
+ if (this.state.isAllUsersSelected) {
+ // if previous state is allSelected, toggle to not select
+ let users = this.state.userList.map(user => {
+ user.isSelected = false;
+ return user;
+ });
+ this.setState({
+ userList: users,
+ hasUserSelected: false,
+ isAllUsersSelected: false,
+ selectedUserList: [],
+ });
+ } else {
+ // if previous state is not allSelected, toggle to selectAll
+ let users = this.state.userList.map(user => {
+ user.isSelected = true;
+ return user;
+ });
+ this.setState({
+ userList: users,
+ hasUserSelected: true,
+ isAllUsersSelected: true,
+ selectedUserList: users
+ });
+ }
+ }
+
+ getItems = () => {
+ seafileAPI.sysAdminSearchUsers(this.state.query.trim()).then(res => {
+ this.setState({
+ userList: res.data.user_list,
+ loading: false
+ });
+ }).catch((error) => {
+ if (error.response) {
+ if (error.response.status == 403) {
+ this.setState({
+ loading: false,
+ errorMsg: gettext('Permission denied')
+ });
+ location.href = `${loginUrl}?next=${encodeURIComponent(location.href)}`;
+ } else {
+ this.setState({
+ loading: false,
+ errorMsg: gettext('Error')
+ });
+ }
+ } else {
+ this.setState({
+ loading: false,
+ errorMsg: gettext('Please check the network.')
+ });
+ }
+ });
+ }
+
+ deleteUser = (email) => {
+ seafileAPI.sysAdminDeleteUser(email).then(res => {
+ let newUserList = this.state.userList.filter(item => {
+ return item.email != email;
+ });
+ this.setState({userList: newUserList});
+ toaster.success(gettext('Successfully deleted 1 item.'));
+ }).catch((error) => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+
+ setUserQuotaInBatch = (quotaTotal) => {
+ let emails = this.state.selectedUserList.map(user => {
+ return user.email;
+ });
+ seafileAPI.sysAdminSetUserQuotaInBatch(emails, quotaTotal).then(res => {
+ let userList = this.state.userList.map(item => {
+ res.data.success.map(resultUser => {
+ if (item.email == resultUser.email) {
+ item.quota_total = resultUser.quota_total;
+ }
+ });
+ return item;
+ });
+ this.setState({userList: userList});
+ }).catch((error) => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+
+ deleteUserInBatch = () => {
+ let emails = this.state.selectedUserList.map(user => {
+ return user.email;
+ });
+ seafileAPI.sysAdminDeleteUserInBatch(emails).then(res => {
+ if (res.data.success.length) {
+ let oldUserList = this.state.userList;
+ let newUserList = oldUserList.filter(oldUser => {
+ return !res.data.success.some(deletedUser =>{
+ return deletedUser.email == oldUser.email;
+ });
+ });
+ this.setState({
+ userList: newUserList,
+ hasUserSelected: emails.length != res.data.success.length
+ });
+ const length = res.data.success.length;
+ const msg = length == 1 ?
+ gettext('Successfully deleted 1 user.') :
+ gettext('Successfully deleted {user_number_placeholder} users.')
+ .replace('{user_number_placeholder}', length);
+ toaster.success(msg);
+ }
+ res.data.failed.map(item => {
+ const msg = `${item.email}: ${item.error_msg}`;
+ toaster.danger(msg);
+ });
+ }).catch((error) => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+
+ updateUser = (email, key, value) => {
+ seafileAPI.sysAdminUpdateUser(email, key, value).then(res => {
+ let newUserList = this.state.userList.map(item => {
+ if (item.email == email) {
+ item[key]= res.data[key];
+ }
+ return item;
+ });
+ this.setState({userList: newUserList});
+ const msg = (key == 'is_active' && value) ?
+ res.data.update_status_tip : gettext('Edit succeeded');
+ toaster.success(msg);
+ }).catch((error) => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+
+ updateAdminRole = (email, role) => {
+ seafileAPI.sysAdminUpdateAdminRole(email, role).then(res => {
+ let newUserList = this.state.userList.map(item => {
+ if (item.email == email) {
+ item.admin_role = res.data.role;
+ }
+ return item;
+ });
+ this.setState({userList: newUserList});
+ toaster.success(gettext('Edit succeeded'));
+ }).catch((error) => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+
+ revokeAdmin = (email, name) => {
+ seafileAPI.sysAdminUpdateUser(email, 'is_staff', false).then(res => {
+ let userList = this.state.userList.filter(item => {
+ return item.email != email;
+ });
+ this.setState({
+ userList: userList
+ });
+ toaster.success(gettext('Successfully revoked the admin permission of {placeholder}'.replace('{placeholder}', name)));
+ }).catch((error) => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+
+ handleInputChange = (e) => {
+ this.setState({
+ query: e.target.value
+ }, this.checkSubmitBtnActive);
+ }
+
+ checkSubmitBtnActive = () => {
+ const { email } = this.state;
+ this.setState({
+ isSubmitBtnActive: email.trim()
+ });
+ }
+
+ render() {
+ const { query, isSubmitBtnActive } = this.state;
+ const {
+ hasUserSelected,
+ isBatchDeleteUserDialogOpen,
+ isBatchSetQuotaDialogOpen
+ } = this.state;
+ return (
+
+ {hasUserSelected ?
+
+
+
+
+
+ :
+
+ }
+
+
+
+
{gettext('Users')}
+
+
+
+
{gettext('Search Users')}
+
+
+
+
{gettext('Result')}
+
+
+
+
+
+ {isBatchSetQuotaDialogOpen &&
+
+ }
+ {isBatchDeleteUserDialogOpen &&
+
+ }
+
+ );
+ }
+}
+
+export default SearchUsers;
diff --git a/frontend/src/pages/sys-admin/users/users-content.js b/frontend/src/pages/sys-admin/users/users-content.js
new file mode 100644
index 0000000000..f3606ab60e
--- /dev/null
+++ b/frontend/src/pages/sys-admin/users/users-content.js
@@ -0,0 +1,452 @@
+import React, { Component, Fragment } from 'react';
+import moment from 'moment';
+import { Link } from '@reach/router';
+import { Utils } from '../../../utils/utils';
+import { seafileAPI } from '../../../utils/seafile-api';
+import { isPro, username, gettext, multiInstitution, siteRoot } from '../../../utils/constants';
+import toaster from '../../../components/toast';
+import EmptyTip from '../../../components/empty-tip';
+import Loading from '../../../components/loading';
+import Paginator from '../../../components/paginator';
+import SysAdminUserStatusEditor from '../../../components/select-editor/sysadmin-user-status-editor';
+import SysAdminUserRoleEditor from '../../../components/select-editor/sysadmin-user-role-editor';
+import SelectEditor from '../../../components/select-editor/select-editor';
+import SysAdminUserSetQuotaDialog from '../../../components/dialog/sysadmin-dialog/set-quota';
+import CommonOperationConfirmationDialog from '../../../components/dialog/common-operation-confirmation-dialog';
+import UserLink from '../user-link';
+import OpMenu from './user-op-menu';
+
+const { availableRoles, availableAdminRoles, institutions } = window.sysadmin.pageOptions;
+
+class Content extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ isItemFreezed: false
+ };
+ }
+
+ onFreezedItem = () => {
+ this.setState({isItemFreezed: true});
+ }
+
+ onUnfreezedItem = () => {
+ this.setState({isItemFreezed: false});
+ }
+
+ getPreviousPage = () => {
+ this.props.getListByPage(this.props.currentPage - 1);
+ }
+
+ getNextPage = () => {
+ this.props.getListByPage(this.props.currentPage + 1);
+ }
+
+ render() {
+ const { isAdmin, loading, errorMsg, items, isAllUsersSelected, curPerPage, hasNextPage, currentPage } = this.props;
+ if (loading) {
+ return ;
+ } else if (errorMsg) {
+ return
{errorMsg}
;
+ } else {
+ const emptyTip = (
+
+ {gettext('No users')}
+
+ );
+
+ let columns = [];
+ const colNameText = `${gettext('Name')} / ${gettext('Contact Email')}`;
+ const colSpaceText = `${gettext('Space Used')} / ${gettext('Quota')}`;
+ const colCreatedText = `${gettext('Created At')} / ${gettext('Last Login')}`;
+ if (isPro) {
+ columns.push(
+ {width: '20%', text: colNameText},
+ {width: '15%', text: gettext('Status')},
+ {width: '15%', text: gettext('Role')}
+ );
+ } else {
+ columns.push(
+ {width: '30%', text: colNameText},
+ {width: '20%', text: gettext('Status')}
+ );
+ }
+ if (multiInstitution && !isAdmin) {
+ columns.push(
+ {width: '14%', text: colSpaceText},
+ {width: '14%', text: gettext('Institution')},
+ {width: '14%', text: colCreatedText},
+ {width: '5%', text: ''}
+ );
+ } else {
+ columns.push(
+ {width: '20%', text: colSpaceText},
+ {width: '22%', text: colCreatedText},
+ {width: '5%', text: ''}
+ );
+ }
+
+ const table = (
+
+
+ {(!this.props.isAdmin && !this.props.isSearchResult) &&
+
+ }
+
+ );
+
+ return items.length ? table : emptyTip;
+ }
+ }
+}
+
+class Item extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ isOpIconShown: false,
+ highlight: false,
+ isSetQuotaDialogOpen: false,
+ isDeleteUserDialogOpen: false,
+ isResetUserPasswordDialogOpen: false,
+ isRevokeAdminDialogOpen: false
+ };
+ }
+
+ handleMouseEnter = () => {
+ if (!this.props.isItemFreezed) {
+ this.setState({
+ isOpIconShown: true,
+ highlight: true
+ });
+ }
+ }
+
+ handleMouseLeave = () => {
+ if (!this.props.isItemFreezed) {
+ this.setState({
+ isOpIconShown: false,
+ highlight: false
+ });
+ }
+ }
+
+ onUnfreezedItem = () => {
+ this.setState({
+ highlight: false,
+ isOpIconShow: false
+ });
+ this.props.onUnfreezedItem();
+ }
+
+ toggleSetQuotaDialog = () => {
+ this.setState({isSetQuotaDialogOpen: !this.state.isSetQuotaDialogOpen});
+ }
+
+ toggleDeleteUserDialog = () => {
+ this.setState({isDeleteUserDialogOpen: !this.state.isDeleteUserDialogOpen});
+ }
+
+ toggleResetUserPasswordDialog = () => {
+ this.setState({isResetUserPasswordDialogOpen: !this.state.isResetUserPasswordDialogOpen});
+ }
+
+ toggleRevokeAdminDialog = () => {
+ this.setState({isRevokeAdminDialogOpen: !this.state.isRevokeAdminDialogOpen});
+ }
+
+ onUserSelected = () => {
+ this.props.onUserSelected(this.props.item);
+ }
+
+ updateStatus= (value) => {
+ const isActive = value == 'active';
+ if (isActive) {
+ toaster.notify(gettext('It may take some time, please wait.'));
+ }
+ this.props.updateUser(this.props.item.email, 'is_active', isActive);
+ }
+
+ updateRole = (value) => {
+ this.props.updateUser(this.props.item.email, 'role', value);
+ }
+
+ updateAdminRole = (value) => {
+ this.props.updateAdminRole(this.props.item.email, value);
+ }
+
+ translateAdminRole = (role) => {
+ switch (role) {
+ case 'default_admin':
+ return gettext('Default Admin');
+ case 'system_admin':
+ return gettext('System Admin');
+ case 'daily_admin':
+ return gettext('Daily Admin');
+ case 'audit_admin':
+ return gettext('Audit Admin');
+ default:
+ return role;
+ }
+ }
+
+ updateInstitution = (value) => {
+ this.props.updateUser(this.props.item.email, 'institution', value);
+ }
+
+ translateInstitution = (inst) => {
+ return inst;
+ }
+
+ updateQuota = (value) => {
+ this.props.updateUser(this.props.item.email, 'quota_total', value);
+ }
+
+ deleteUser = () => {
+ this.props.deleteUser(this.props.item.email);
+ }
+
+ resetPassword = () => {
+ toaster.notify(gettext('It may take some time, please wait.'));
+ seafileAPI.sysAdminResetUserPassword(this.props.item.email).then(res => {
+ toaster.success(res.data.reset_tip);
+ }).catch((error) => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+
+ revokeAdmin = () => {
+ const { item } = this.props;
+ this.props.revokeAdmin(item.email, item.name);
+ }
+
+ getMenuOperations = () => {
+ const {
+ isAdmin, isLDAPImported,
+ isSearchResult, item
+ } = this.props;
+ let list = ['Delete'];
+ if (!isLDAPImported ||
+ (isSearchResult && item.source == 'db')) {
+ list.push('Reset Password');
+ }
+ if (isAdmin) {
+ list = ['Revoke Admin'];
+ }
+ return list;
+ }
+
+ translateOperations = (item) => {
+ let translateResult = '';
+ switch (item) {
+ case 'Delete':
+ translateResult = gettext('Delete');
+ break;
+ case 'Reset Password':
+ translateResult = gettext('Reset Password');
+ break;
+ case 'Revoke Admin':
+ translateResult = gettext('Revoke Admin');
+ break;
+ }
+
+ return translateResult;
+ }
+
+ onMenuItemClick = (operation) => {
+ switch(operation) {
+ case 'Delete':
+ this.toggleDeleteUserDialog();
+ break;
+ case 'Reset Password':
+ this.toggleResetUserPasswordDialog();
+ break;
+ case 'Revoke Admin':
+ this.toggleRevokeAdminDialog();
+ break;
+ default:
+ break;
+ }
+ }
+
+ render() {
+ const { item, isAdmin } = this.props;
+ const {
+ isOpIconShown,
+ isSetQuotaDialogOpen,
+ isDeleteUserDialogOpen,
+ isResetUserPasswordDialogOpen,
+ isRevokeAdminDialogOpen
+ } = this.state;
+
+ const itemName = '' + Utils.HTMLescape(item.name) + '';
+ const deleteDialogMsg = gettext('Are you sure you want to delete {placeholder} ?').replace('{placeholder}', itemName);
+ const resetPasswordDialogMsg = gettext('Are you sure you want to reset the password of {placeholder} ?').replace('{placeholder}', itemName);
+ const revokeAdminDialogMsg = gettext('Are you sure you want to revoke the admin permission of {placeholder} ?').replace('{placeholder}', itemName);
+
+ return (
+
+
+
+
+ |
+
+
+ {item.contact_email &&
+
+
+ {item.contact_email}
+ }
+ {item.org_id &&
+
+
+ ({item.org_name})
+
+ }
+ |
+
+
+ |
+ {isPro &&
+
+ {isAdmin ?
+ :
+
+ }
+ |
+ }
+
+ {`${Utils.bytesToSize(item.quota_usage)} / ${item.quota_total > 0 ? Utils.bytesToSize(item.quota_total) : '--'}`}
+
+
+ |
+ {(multiInstitution && !isAdmin) &&
+
+ 0}
+ options={institutions}
+ currentOption={item.institution}
+ onOptionChanged={this.updateInstitution}
+ translateOption={this.translateInstitution}
+ />
+ |
+ }
+
+ {`${item.create_time ? moment(item.create_time).format('YYYY-MM-DD HH:mm') : '--'} /`}
+
+ {`${item.last_login ? moment(item.last_login).fromNow() : '--'}`}
+ |
+
+ {(item.email != username && isOpIconShown) &&
+
+ }
+ |
+
+ {isSetQuotaDialogOpen &&
+
+ }
+ {isDeleteUserDialogOpen &&
+
+ }
+ {isResetUserPasswordDialogOpen &&
+
+ }
+ {isRevokeAdminDialogOpen &&
+
+ }
+
+ );
+ }
+}
+
+export default Content;
diff --git a/frontend/src/pages/sys-admin/users/users.js b/frontend/src/pages/sys-admin/users/users.js
index 2cf00ed047..b96c5c1778 100644
--- a/frontend/src/pages/sys-admin/users/users.js
+++ b/frontend/src/pages/sys-admin/users/users.js
@@ -1,17 +1,10 @@
import React, { Component, Fragment } from 'react';
-import { Link } from '@reach/router';
+import { navigate } from '@reach/router';
import { Button } from 'reactstrap';
-import moment from 'moment';
import { Utils } from '../../../utils/utils';
import { seafileAPI } from '../../../utils/seafile-api';
-import { isPro, username, gettext, multiInstitution, siteRoot, loginUrl } from '../../../utils/constants';
+import { isPro, gettext, siteRoot, loginUrl } from '../../../utils/constants';
import toaster from '../../../components/toast';
-import EmptyTip from '../../../components/empty-tip';
-import Loading from '../../../components/loading';
-import Paginator from '../../../components/paginator';
-import SysAdminUserStatusEditor from '../../../components/select-editor/sysadmin-user-status-editor';
-import SysAdminUserRoleEditor from '../../../components/select-editor/sysadmin-user-role-editor';
-import SelectEditor from '../../../components/select-editor/select-editor';
import SysAdminUserSetQuotaDialog from '../../../components/dialog/sysadmin-dialog/set-quota';
import SysAdminImportUserDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-import-user-dialog';
import SysAdminAddUserDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-add-user-dialog';
@@ -20,438 +13,11 @@ import CommonOperationConfirmationDialog from '../../../components/dialog/common
import SysAdminUser from '../../../models/sysadmin-user';
import SysAdminAdminUser from '../../../models/sysadmin-admin-user';
import MainPanelTopbar from '../main-panel-topbar';
-import UserLink from '../user-link';
+import Search from '../search';
import UsersNav from './users-nav';
-import OpMenu from './user-op-menu';
-
-const { availableRoles, availableAdminRoles, institutions } = window.sysadmin.pageOptions;
-
-class Content extends Component {
-
- constructor(props) {
- super(props);
- this.state = {
- isItemFreezed: false
- };
- }
-
- onFreezedItem = () => {
- this.setState({isItemFreezed: true});
- }
-
- onUnfreezedItem = () => {
- this.setState({isItemFreezed: false});
- }
-
- getPreviousPage = () => {
- this.props.getListByPage(this.props.currentPage - 1);
- }
-
- getNextPage = () => {
- this.props.getListByPage(this.props.currentPage + 1);
- }
-
- render() {
- const { isAdmin, loading, errorMsg, items, pageInfo, isAllUsersSelected, curPerPage, hasNextPage, currentPage } = this.props;
- if (loading) {
- return ;
- } else if (errorMsg) {
- return {errorMsg}
;
- } else {
- const emptyTip = (
-
- {gettext('No users')}
-
- );
-
- let columns = [];
- const colNameText = `${gettext('Name')} / ${gettext('Contact Email')}`;
- const colSpaceText = `${gettext('Space Used')} / ${gettext('Quota')}`;
- const colCreatedText = `${gettext('Created At')} / ${gettext('Last Login')}`;
- if (isPro) {
- columns.push(
- {width: '20%', text: colNameText},
- {width: '15%', text: gettext('Status')},
- {width: '15%', text: gettext('Role')}
- );
- } else {
- columns.push(
- {width: '30%', text: colNameText},
- {width: '20%', text: gettext('Status')}
- );
- }
- if (multiInstitution && !isAdmin) {
- columns.push(
- {width: '14%', text: colSpaceText},
- {width: '14%', text: gettext('Institution')},
- {width: '14%', text: colCreatedText},
- {width: '5%', text: ''}
- );
- } else {
- columns.push(
- {width: '20%', text: colSpaceText},
- {width: '22%', text: colCreatedText},
- {width: '5%', text: ''}
- );
- }
-
- const table = (
-
-
- {!this.props.isAdmin &&
-
- }
-
- );
-
- return items.length ? table : emptyTip;
- }
- }
-}
-
-class Item extends Component {
-
- constructor(props) {
- super(props);
- this.state = {
- isOpIconShown: false,
- highlight: false,
- isSetQuotaDialogOpen: false,
- isDeleteUserDialogOpen: false,
- isResetUserPasswordDialogOpen: false,
- isRevokeAdminDialogOpen: false
- };
- }
-
- handleMouseEnter = () => {
- if (!this.props.isItemFreezed) {
- this.setState({
- isOpIconShown: true,
- highlight: true
- });
- }
- }
-
- handleMouseLeave = () => {
- if (!this.props.isItemFreezed) {
- this.setState({
- isOpIconShown: false,
- highlight: false
- });
- }
- }
-
- onUnfreezedItem = () => {
- this.setState({
- highlight: false,
- isOpIconShow: false
- });
- this.props.onUnfreezedItem();
- }
-
- toggleSetQuotaDialog = () => {
- this.setState({isSetQuotaDialogOpen: !this.state.isSetQuotaDialogOpen});
- }
-
- toggleDeleteUserDialog = () => {
- this.setState({isDeleteUserDialogOpen: !this.state.isDeleteUserDialogOpen});
- }
-
- toggleResetUserPasswordDialog = () => {
- this.setState({isResetUserPasswordDialogOpen: !this.state.isResetUserPasswordDialogOpen});
- }
-
- toggleRevokeAdminDialog = () => {
- this.setState({isRevokeAdminDialogOpen: !this.state.isRevokeAdminDialogOpen});
- }
-
- onUserSelected = () => {
- this.props.onUserSelected(this.props.item);
- }
-
- updateStatus= (value) => {
- const isActive = value == 'active';
- if (isActive) {
- toaster.notify(gettext('It may take some time, please wait.'));
- }
- this.props.updateUser(this.props.item.email, 'is_active', isActive);
- }
-
- updateRole = (value) => {
- this.props.updateUser(this.props.item.email, 'role', value);
- }
-
- updateAdminRole = (value) => {
- this.props.updateAdminRole(this.props.item.email, value);
- }
-
- translateAdminRole = (role) => {
- switch (role) {
- case 'default_admin':
- return gettext('Default Admin');
- case 'system_admin':
- return gettext('System Admin');
- case 'daily_admin':
- return gettext('Daily Admin');
- case 'audit_admin':
- return gettext('Audit Admin');
- default:
- return role;
- }
- }
-
- updateInstitution = (value) => {
- this.props.updateUser(this.props.item.email, 'institution', value);
- }
-
- translateInstitution = (inst) => {
- return inst;
- }
-
- updateQuota = (value) => {
- this.props.updateUser(this.props.item.email, 'quota_total', value);
- }
-
- deleteUser = () => {
- this.props.deleteUser(this.props.item.email);
- }
-
- resetPassword = () => {
- seafileAPI.sysAdminResetUserPassword(this.props.item.email).then(res => {
- toaster.success(res.data.reset_tip);
- }).catch((error) => {
- let errMessage = Utils.getErrorMsg(error);
- toaster.danger(errMessage);
- });
- }
-
- revokeAdmin = () => {
- const { item } = this.props;
- this.props.revokeAdmin(item.email, item.name);
- }
-
- getMenuOperations = () => {
- const { isAdmin, isLDAPImported } = this.props;
- let list = ['Delete'];
- if (!isLDAPImported) {
- list.push('Reset Password');
- }
- if (isAdmin) {
- list = ['Revoke Admin'];
- }
- return list;
- }
-
- translateOperations = (item) => {
- let translateResult = '';
- switch (item) {
- case 'Delete':
- translateResult = gettext('Delete');
- break;
- case 'Reset Password':
- translateResult = gettext('Reset Password');
- break;
- case 'Revoke Admin':
- translateResult = gettext('Revoke Admin');
- break;
- }
-
- return translateResult;
- }
-
- onMenuItemClick = (operation) => {
- switch(operation) {
- case 'Delete':
- this.toggleDeleteUserDialog();
- break;
- case 'Reset Password':
- this.toggleResetUserPasswordDialog();
- break;
- case 'Revoke Admin':
- this.toggleRevokeAdminDialog();
- break;
- default:
- break;
- }
- }
-
- render() {
- const { item, isAdmin } = this.props;
- const {
- isOpIconShown,
- isSetQuotaDialogOpen,
- isDeleteUserDialogOpen,
- isResetUserPasswordDialogOpen,
- isRevokeAdminDialogOpen
- } = this.state;
-
- const itemName = '' + Utils.HTMLescape(item.name) + '';
- const deleteDialogMsg = gettext('Are you sure you want to delete {placeholder} ?').replace('{placeholder}', itemName);
- const resetPasswordDialogMsg = gettext('Are you sure you want to reset the password of {placeholder} ?').replace('{placeholder}', itemName);
- const revokeAdminDialogMsg = gettext('Are you sure you want to revoke the admin permission of {placeholder} ?').replace('{placeholder}', itemName);
-
- return (
-
-
-
-
- |
-
-
- {item.contact_email &&
-
-
- {item.contact_email}
- }
- {item.org_id &&
-
-
- ({item.org_name})
-
- }
- |
-
-
- |
- {isPro &&
-
- {isAdmin ?
- :
-
- }
- |
- }
-
- {`${Utils.bytesToSize(item.quota_usage)} / ${item.quota_total > 0 ? Utils.bytesToSize(item.quota_total) : '--'}`}
-
-
- |
- {(multiInstitution && !isAdmin) &&
-
- 0}
- options={institutions}
- currentOption={item.institution}
- onOptionChanged={this.updateInstitution}
- translateOption={this.translateInstitution}
- />
- |
- }
-
- {`${item.create_time ? moment(item.create_time).format('YYYY-MM-DD HH:mm') : '--'} /`}
-
- {`${item.last_login ? moment(item.last_login).fromNow() : '--'}`}
- |
-
- {(item.email != username && isOpIconShown) &&
-
- }
- |
-
- {isSetQuotaDialogOpen &&
-
- }
- {isDeleteUserDialogOpen &&
-
- }
- {isResetUserPasswordDialogOpen &&
-
- }
- {isRevokeAdminDialogOpen &&
-
- }
-
- );
- }
-}
+import Content from './users-content';
+const { availableRoles } = window.sysadmin.pageOptions;
class Users extends Component {
@@ -840,6 +406,21 @@ class Users extends Component {
});
}
+ getSearch = () => {
+ if (this.props.isAdmin) {
+ return null;
+ }
+ // offer 'Search' for 'DB' & 'LDAPImported' users
+ return ;
+ }
+
+ searchItems = (keyword) => {
+ navigate(`${siteRoot}sys/search-users/?query=${encodeURIComponent(keyword)}`);
+ }
+
render() {
const { isAdmin, isLDAPImported } = this.props;
const {
@@ -852,7 +433,7 @@ class Users extends Component {
} = this.state;
return (
-
+
{hasUserSelected ?
diff --git a/seahub/urls.py b/seahub/urls.py
index 0d85b8280a..94e11de86d 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -717,6 +717,7 @@ urlpatterns = [
url(r'^sys/departments/$', sysadmin_react_fake_view, name="sys_departments"),
url(r'^sys/departments/(?P\d+)/$', sysadmin_react_fake_view, name="sys_department"),
url(r'^sys/users/$', sysadmin_react_fake_view, name="sys_users"),
+ url(r'^sys/search-users/$', sysadmin_react_fake_view, name="sys_search_users"),
url(r'^sys/users/admins/$', sysadmin_react_fake_view, name="sys_users_admin"),
url(r'^sys/users/ldap/$', sysadmin_react_fake_view, name="sys_users_ldap"),
url(r'^sys/users/ldap-imported/$', sysadmin_react_fake_view, name="sys_users_ldap_imported"),