import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { Button } from 'reactstrap'; import moment from 'moment'; import { Utils } from '../../../utils/utils'; import { seafileAPI } from '../../../utils/seafile-api'; import { gettext, orgID } from '../../../utils/constants'; import toaster from '../../../components/toast/index'; import EmptyTip from '../../../components/empty-tip'; import Loading from '../../../components/loading'; import Paginator from '../../../components/paginator'; import ModalPortal from '../../../components/modal-portal'; import OpMenu from '../../../components/dialog/op-menu'; import CommonOperationConfirmationDialog from '../../../components/dialog/common-operation-confirmation-dialog'; import MainPanelTopbar from '../main-panel-topbar'; import UserLink from '../user-link'; import ReposNav from './org-repo-nav'; class Content extends Component { constructor(props) { super(props); this.state = { isItemFreezed: false }; } onFreezedItem = () => { this.setState({isItemFreezed: true}); }; onUnfreezedItem = () => { this.setState({isItemFreezed: false}); }; getPreviousPageList = () => { this.props.getListByPage(this.props.pageInfo.current_page - 1); }; getNextPageList = () => { this.props.getListByPage(this.props.pageInfo.current_page + 1); }; render() { const { loading, errorMsg, items, pageInfo, curPerPage } = this.props; if (loading) { return ; } else if (errorMsg) { return

{errorMsg}

; } else { const emptyTip = (

{gettext('No deleted libraries')}

); const table = ( {/*

{gettext('Tip: libraries deleted {trashReposExpireDays} days ago will be cleaned automatically.').replace('{trashReposExpireDays}', trashReposExpireDays)}

*/} {items.map((item, index) => { return (); })}
{/*icon*/} {gettext('Name')} {gettext('Owner')} {gettext('Deleted Time')} {/*Operations*/}
{pageInfo && }
); return items.length ? table : emptyTip; } } } Content.propTypes = { loading: PropTypes.bool.isRequired, errorMsg: PropTypes.string.isRequired, items: PropTypes.array.isRequired, deleteItem: PropTypes.func, onDeleteRepo: PropTypes.func.isRequired, onRestoreRepo: PropTypes.func, getListByPage: PropTypes.func.isRequired, resetPerPage: PropTypes.func, pageInfo: PropTypes.object, curPerPage: PropTypes.number, }; class Item extends Component { constructor(props) { super(props); this.state = { highlight: false, isOpIconShown: false, isDeleteRepoDialogOpen: false, isRestoreRepoDialogOpen: false }; } handleMouseOver = () => { if (!this.props.isItemFreezed) { this.setState({ isOpIconShown: true, highlight: true }); } }; handleMouseOut = () => { if (!this.props.isItemFreezed) { this.setState({ isOpIconShown: false, highlight: false }); } }; onUnfreezedItem = () => { this.setState({ highlight: false, isOpIconShow: false }); this.props.onUnfreezedItem(); }; onDeleteRepo = () => { const repo = this.props.repo; seafileAPI.orgAdminDeleteTrashRepo(orgID, repo.id).then((res) => { this.props.onDeleteRepo(repo); const msg = gettext('Successfully deleted {name}.').replace('{name}', repo.name); toaster.success(msg); }).catch((error) => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); }; onRestoreRepo = () => { const repo = this.props.repo; seafileAPI.orgAdminRestoreTrashRepo(orgID, repo.id).then((res) => { this.props.onRestoreRepo(repo); let message = gettext('Successfully restored the library.'); toaster.success(message); }).catch((error) => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); }; toggleDeleteRepoDialog = (e) => { if (e) { e.preventDefault(); } this.setState({isDeleteRepoDialogOpen: !this.state.isDeleteRepoDialogOpen}); }; toggleRestoreRepoDialog = (e) => { if (e) { e.preventDefault(); } this.setState({isRestoreRepoDialogOpen: !this.state.isRestoreRepoDialogOpen}); }; translateOperations = (item) => { let translateResult = ''; switch(item) { case 'Restore': translateResult = gettext('Restore'); break; case 'Delete': translateResult = gettext('Delete'); break; default: break; } return translateResult; }; onMenuItemClick = (operation) => { switch(operation) { case 'Restore': this.toggleRestoreRepoDialog(); break; case 'Delete': this.toggleDeleteRepoDialog(); break; default: break; } }; render () { const { repo } = this.props; const { isOpIconShown, isDeleteRepoDialogOpen, isRestoreRepoDialogOpen } = this.state; const iconUrl = Utils.getLibIconUrl(repo); const iconTitle = Utils.getLibIconTitle(repo); const repoName = '' + Utils.HTMLescape(repo.name) + ''; return ( {iconTitle} {repo.name} {repo.owner.indexOf('@seafile_group') == -1 ? : repo.group_name} {moment(repo.delete_time).fromNow()} {isOpIconShown && ( )} {isDeleteRepoDialogOpen && } {isRestoreRepoDialogOpen && } ); } } Item.propTypes = { repo: PropTypes.object.isRequired, isItemFreezed: PropTypes.bool.isRequired, onFreezedItem: PropTypes.func.isRequired, onUnfreezedItem: PropTypes.func.isRequired, onDeleteRepo: PropTypes.func.isRequired, onRestoreRepo: PropTypes.func, }; class TrashRepos extends Component { constructor(props) { super(props); this.state = { loading: true, errorMsg: '', repos: [], pageInfo: {}, perPage: 25, isCleanTrashDialogOpen: false }; } componentDidMount () { let urlParams = (new URL(window.location)).searchParams; const { currentPage = 1, perPage } = this.state; this.setState({ perPage: parseInt(urlParams.get('per_page') || perPage), currentPage: parseInt(urlParams.get('page') || currentPage) }, () => { this.getReposByPage(this.state.currentPage); }); } toggleCleanTrashDialog = () => { this.setState({isCleanTrashDialogOpen: !this.state.isCleanTrashDialogOpen}); }; getReposByPage = (page) => { let { perPage } = this.state; seafileAPI.orgAdminListTrashRepos(orgID, page, perPage).then((res) => { this.setState({ repos: res.data.repos, pageInfo: res.data.page_info, loading: false }); }).catch((error) => { this.setState({ loading: false, errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403 }); }); }; resetPerPage = (perPage) => { this.setState({ perPage: perPage }, () => { this.getReposByPage(1); }); }; onDeleteRepo = (targetRepo) => { let repos = this.state.repos.filter(repo => { return repo.id != targetRepo.id; }); this.setState({ repos: repos }); }; onRestoreRepo = (targetRepo) => { let repos = this.state.repos.filter(repo => { return repo.id != targetRepo.id; }); this.setState({ repos: repos }); }; cleanTrash = () => { seafileAPI.orgAdminCleanTrashRepo(orgID).then(res => { this.setState({repos: []}); toaster.success(gettext('Successfully cleared trash.')); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); }; render() { const { isCleanTrashDialogOpen } = this.state; // enable 'search': return ( {this.state.repos.length ? ( ) : }
{isCleanTrashDialogOpen && }
); } } export default TrashRepos;