mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-13 12:45:46 +00:00
272 lines
7.4 KiB
JavaScript
272 lines
7.4 KiB
JavaScript
|
import React from 'react';
|
||
|
import PropTypes from 'prop-types';
|
||
|
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
|
||
|
import moment from 'moment';
|
||
|
import { Utils } from '../../utils/utils';
|
||
|
import { gettext, wikiId } from '../../utils/constants';
|
||
|
import wikiAPI from '../../utils/wiki-api';
|
||
|
import ModalPortal from '../../components/modal-portal';
|
||
|
import toaster from '../../components/toast';
|
||
|
import Paginator from '../../components/paginator';
|
||
|
import WikiCleanTrash from '../../components/dialog/wiki-clean-trash';
|
||
|
import NavItemIcon from './common/nav-item-icon';
|
||
|
|
||
|
import '../../css/toolbar.css';
|
||
|
import '../../css/search.css';
|
||
|
import '../../css/wiki-trash-dialog.css';
|
||
|
|
||
|
const propTypes = {
|
||
|
showTrashDialog: PropTypes.bool.isRequired,
|
||
|
toggleTrashDialog: PropTypes.func.isRequired,
|
||
|
getWikiConfig: PropTypes.func.isRequired
|
||
|
};
|
||
|
|
||
|
class WikiTrashDialog extends React.Component {
|
||
|
|
||
|
constructor(props) {
|
||
|
super(props);
|
||
|
this.state = {
|
||
|
isLoading: true,
|
||
|
errorMsg: '',
|
||
|
items: [],
|
||
|
isCleanTrashDialogOpen: false,
|
||
|
currentPage: 1,
|
||
|
perPage: 100,
|
||
|
hasNextPage: false
|
||
|
};
|
||
|
}
|
||
|
|
||
|
componentDidMount() {
|
||
|
this.getItems();
|
||
|
}
|
||
|
|
||
|
getItems = (page) => {
|
||
|
wikiAPI.getWikiTrash(wikiId, page, this.state.perPage).then((res) => {
|
||
|
const { items, total_count } = res.data;
|
||
|
if (!page) {
|
||
|
page = 1;
|
||
|
}
|
||
|
this.setState({
|
||
|
currentPage: page,
|
||
|
hasNextPage: total_count - page * this.state.perPage > 0,
|
||
|
isLoading: false,
|
||
|
items: items,
|
||
|
});
|
||
|
});
|
||
|
};
|
||
|
|
||
|
resetPerPage = (perPage) => {
|
||
|
this.setState({
|
||
|
perPage: perPage
|
||
|
}, () => {
|
||
|
this.getItems(1);
|
||
|
});
|
||
|
};
|
||
|
cleanTrash = () => {
|
||
|
this.toggleCleanTrashDialog();
|
||
|
};
|
||
|
|
||
|
toggleCleanTrashDialog = () => {
|
||
|
this.setState({
|
||
|
isCleanTrashDialogOpen: !this.state.isCleanTrashDialogOpen
|
||
|
});
|
||
|
};
|
||
|
|
||
|
refreshTrash = () => {
|
||
|
this.setState({
|
||
|
isLoading: true,
|
||
|
errorMsg: '',
|
||
|
items: []
|
||
|
});
|
||
|
this.getItems();
|
||
|
};
|
||
|
|
||
|
render() {
|
||
|
const { showTrashDialog, toggleTrashDialog } = this.props;
|
||
|
const { isCleanTrashDialogOpen } = this.state;
|
||
|
const { isAdmin, enableUserCleanTrash, repoName } = window.wiki.config;
|
||
|
let title = gettext('{placeholder} Wiki Trash');
|
||
|
title = title.replace('{placeholder}', '<span class="op-target text-truncate mx-1">' + Utils.HTMLescape(repoName) + '</span>');
|
||
|
return (
|
||
|
<Modal className="trash-dialog" isOpen={showTrashDialog} toggle={toggleTrashDialog}>
|
||
|
<ModalHeader
|
||
|
close={
|
||
|
<>
|
||
|
<div className="but-contral">
|
||
|
{(isAdmin && enableUserCleanTrash) &&
|
||
|
<button className="btn btn-secondary clean flex-shrink-0 ml-4" onClick={this.cleanTrash}>{gettext('Clean')}</button>
|
||
|
}
|
||
|
<span aria-hidden="true" className="trash-dialog-close-icon sf3-font sf3-font-x-01 ml-4" onClick={toggleTrashDialog}></span>
|
||
|
</div>
|
||
|
</>
|
||
|
}
|
||
|
>
|
||
|
<div dangerouslySetInnerHTML={{ __html: title }}></div>
|
||
|
</ModalHeader>
|
||
|
<ModalBody>
|
||
|
<Content
|
||
|
data={this.state}
|
||
|
currentPage={this.state.currentPage}
|
||
|
curPerPage={this.state.perPage}
|
||
|
hasNextPage={this.state.hasNextPage}
|
||
|
getListByPage={this.getItems}
|
||
|
resetPerPage={this.resetPerPage}
|
||
|
getWikiConfig={this.props.getWikiConfig}
|
||
|
/>
|
||
|
{isCleanTrashDialogOpen &&
|
||
|
<ModalPortal>
|
||
|
<WikiCleanTrash
|
||
|
wikiId={wikiId}
|
||
|
refreshTrash={this.refreshTrash}
|
||
|
toggleDialog={this.toggleCleanTrashDialog}
|
||
|
/>
|
||
|
</ModalPortal>
|
||
|
}
|
||
|
</ModalBody>
|
||
|
</Modal>
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class Content extends React.Component {
|
||
|
|
||
|
constructor(props) {
|
||
|
super(props);
|
||
|
this.theadData = [
|
||
|
{ width: '3%', text: gettext('Name') },
|
||
|
{ width: '20%', text: '' },
|
||
|
{ width: '30%', text: gettext('Size') },
|
||
|
{ width: '37%', text: gettext('Delete Time') },
|
||
|
{ width: '10%', text: '' }
|
||
|
];
|
||
|
}
|
||
|
|
||
|
getPreviousPage = () => {
|
||
|
this.props.getListByPage(this.props.currentPage - 1);
|
||
|
};
|
||
|
|
||
|
getNextPage = () => {
|
||
|
this.props.getListByPage(this.props.currentPage + 1);
|
||
|
};
|
||
|
|
||
|
render() {
|
||
|
const { items } = this.props.data;
|
||
|
const { curPerPage, currentPage, hasNextPage } = this.props;
|
||
|
return (
|
||
|
<React.Fragment>
|
||
|
<table className="table-hover">
|
||
|
<thead>
|
||
|
<tr>
|
||
|
{this.theadData.map((item, index) => {
|
||
|
return <th key={index} className={index === 0 ? 'pl-3' : ''} width={item.width}>{item.text}</th>;
|
||
|
})}
|
||
|
</tr>
|
||
|
</thead>
|
||
|
<tbody>
|
||
|
{items.map((item, index) => {
|
||
|
return (
|
||
|
<Item
|
||
|
key={index}
|
||
|
item={item}
|
||
|
getWikiConfig={this.props.getWikiConfig}
|
||
|
/>
|
||
|
);
|
||
|
})}
|
||
|
</tbody>
|
||
|
</table>
|
||
|
<Paginator
|
||
|
gotoPreviousPage={this.getPreviousPage}
|
||
|
gotoNextPage={this.getNextPage}
|
||
|
currentPage={currentPage}
|
||
|
hasNextPage={hasNextPage}
|
||
|
curPerPage={curPerPage}
|
||
|
resetPerPage={this.props.resetPerPage}
|
||
|
/>
|
||
|
</React.Fragment>
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Content.propTypes = {
|
||
|
data: PropTypes.object.isRequired,
|
||
|
getListByPage: PropTypes.func.isRequired,
|
||
|
resetPerPage: PropTypes.func.isRequired,
|
||
|
currentPage: PropTypes.number.isRequired,
|
||
|
curPerPage: PropTypes.number.isRequired,
|
||
|
hasNextPage: PropTypes.bool.isRequired,
|
||
|
getWikiConfig: PropTypes.func.isRequired
|
||
|
|
||
|
};
|
||
|
|
||
|
class Item extends React.Component {
|
||
|
|
||
|
constructor(props) {
|
||
|
super(props);
|
||
|
this.state = {
|
||
|
restored: false,
|
||
|
isIconShown: false,
|
||
|
getWikiConfig: PropTypes.func.isRequired
|
||
|
};
|
||
|
}
|
||
|
|
||
|
handleMouseOver = () => {
|
||
|
this.setState({ isIconShown: true });
|
||
|
};
|
||
|
|
||
|
handleMouseOut = () => {
|
||
|
this.setState({ isIconShown: false });
|
||
|
};
|
||
|
|
||
|
restoreItem = (e) => {
|
||
|
e.preventDefault();
|
||
|
const item = this.props.item;
|
||
|
wikiAPI.revertTrashPage(wikiId, item.page_id).then(res => {
|
||
|
this.setState({
|
||
|
restored: true
|
||
|
});
|
||
|
this.props.getWikiConfig();
|
||
|
toaster.success(gettext('Successfully restored 1 item.'));
|
||
|
}).catch((error) => {
|
||
|
let errorMsg = '';
|
||
|
if (error.response) {
|
||
|
errorMsg = error.response.data.error_msg || gettext('Error');
|
||
|
} else {
|
||
|
errorMsg = gettext('Please check the network.');
|
||
|
}
|
||
|
toaster.danger(errorMsg);
|
||
|
});
|
||
|
};
|
||
|
|
||
|
|
||
|
render() {
|
||
|
const item = this.props.item;
|
||
|
const { restored, isIconShown } = this.state;
|
||
|
if (restored) {
|
||
|
return null;
|
||
|
}
|
||
|
const { isAdmin } = window.wiki.config;
|
||
|
return (
|
||
|
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut} onFocus={this.handleMouseOver}>
|
||
|
<td><NavItemIcon symbol={'file'} disable={true} /></td>
|
||
|
<td>{item.name}</td>
|
||
|
<td>{Utils.bytesToSize(item.size)}</td>
|
||
|
<td title={moment(item.deleted_time).format('LLLL')}>{moment(item.deleted_time).format('YYYY-MM-DD')}</td>
|
||
|
<td>
|
||
|
{isAdmin &&
|
||
|
<a href="#" className={isIconShown ? '' : 'invisible'} onClick={this.restoreItem} role="button">{gettext('Restore')}</a>
|
||
|
}
|
||
|
</td>
|
||
|
</tr>
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Item.propTypes = {
|
||
|
item: PropTypes.object.isRequired
|
||
|
};
|
||
|
|
||
|
|
||
|
WikiTrashDialog.propTypes = propTypes;
|
||
|
|
||
|
export default WikiTrashDialog;
|