mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-01 15:23:05 +00:00
[system admin / logs] file access: added 'filter by user / library' (#4731)
This commit is contained in:
parent
9c11444518
commit
3d79d9fad8
@ -0,0 +1,46 @@
|
||||
import React from 'react';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
|
||||
|
||||
class FilterMenu extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isMenuShown: false
|
||||
};
|
||||
}
|
||||
|
||||
toggleMenu = () => {
|
||||
this.setState({
|
||||
isMenuShown: !this.state.isMenuShown
|
||||
}, () => {
|
||||
this.props.toggleFreezeItem(this.state.isMenuShown);
|
||||
});
|
||||
}
|
||||
|
||||
onItemClick = () => {
|
||||
this.props.filterItems();
|
||||
this.props.toggleFreezeItem(false);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { filterBy } = this.props;
|
||||
return (
|
||||
<Dropdown isOpen={this.state.isMenuShown} toggle={this.toggleMenu}>
|
||||
<DropdownToggle
|
||||
tag="i"
|
||||
className="sf-dropdown-toggle sf2-icon-caret-down"
|
||||
title={gettext('More Operations')}
|
||||
data-toggle="dropdown"
|
||||
aria-expanded={this.state.isMenuShown}
|
||||
/>
|
||||
<DropdownMenu>
|
||||
<DropdownItem onClick={this.onItemClick}>{gettext('only show {placeholder}').replace('{placeholder}', filterBy)}</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FilterMenu;
|
@ -1,23 +1,29 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { seafileAPI } from '../../../utils/seafile-api';
|
||||
import { gettext, siteRoot } from '../../../utils/constants';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
import { Utils } from '../../../utils/utils';
|
||||
import EmptyTip from '../../../components/empty-tip';
|
||||
import { Button } from 'reactstrap';
|
||||
import { navigate } from '@reach/router';
|
||||
import moment from 'moment';
|
||||
import Loading from '../../../components/loading';
|
||||
import Paginator from '../../../components/paginator';
|
||||
import LogsNav from './logs-nav';
|
||||
import MainPanelTopbar from '../main-panel-topbar';
|
||||
import UserLink from '../user-link';
|
||||
import LogsExportExcelDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-logs-export-excel-dialog';
|
||||
import ModalPortal from '../../../components/modal-portal';
|
||||
import LogsNav from './logs-nav';
|
||||
import FilterMenu from './file-access-item-menu';
|
||||
import ToggleFilter from './file-access-toggle-filter';
|
||||
import MainPanelTopbar from '../main-panel-topbar';
|
||||
import UserLink from '../user-link';
|
||||
|
||||
|
||||
class Content extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isItemFreezed: false
|
||||
};
|
||||
}
|
||||
|
||||
getPreviousPage = () => {
|
||||
@ -28,8 +34,26 @@ class Content extends Component {
|
||||
this.props.getLogsByPage(this.props.currentPage + 1);
|
||||
}
|
||||
|
||||
toggleFilterByUser = () => {
|
||||
this.props.filterByUser(null);
|
||||
}
|
||||
|
||||
toggleFilterByRepo = () => {
|
||||
this.props.filterByRepo(null);
|
||||
}
|
||||
|
||||
toggleFreezeItem = (freezed) => {
|
||||
this.setState({
|
||||
isItemFreezed: freezed
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading, errorMsg, items, perPage, currentPage, hasNextPage } = this.props;
|
||||
const {
|
||||
loading, errorMsg, items,
|
||||
userFilteredBy, repoFilteredBy,
|
||||
perPage, currentPage, hasNextPage
|
||||
} = this.props;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
} else if (errorMsg) {
|
||||
@ -42,7 +66,21 @@ class Content extends Component {
|
||||
);
|
||||
const table = (
|
||||
<Fragment>
|
||||
<table className="table-hover">
|
||||
<div>
|
||||
{userFilteredBy && (
|
||||
<ToggleFilter
|
||||
filterBy={items[0].name}
|
||||
toggleFilter={this.toggleFilterByUser}
|
||||
/>
|
||||
)}
|
||||
{repoFilteredBy && (
|
||||
<ToggleFilter
|
||||
filterBy={items[0].repo_name}
|
||||
toggleFilter={this.toggleFilterByRepo}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="20%">{gettext('Name')}</th>
|
||||
@ -59,6 +97,12 @@ class Content extends Component {
|
||||
return (<Item
|
||||
key={index}
|
||||
item={item}
|
||||
isFreezed={this.state.isItemFreezed}
|
||||
toggleFreezeItem={this.toggleFreezeItem}
|
||||
userFilteredBy={userFilteredBy}
|
||||
repoFilteredBy={repoFilteredBy}
|
||||
filterByUser={this.props.filterByUser}
|
||||
filterByRepo={this.props.filterByRepo}
|
||||
/>);
|
||||
})}
|
||||
</tbody>
|
||||
@ -84,31 +128,77 @@ class Item extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpIconShown: false,
|
||||
isHighlighted: false,
|
||||
isOpIconShown: false
|
||||
};
|
||||
}
|
||||
|
||||
handleMouseOver = () => {
|
||||
this.setState({
|
||||
isOpIconShown: true
|
||||
});
|
||||
handleMouseEnter = () => {
|
||||
if (!this.props.isFreezed) {
|
||||
this.setState({
|
||||
isHighlighted: true,
|
||||
isOpIconShown: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseOut = () => {
|
||||
this.setState({
|
||||
isOpIconShown: false
|
||||
});
|
||||
handleMouseLeave = () => {
|
||||
if (!this.props.isFreezed) {
|
||||
this.setState({
|
||||
isHighlighted: false,
|
||||
isOpIconShown: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
filterByUser = () => {
|
||||
const { item } = this.props;
|
||||
this.props.filterByUser(item.email);
|
||||
}
|
||||
|
||||
filterByRepo = () => {
|
||||
const { item } = this.props;
|
||||
this.props.filterByRepo(item.repo_id);
|
||||
}
|
||||
|
||||
toggleFreezeItem = (freezed) => {
|
||||
this.props.toggleFreezeItem(freezed);
|
||||
if (!freezed) {
|
||||
this.setState({
|
||||
isHighlighted: false,
|
||||
isOpIconShown: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let { item } = this.props;
|
||||
const { isHighlighted, isOpIconShown } = this.state;
|
||||
const { item, userFilteredBy, repoFilteredBy } = this.props;
|
||||
return (
|
||||
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
|
||||
<td><UserLink email={item.email} name={item.name} /></td>
|
||||
<tr className={isHighlighted ? 'tr-highlight' : ''} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||
<td>
|
||||
<UserLink email={item.email} name={item.name} />
|
||||
{isOpIconShown && !userFilteredBy && (
|
||||
<FilterMenu
|
||||
filterBy={item.name}
|
||||
filterItems={this.filterByUser}
|
||||
toggleFreezeItem={this.toggleFreezeItem}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td>{item.event_type}</td>
|
||||
<td>{item.ip}{' / '}{item.device}</td>
|
||||
<td>{item.ip}{' / '}{item.device || '--'}</td>
|
||||
<td>{moment(item.time).fromNow()}</td>
|
||||
<td>{item.repo_name ? item.repo_name : gettext('Deleted')}</td>
|
||||
<td>
|
||||
{item.repo_name ? item.repo_name : gettext('Deleted')}
|
||||
{isOpIconShown && item.repo_name && !repoFilteredBy && (
|
||||
<FilterMenu
|
||||
filterBy={item.repo_name}
|
||||
filterItems={this.filterByRepo}
|
||||
toggleFreezeItem={this.toggleFreezeItem}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td>{item.file_or_dir_name}</td>
|
||||
</tr>
|
||||
);
|
||||
@ -140,15 +230,17 @@ class FileAccessLogs extends Component {
|
||||
const { currentPage, perPage } = this.state;
|
||||
this.setState({
|
||||
perPage: parseInt(urlParams.get('per_page') || perPage),
|
||||
currentPage: parseInt(urlParams.get('page') || currentPage)
|
||||
currentPage: parseInt(urlParams.get('page') || currentPage),
|
||||
userFilteredBy: urlParams.get('email'),
|
||||
repoFilteredBy: urlParams.get('repo_id')
|
||||
}, () => {
|
||||
this.getLogsByPage(this.state.currentPage);
|
||||
});
|
||||
}
|
||||
|
||||
getLogsByPage = (page) => {
|
||||
let { perPage } = this.state;
|
||||
seafileAPI.sysAdminListFileAccessLogs(page, perPage).then((res) => {
|
||||
const { perPage, userFilteredBy, repoFilteredBy } = this.state;
|
||||
seafileAPI.sysAdminListFileAccessLogs(page, perPage, userFilteredBy, repoFilteredBy).then((res) => {
|
||||
this.setState({
|
||||
logList: res.data.file_access_log_list,
|
||||
loading: false,
|
||||
@ -169,8 +261,43 @@ class FileAccessLogs extends Component {
|
||||
}, () => this.getLogsByPage(this.initPage));
|
||||
}
|
||||
|
||||
updateURL = (obj) => {
|
||||
let url = new URL(location.href);
|
||||
let searchParams = new URLSearchParams(url.search);
|
||||
for (let key in obj) {
|
||||
obj[key] == null ?
|
||||
searchParams.delete(key) :
|
||||
searchParams.set(key, obj[key]);
|
||||
}
|
||||
url.search = searchParams.toString();
|
||||
navigate(url.toString());
|
||||
}
|
||||
|
||||
filterByUser = (email) => {
|
||||
this.setState({
|
||||
userFilteredBy: email
|
||||
}, () => {
|
||||
this.getLogsByPage(this.initPage);
|
||||
this.updateURL({'email': email});
|
||||
});
|
||||
}
|
||||
|
||||
filterByRepo = (repoID) => {
|
||||
this.setState({
|
||||
repoFilteredBy: repoID
|
||||
}, () => {
|
||||
this.getLogsByPage(this.initPage);
|
||||
this.updateURL({'repo_id': repoID});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let { logList, currentPage, perPage, hasNextPage, isExportExcelDialogOpen } = this.state;
|
||||
const {
|
||||
logList,
|
||||
userFilteredBy, repoFilteredBy,
|
||||
currentPage, perPage, hasNextPage,
|
||||
isExportExcelDialogOpen
|
||||
} = this.state;
|
||||
return (
|
||||
<Fragment>
|
||||
<MainPanelTopbar>
|
||||
@ -184,6 +311,10 @@ class FileAccessLogs extends Component {
|
||||
loading={this.state.loading}
|
||||
errorMsg={this.state.errorMsg}
|
||||
items={logList}
|
||||
userFilteredBy={userFilteredBy}
|
||||
repoFilteredBy={repoFilteredBy}
|
||||
filterByUser={this.filterByUser}
|
||||
filterByRepo={this.filterByRepo}
|
||||
currentPage={currentPage}
|
||||
perPage={perPage}
|
||||
hasNextPage={hasNextPage}
|
||||
|
@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import { Button } from 'reactstrap';
|
||||
|
||||
class ToggleFilter extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { filterBy } = this.props;
|
||||
return (
|
||||
<Button
|
||||
color='secondary'
|
||||
className="my-2 mr-2"
|
||||
onClick={this.props.toggleFilter}
|
||||
>
|
||||
<span className="text-primary">{filterBy}</span>
|
||||
<span className="ml-2 close" style={{fontSize: '1.2rem'}}>x</span>
|
||||
</Button>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ToggleFilter;
|
Loading…
Reference in New Issue
Block a user