1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-19 10:26:17 +00:00

Add department transfer function to the administrator interface (#6014)

* Add the function of transferring departments in the administrator interface

* fix admin get all departments

* update

* Update departments.py

* update

* update

* add department repo transfer

* Update groups.py

* update

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
This commit is contained in:
awu0403
2024-04-25 12:08:13 +08:00
committed by GitHub
parent 7a04bfa928
commit db6cee68e9
11 changed files with 362 additions and 51 deletions

View File

@@ -1,19 +1,34 @@
import React from 'react';
import React, {Fragment} from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import {
Button,
Modal,
ModalHeader,
ModalBody,
ModalFooter,
Nav,
NavItem,
NavLink,
TabContent,
TabPane
} from 'reactstrap';
import makeAnimated from 'react-select/animated';
import { seafileAPI } from '../../utils/seafile-api';
import { gettext, isPro } from '../../utils/constants';
import {gettext, isPro, orgID} from '../../utils/constants';
import { Utils } from '../../utils/utils';
import toaster from '../toast';
import UserSelect from '../user-select';
import { SeahubSelect } from '../common/select';
import '../../css/transfer-dialog.css';
const propTypes = {
itemName: PropTypes.string.isRequired,
toggleDialog: PropTypes.func.isRequired,
submit: PropTypes.func.isRequired,
canTransferToDept: PropTypes.bool
canTransferToDept: PropTypes.bool,
isOrgAdmin: PropTypes.bool,
isSysAdmin: PropTypes.bool,
};
class TransferDialog extends React.Component {
@@ -23,23 +38,54 @@ class TransferDialog extends React.Component {
selectedOption: null,
errorMsg: [],
transferToUser: true,
transferToGroup: false,
activeTab: 'transUser'
};
this.options = [];
}
handleSelectChange = (option) => {
this.setState({selectedOption: option});
this.setState({ selectedOption: option });
};
submit = () => {
let user = this.state.selectedOption;
this.props.submit(user);
};
componentDidMount() {
if (isPro) {
if (this.props.isOrgAdmin) {
seafileAPI.orgAdminListDepartments(orgID).then((res) => {
for (let i = 0; i < res.data.length; i++) {
let obj = {};
obj.value = res.data[i].name;
obj.email = res.data[i].email;
obj.label = res.data[i].name;
this.options.push(obj);
}
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
else if (this.props.isSysAdmin) {
seafileAPI.sysAdminListDepartments().then((res) => {
for (let i = 0; i < res.data.length; i++) {
let obj = {};
obj.value = res.data[i].name;
obj.email = res.data[i].email;
obj.label = res.data[i].name;
this.options.push(obj);
}
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
else{
seafileAPI.listDepartments().then((res) => {
for (let i = 0 ; i < res.data.length; i++) {
for (let i = 0; i < res.data.length; i++) {
let obj = {};
obj.value = res.data[i].name;
obj.email = res.data[i].email;
@@ -56,49 +102,85 @@ class TransferDialog extends React.Component {
onClick = () => {
this.setState({
transferToUser: !this.state.transferToUser,
});
};
render() {
toggle = (tab) => {
if (this.state.activeTab !== tab) {
this.setState({ activeTab: tab });
}
};
renderTransContent = () => {
let activeTab = this.state.activeTab;
let canTransferToDept = true;
if (this.props.canTransferToDept != undefined) {
canTransferToDept = this.props.canTransferToDept;
}
return (
<Fragment>
<div className="transfer-dialog-side">
<Nav pills>
<NavItem role="tab" aria-selected={activeTab === 'transUser'} aria-controls="transfer-user-panel">
<NavLink className={activeTab === 'transUser' ? 'active' : ''} onClick={(this.toggle.bind(this, 'transUser'))} tabIndex="0" onKeyDown={this.onTabKeyDown}>
{gettext('Transfer to user')}
</NavLink>
</NavItem>
{isPro &&
<NavItem role="tab" aria-selected={activeTab === 'transDepart'} aria-controls="transfer-depart-panel">
<NavLink className={activeTab === 'transDepart' ? 'active' : ''} onClick={this.toggle.bind(this, 'transDepart')} tabIndex="0" onKeyDown={this.onTabKeyDown}>
{gettext('Transfer to department')}
</NavLink>
</NavItem>}
</Nav>
</div>
<div className="transfer-dialog-main">
<TabContent activeTab={this.state.activeTab}>
<Fragment>
<TabPane tabId="transUser" role="tabpanel" id="transfer-user-panel">
<UserSelect
ref="userSelect"
isMulti={false}
className="reviewer-select"
placeholder={gettext('Select a user')}
onSelectChange={this.handleSelectChange}
/>
</TabPane>
{isPro && canTransferToDept &&
<TabPane tabId="transDepart" role="tabpanel" id="transfer-depart-panel">
<SeahubSelect
isClearable
maxMenuHeight={200}
hideSelectedOptions={true}
components={makeAnimated()}
placeholder={gettext('Select a department')}
options={this.options}
onChange={this.handleSelectChange}
value={this.state.selectedOption}
/>
</TabPane>}
</Fragment>
</TabContent>
</div>
</Fragment>
);
};
render() {
const { itemName: repoName } = this.props;
let title = gettext('Transfer Library {library_name}');
title = title.replace('{library_name}', '<span class="op-target text-truncate mx-1">' + Utils.HTMLescape(repoName) + '</span>');
return (
<Modal isOpen={true} toggle={this.props.toggleDialog}>
<Modal isOpen={true} style={{maxWidth: '720px'}} toggle={this.props.toggleDialog} className="transfer-dialog">
<ModalHeader toggle={this.props.toggleDialog}>
<span dangerouslySetInnerHTML={{__html: title}} className="d-flex mw-100"></span>
<span dangerouslySetInnerHTML={{ __html: title }} className="d-flex mw-100"></span>
</ModalHeader>
<ModalBody>
{this.state.transferToUser ?
<UserSelect
ref="userSelect"
isMulti={false}
className="reviewer-select"
placeholder={gettext('Select a user')}
onSelectChange={this.handleSelectChange}
/> :
<SeahubSelect
isClearable
maxMenuHeight={200}
hideSelectedOptions={true}
components={makeAnimated()}
placeholder={gettext('Select a department')}
options={this.options}
onChange={this.handleSelectChange}
value={this.state.selectedOption}
/>
}
{isPro && canTransferToDept &&
<span role="button" tabIndex="0" className="action-link" onClick={this.onClick} onKeyDown={Utils.onKeyDown}>{this.state.transferToUser ?
gettext('Transfer to department'): gettext('Transfer to user')}
</span>
}
<ModalBody className="transfer-dialog-content" role="tablist">
{this.renderTransContent()}
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={this.props.toggleDialog}>{gettext('Cancel')}</Button>

View File

@@ -0,0 +1,52 @@
.transfer-dialog .transfer-dialog-content {
padding: 0;
min-height: 22.5rem;
display: flex;
flex-direction: column;
}
@media (min-width: 768px) {
.transfer-dialog .transfer-dialog-content {
flex-direction: row;
}
}
.transfer-dialog-content .transfer-dialog-side {
flex-basis: 29%;
padding: 1rem;
border-bottom: 1px solid #eee;
}
.transfer-dialog .nav .nav-item .nav-link {
padding: 0.3125rem 0.25rem;
}
@media (min-width: 768px) {
.transfer-dialog-content .transfer-dialog-side {
padding: 12px 8px;
border: 0;
border-right: 1px solid #eee;
}
.transfer-dialog-side .nav {
flex-direction: column;
}
.transfer-dialog-side .nav-pills .nav-item .nav-link {
width: 100%;
padding: 0.3125rem 0.5rem;
margin: 0;
}
}
.transfer-dialog-content .transfer-dialog-main {
display: flex;
flex-basis: 78%;
padding: 1rem;
}
.transfer-dialog-content .transfer-dialog-main .tab-content {
flex: 1;
}
.transfer-dialog-content .transfer-dialog-main .tab-pane {
height: 100%;
}

View File

@@ -200,7 +200,8 @@ class RepoItem extends React.Component {
highlight: false,
showMenu: false,
isItemMenuShow: false,
isTransferDialogShow: false
isTransferDialogShow: false,
orgAdmin: true
};
}
@@ -291,7 +292,7 @@ class RepoItem extends React.Component {
render() {
let { repo } = this.props;
let isOperationMenuShow = this.state.showMenu && !repo.isDepartmentRepo;
let isOperationMenuShow = this.state.showMenu;
return (
<Fragment>
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
@@ -326,6 +327,7 @@ class RepoItem extends React.Component {
itemName={repo.repoName}
submit={this.onTransferRepo}
toggleDialog={this.toggleTransfer}
isOrgAdmin={true}
/>
</ModalPortal>
)}

View File

@@ -280,6 +280,12 @@ class Item extends Component {
getOperations = () => {
const { repo } = this.props;
let operations = ['Delete', 'Transfer'];
const index = repo.owner_email.indexOf('@seafile_group');
let isGroupOwnedRepo = index != -1;
if (isGroupOwnedRepo) {
operations = ['Transfer'];
return operations;
}
if (!repo.encrypted) {
operations.push('Share');
}
@@ -319,7 +325,7 @@ class Item extends Component {
}
</td>
<td>
{(!isGroupOwnedRepo && isOpIconShown) &&
{(isOpIconShown) &&
<OpMenu
operations={this.getOperations()}
translateOperations={this.translateOperations}
@@ -359,8 +365,8 @@ class Item extends Component {
<TransferDialog
itemName={repo.name}
submit={this.onTransferRepo}
canTransferToDept={false}
toggleDialog={this.toggleTransferDialog}
isSysAdmin={true}
/>
</ModalPortal>
}