1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-28 16:17:02 +00:00

Optimize fileter log (#7669)

* filter log v1

* optimize log user selector

* optimize ui and filter repo

* optimize ui of file update and permission

* admin filter completed

* optimize selector component

* optimize lint

* optimize code

* optimize filter

* update

* add filter group audit

* update parameters

* Update log-user-selector.js

* Update log-filter.css

* update var name

* update func name

---------

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
2025-04-10 13:55:15 +08:00
committed by GitHub
parent efb1ac8286
commit 8e36f412da
16 changed files with 1633 additions and 255 deletions

View File

@@ -0,0 +1,74 @@
.activity-details {
text-decoration: underline;
cursor: pointer;
}
.activity-details:hover {
color: #212529;
}
.mobile-activity-time {
display: inline-block;
margin-bottom: .2em;
}
.cur-activity-modifiers {
margin-left: -0.5rem;
}
.cur-activity-modifiers:hover {
background: #f5f5f5;
cursor: pointer;
}
.cur-activity-modifiers .toggle-icon {
color: #999;
}
.activity-modifier-selector-container {
width: 320px;
background: #fff;
border: 1px solid #e8e8e8;
margin-top: 2px;
z-index: 2;
}
.activity-selected-modifiers {
min-height: 2rem;
background: #f6f6f6;
border-bottom: 1px solid #dde2ea;
line-height: 1;
}
.activity-selected-modifier {
display: inline-flex;
align-items: center;
margin-right: 10px;
padding: 0 8px 0 2px;
border-radius: 10px;
background: #eaeaea;
}
.unselect-activity-user {
color: #909090;
cursor: pointer;
}
.unselect-activity-user:hover {
color: #5a5a5a;
}
.activity-user-list {
min-height: 4rem;
max-height: 200px;
}
.activity-user-item {
cursor: pointer;
}
.activity-user-item:hover {
background: #f5f5f5;
}
.activity-user-name {
font-size: 14px;
}

View File

@@ -235,7 +235,7 @@ class FilesActivities extends Component {
const { onlyMine } = this.props;
const { targetUsers, availableUsers } = this.state;
return (
<div className="main-panel-center">
<div className="mt-4">
<div className="cur-view-container" id="activities">
<div className="cur-view-path">
<ul className="nav">

View File

@@ -0,0 +1,154 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Input } from 'reactstrap';
import { gettext } from '../../utils/constants';
import '../../css/log-filter.css';
import { Utils } from '../../utils/utils';
import toaster from '../../components/toast';
const propTypes = {
items: PropTypes.array.isRequired,
selectedItems: PropTypes.array.isRequired,
onSelect: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
onToggle: PropTypes.func.isRequired,
searchReposFunc: PropTypes.func.isRequired,
};
class LogRepoSelector extends Component {
constructor(props) {
super(props);
this.state = {
query: '',
isLoading: false,
searchResults: []
};
this.dropdownRef = React.createRef();
this.finalValue = '';
}
componentDidMount() {
document.addEventListener('click', this.handleClickOutside);
}
componentWillUnmount() {
document.removeEventListener('click', this.handleClickOutside);
}
handleClickOutside = (e) => {
if (this.props.isOpen && !this.repoSelector.contains(e.target)) {
this.props.onToggle();
}
};
onToggleClick = (e) => {
e.stopPropagation();
this.props.onToggle();
};
onQueryChange = (e) => {
const value = e.target.value;
this.setState({ query: value });
this.searchRepos(value);
};
searchRepos = (value) => {
this.finalValue = value;
if (value.length > 0) {
this.setState({ isLoading: true });
setTimeout(() => {
if (this.finalValue === value) {
this.props.searchReposFunc(value).then((res) => {
const repos = res.data.repo_list || res.data.repos || [];
this.setState({
searchResults: repos,
isLoading: false
});
}).catch(error => {
this.setState({ isLoading: false });
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
}, 500);
} else {
this.setState({ searchResults: [] });
}
};
toggleSelectItem = (e, item) => {
e.stopPropagation();
this.props.onSelect(item, false);
};
render() {
const { query, isLoading, searchResults } = this.state;
const { selectedItems, isOpen } = this.props;
const displayItems = query.trim() ? searchResults : this.props.items;
return (
<div className="position-relative d-inline-block" ref={this.dropdownRef}>
<span className="cur-activity-modifiers d-inline-block p-2 rounded" onClick={this.onToggleClick}>
{selectedItems.length > 0 ? (
<>
<span>{gettext('Libraries:')}</span>
<span className="d-inline-block ml-1">{selectedItems.map(item => item.name).join(', ')}</span>
</>
) : gettext('Libraries')}
<i className="sf3-font sf3-font-down ml-2 toggle-icon"></i>
</span>
{isOpen && (
<div className="position-absolute activity-modifier-selector-container rounded shadow" ref={ref => this.repoSelector = ref}>
<ul className="activity-selected-modifiers px-3 py-1 list-unstyled">
{selectedItems.map((item, index) => (
<li key={index} className="activity-selected-modifier">
<i className="fas fa-folder"></i>
<span className="activity-user-name ml-2">{item.name}</span>
<i className="sf2-icon-close unselect-activity-user ml-2" onClick={(e) => {this.toggleSelectItem(e, item);}}></i>
</li>
))}
</ul>
<div className="px-3 pt-3">
<Input
type="text"
placeholder={gettext('Find libraries')}
value={query}
onChange={this.onQueryChange}
/>
</div>
<ul className="activity-user-list list-unstyled p-3 o-auto">
{isLoading ? (
<li className="text-center">{gettext('Loading...')}</li>
) : displayItems.length === 0 ? (
<li className="text-center">
{query ? gettext('Library not found') : gettext('Enter characters to start searching')}
</li>
) : (
displayItems.map((item, index) => {
const isSelected = selectedItems.some(selected => selected.id === item.id);
return (
<li key={index}
className="activity-user-item h-6 p-1 rounded d-flex justify-content-between align-items-center"
onClick={(e) => {this.toggleSelectItem(e, item);}}
>
<div>
<i className="fas fa-folder"></i>
<span className="activity-user-name ml-2">{item.name}</span>
</div>
{isSelected && <i className="sf2-icon-tick text-gray font-weight-bold"></i>}
</li>
);
})
)}
</ul>
</div>
)}
</div>
);
}
}
LogRepoSelector.propTypes = propTypes;
export default LogRepoSelector;

View File

@@ -0,0 +1,190 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Input } from 'reactstrap';
import { gettext } from '../../utils/constants';
import '../../css/log-filter.css';
import { Utils } from '../../utils/utils';
import toaster from '../../components/toast';
const propTypes = {
componentName: PropTypes.string.isRequired,
items: PropTypes.array.isRequired,
selectedItems: PropTypes.array.isRequired,
onSelect: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
onToggle: PropTypes.func.isRequired,
searchUsersFunc: PropTypes.func,
searchGroupsFunc: PropTypes.func
};
class LogUserSelector extends Component {
constructor(props) {
super(props);
this.state = {
query: '',
searchResults: [],
isLoading: false
};
this.dropdownRef = React.createRef();
this.finalValue = '';
}
componentDidMount() {
document.addEventListener('click', this.handleClickOutside);
}
componentWillUnmount() {
document.removeEventListener('click', this.handleClickOutside);
}
handleClickOutside = (e) => {
if (this.props.isOpen && !this.userSelector.contains(e.target)) {
this.props.onToggle();
}
};
onToggleClick = (e) => {
e.stopPropagation();
this.props.onToggle();
};
onQueryChange = (e) => {
const value = e.target.value;
this.setState({ query: value });
this.handleSearchUser(value);
};
handleSearchUser = (value) => {
if (!value.trim()) {
this.setState({
searchResults: []
});
return;
}
this.setState({
isLoading: true
});
this.finalValue = value;
setTimeout(() => {
if (this.finalValue === value) {
if (this.props.searchUsersFunc) {
this.props.searchUsersFunc(value).then((res) => {
const users = res.data.user_list || res.data.users || [];
this.setState({
searchResults: users,
isLoading: false
}, () => {
if (this.props.searchGroupsFunc) {
this.props.searchGroupsFunc(value).then((res) => {
const groups = res.data.group_list || res.data.groups || [];
this.setState({
searchResults: [...users, ...groups]
});
});
}
});
}).catch((error) => {
this.setState({
isLoading: false
});
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
if (this.props.searchGroupsFunc && !this.props.searchUsersFunc) {
this.props.searchGroupsFunc(value).then((res) => {
const groups = res.data.group_list || res.data.groups || [];
this.setState({
searchResults: groups,
isLoading: false
});
});
}
}
}, 500);
};
toggleSelectItem = (e, item) => {
e.stopPropagation();
this.props.onSelect(item, false);
};
render() {
const { query, isLoading, searchResults } = this.state;
const { selectedItems, isOpen } = this.props;
const displayItems = query.trim() ? searchResults : this.props.items;
return (
<div className="position-relative d-inline-block ml-2" ref={this.dropdownRef}>
<span className="cur-activity-modifiers d-inline-block p-2 rounded" onClick={this.onToggleClick}>
{selectedItems.length > 0 ? (
<>
<span>{gettext(this.props.componentName + ':')}</span>
<span className="d-inline-block ml-1">{selectedItems.map(item => item.name).join(', ')}</span>
</>
) : gettext(this.props.componentName)}
<i className="sf3-font sf3-font-down ml-2 toggle-icon"></i>
</span>
{isOpen && (
<div className="position-absolute activity-modifier-selector-container rounded shadow" ref={ref => this.userSelector = ref}>
<ul className="activity-selected-modifiers px-3 py-1 list-unstyled">
{selectedItems.map((item, index) => {
return (
<li key={index} className="activity-selected-modifier">
<img src={item.avatar_url} className="avatar w-5 h-5" alt="" />
<span className="activity-user-name ml-2">{item.name}</span>
<i className="sf2-icon-close unselect-activity-user ml-2" onClick={(e) => {this.toggleSelectItem(e, item);}}></i>
</li>
);
})}
</ul>
<div className="px-3 pt-3">
<Input
type="text"
placeholder={gettext('Find users')}
value={query}
onChange={this.onQueryChange}
/>
</div>
<ul className="activity-user-list list-unstyled p-3 o-auto">
{isLoading ? (
<li className="text-center">{gettext('Loading...')}</li>
) : displayItems.length === 0 ? (
<li className="text-center">
{query ? gettext('User not found') : gettext('Enter characters to start searching')}
</li>
) : (
displayItems.map((item, index) => {
const isSelected = selectedItems.some(selected =>
(item.email && selected.email === item.email) ||
(item.id && selected.id === item.id)
);
return (
<li key={index}
className="activity-user-item h-6 p-1 rounded d-flex justify-content-between align-items-center"
onClick={(e) => {this.toggleSelectItem(e, item);}}
>
<div>
<img src={item.avatar_url} className="avatar w-5 h-5" alt="" />
<span className="activity-user-name ml-2">{item.name}</span>
</div>
{isSelected && <i className="sf2-icon-tick text-gray font-weight-bold"></i>}
</li>
);
})
)}
</ul>
</div>
)}
</div>
);
}
}
LogUserSelector.propTypes = propTypes;
export default LogUserSelector;

View File

@@ -13,10 +13,10 @@ import Paginator from '../../../components/paginator';
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';
import LogUserSelector from '../../dashboard/log-user-selector';
import LogRepoSelector from '../../dashboard/log-repo-selector';
dayjs.extend(relativeTime);
@@ -37,14 +37,6 @@ 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
@@ -54,7 +46,6 @@ class Content extends Component {
render() {
const {
loading, errorMsg, items,
userFilteredBy, repoFilteredBy,
perPage, currentPage, hasNextPage
} = this.props;
if (loading) {
@@ -68,20 +59,6 @@ class Content extends Component {
);
const table = (
<Fragment>
<div>
{userFilteredBy && (
<ToggleFilter
filterBy={items[0].name}
toggleFilter={this.toggleFilterByUser}
/>
)}
{repoFilteredBy && (
<ToggleFilter
filterBy={items[0].repo_name}
toggleFilter={this.toggleFilterByRepo}
/>
)}
</div>
<table>
<thead>
<tr>
@@ -101,10 +78,6 @@ class Content extends Component {
item={item}
isFreezed={this.state.isItemFreezed}
toggleFreezeItem={this.toggleFreezeItem}
userFilteredBy={userFilteredBy}
repoFilteredBy={repoFilteredBy}
filterByUser={this.props.filterByUser}
filterByRepo={this.props.filterByRepo}
/>);
})}
</tbody>
@@ -135,11 +108,7 @@ Content.propTypes = {
perPage: PropTypes.number,
pageInfo: PropTypes.object,
hasNextPage: PropTypes.bool,
toggleFreezeItem: PropTypes.func,
userFilteredBy: PropTypes.string,
repoFilteredBy: PropTypes.string,
filterByUser: PropTypes.func,
filterByRepo: PropTypes.func,
toggleFreezeItem: PropTypes.func
};
@@ -149,7 +118,6 @@ class Item extends Component {
super(props);
this.state = {
isHighlighted: false,
isOpIconShown: false
};
}
@@ -157,7 +125,6 @@ class Item extends Component {
if (!this.props.isFreezed) {
this.setState({
isHighlighted: true,
isOpIconShown: true
});
}
};
@@ -166,58 +133,32 @@ class Item extends Component {
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() {
const { isHighlighted, isOpIconShown } = this.state;
const { item, userFilteredBy, repoFilteredBy } = this.props;
const { isHighlighted } = this.state;
const { item } = this.props;
return (
<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>{dayjs(item.time).fromNow()}</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>
@@ -230,10 +171,6 @@ Item.propTypes = {
item: PropTypes.object,
isFreezed: PropTypes.bool,
toggleFreezeItem: PropTypes.func,
userFilteredBy: PropTypes.string,
repoFilteredBy: PropTypes.string,
filterByUser: PropTypes.func,
filterByRepo: PropTypes.func,
};
class FileAccessLogs extends Component {
@@ -248,6 +185,11 @@ class FileAccessLogs extends Component {
currentPage: 1,
hasNextPage: false,
isExportExcelDialogOpen: false,
availableUsers: [],
selectedUsers: [],
availableRepos: [],
selectedRepos: [],
openSelector: null,
};
this.initPage = 1;
}
@@ -262,16 +204,16 @@ class FileAccessLogs extends Component {
this.setState({
perPage: parseInt(urlParams.get('per_page') || perPage),
currentPage: parseInt(urlParams.get('page') || currentPage),
userFilteredBy: urlParams.get('email'),
repoFilteredBy: urlParams.get('repo_id')
}, () => {
this.getLogsByPage(this.state.currentPage);
});
}
getLogsByPage = (page) => {
const { perPage, userFilteredBy, repoFilteredBy } = this.state;
systemAdminAPI.sysAdminListFileAccessLogs(page, perPage, userFilteredBy, repoFilteredBy).then((res) => {
const { perPage, selectedUsers, selectedRepos } = this.state;
let emails = selectedUsers.map(user => user.email);
let repos = selectedRepos.map(repo => repo.id);
systemAdminAPI.sysAdminListFileAccessLogs(page, perPage, { 'email': emails, 'repo': repos }).then((res) => {
this.setState({
logList: res.data.file_access_log_list,
loading: false,
@@ -304,30 +246,87 @@ class FileAccessLogs extends Component {
navigate(url.toString());
};
filterByUser = (email) => {
handleUserFilter = (user, shouldFetchData = true) => {
const { selectedUsers } = this.state;
let newSelectedUsers;
if (user === null) {
newSelectedUsers = selectedUsers;
} else {
const isSelected = selectedUsers.find(item => item.email === user.email);
if (isSelected) {
newSelectedUsers = selectedUsers.filter(item => item.email !== user.email);
} else {
newSelectedUsers = [...selectedUsers, user];
}
}
this.setState({
userFilteredBy: email
selectedUsers: newSelectedUsers,
currentPage: 1
}, () => {
this.getLogsByPage(this.initPage);
this.updateURL({ 'email': email });
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
filterByRepo = (repoID) => {
handleRepoFilter = (repo, shouldFetchData = true) => {
const { selectedRepos } = this.state;
let newSelectedRepos;
if (repo === null) {
newSelectedRepos = [];
} else {
const isSelected = selectedRepos.find(item => item.id === repo.id);
if (isSelected) {
newSelectedRepos = selectedRepos.filter(item => item.id !== repo.id);
} else {
newSelectedRepos = [...selectedRepos, repo];
}
}
this.setState({
repoFilteredBy: repoID
selectedRepos: newSelectedRepos,
currentPage: 1
}, () => {
this.getLogsByPage(this.initPage);
this.updateURL({ 'repo_id': repoID });
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
handleSelectorToggle = (selectorType) => {
const { openSelector } = this.state;
const wasOpen = openSelector === selectorType;
this.setState({
openSelector: wasOpen ? null : selectorType
}, () => {
if (wasOpen) {
this.getLogsByPage(1);
}
});
};
searchUsers = (value) => {
return systemAdminAPI.sysAdminSearchUsers(value);
};
searchRepos = (value) => {
return systemAdminAPI.sysAdminSearchRepos(value);
};
render() {
const {
logList,
userFilteredBy, repoFilteredBy,
currentPage, perPage, hasNextPage,
isExportExcelDialogOpen
isExportExcelDialogOpen,
availableUsers,
selectedUsers,
availableRepos,
selectedRepos,
openSelector
} = this.state;
return (
<Fragment>
@@ -338,20 +337,38 @@ class FileAccessLogs extends Component {
<div className="cur-view-container">
<LogsNav currentItem="fileAccessLogs" />
<div className="cur-view-content">
<Content
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}
getLogsByPage={this.getLogsByPage}
resetPerPage={this.resetPerPage}
/>
<Fragment>
<div className="d-flex align-items-center mb-2">
<LogUserSelector
componentName="Users"
items={availableUsers}
selectedItems={selectedUsers}
onSelect={this.handleUserFilter}
isOpen={openSelector === 'user'}
onToggle={() => this.handleSelectorToggle('user')}
searchUsersFunc={this.searchUsers}
/>
<div className="mx-3"></div>
<LogRepoSelector
items={availableRepos}
selectedItems={selectedRepos}
onSelect={this.handleRepoFilter}
isOpen={openSelector === 'repo'}
onToggle={() => this.handleSelectorToggle('repo')}
searchReposFunc={this.searchRepos}
/>
</div>
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={logList}
currentPage={currentPage}
perPage={perPage}
hasNextPage={hasNextPage}
getLogsByPage={this.getLogsByPage}
resetPerPage={this.resetPerPage}
/>
</Fragment>
</div>
</div>
</div>

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { Link } from '@gatsbyjs/reach-router';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { seafileAPI } from '../../../utils/seafile-api';
import { systemAdminAPI } from '../../../utils/system-admin-api';
import { gettext, siteRoot } from '../../../utils/constants';
import { Utils } from '../../../utils/utils';
import EmptyTip from '../../../components/empty-tip';
@@ -12,6 +12,8 @@ import Paginator from '../../../components/paginator';
import MainPanelTopbar from '../main-panel-topbar';
import UserLink from '../user-link';
import LogsNav from './logs-nav';
import LogUserSelector from '../../dashboard/log-user-selector';
import LogRepoSelector from '../../dashboard/log-repo-selector';
dayjs.extend(relativeTime);
@@ -162,6 +164,14 @@ class FIleTransferLogs extends Component {
perPage: 100,
currentPage: 1,
hasNextPage: false,
availableUsers: [],
selectedFromUsers: [],
selectedToUsers: [],
selectedToGroups: [],
selectedOperators: [],
openSelector: null,
availableRepos: [],
selectedRepos: [],
};
this.initPage = 1;
}
@@ -178,8 +188,28 @@ class FIleTransferLogs extends Component {
}
getLogsByPage = (page) => {
let { perPage } = this.state;
seafileAPI.sysAdminListFileTransferLogs(page, perPage).then((res) => {
let {
perPage,
selectedFromUsers,
selectedToUsers,
selectedToGroups,
selectedOperators,
selectedRepos
} = this.state;
const options = {
'from_email': selectedFromUsers.filter(item => item.email).map(user => user.email),
'from_group': selectedFromUsers.filter(item => !item.email).map(group => group.id),
'to_email': selectedToUsers.map(user => user.email),
'to_group': selectedToGroups.map(group => group.to_group_id || group.id),
'operator_email': selectedOperators.map(user => user.email),
'repo': selectedRepos.map(repo => repo.id)
};
systemAdminAPI.sysAdminListFileTransferLogs(
page,
perPage,
options
).then((res) => {
this.setState({
logList: res.data.repo_transfer_log_list,
loading: false,
@@ -189,7 +219,8 @@ class FIleTransferLogs extends Component {
}).catch((error) => {
this.setState({
loading: false,
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
currentPage: page,
errorMsg: Utils.getErrorMsg(error, true)
});
});
};
@@ -200,8 +231,196 @@ class FIleTransferLogs extends Component {
}, () => this.getLogsByPage(this.initPage));
};
handleFromUserFilter = (item, shouldFetchData = true) => {
const { selectedFromUsers } = this.state;
let newSelectedUsers;
if (item === null) {
newSelectedUsers = selectedFromUsers;
} else {
if (item.email) {
const isSelected = selectedFromUsers.find(user => user.email === item.email);
if (isSelected) {
newSelectedUsers = selectedFromUsers.filter(user => user.email !== item.email);
} else {
newSelectedUsers = [...selectedFromUsers, item];
}
} else {
const groupId = item.id;
const isSelected = selectedFromUsers.find(group => group.id === groupId);
if (isSelected) {
newSelectedUsers = selectedFromUsers.filter(group => group.id !== groupId);
} else {
const groupItem = {
id: groupId,
name: item.name,
from_group_id: groupId,
from_group_name: item.name
};
newSelectedUsers = [...selectedFromUsers, groupItem];
}
}
}
this.setState({
selectedFromUsers: newSelectedUsers,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
handleToUserFilter = (item, shouldFetchData = true) => {
const { selectedToUsers, selectedToGroups } = this.state;
let newSelectedUsers = selectedToUsers;
let newSelectedGroups = selectedToGroups;
if (item === null) {
newSelectedUsers = selectedToUsers;
newSelectedGroups = selectedToGroups;
} else {
if (item.email) {
const isSelected = selectedToUsers.find(user => user.email === item.email);
if (isSelected) {
newSelectedUsers = selectedToUsers.filter(user => user.email !== item.email);
} else {
newSelectedUsers = [...selectedToUsers, item];
}
} else {
const groupId = item.to_group_id || item.id;
const groupName = item.to_group_name || item.name;
const isSelected = selectedToGroups.find(group => {
const selectedGroupId = group.to_group_id || group.id;
return selectedGroupId === groupId;
});
if (isSelected) {
newSelectedGroups = selectedToGroups.filter(group => {
const selectedGroupId = group.to_group_id || group.id;
return selectedGroupId !== groupId;
});
} else {
const groupItem = {
id: groupId,
name: groupName,
to_group_id: groupId,
to_group_name: groupName
};
newSelectedGroups = [...selectedToGroups, groupItem];
}
}
}
this.setState({
selectedToUsers: newSelectedUsers,
selectedToGroups: newSelectedGroups,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
handleOperatorFilter = (user, shouldFetchData = true) => {
const { selectedOperators } = this.state;
let newSelectedUsers;
if (user === null) {
newSelectedUsers = selectedOperators;
} else {
const isSelected = selectedOperators.find(item => item.email === user.email);
if (isSelected) {
newSelectedUsers = selectedOperators.filter(item => item.email !== user.email);
} else {
newSelectedUsers = [...selectedOperators, user];
}
}
this.setState({
selectedOperators: newSelectedUsers,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
handleSelectorToggle = (selectorType) => {
const { openSelector } = this.state;
const wasOpen = openSelector === selectorType;
this.setState({
openSelector: wasOpen ? null : selectorType
}, () => {
if (wasOpen) {
this.getLogsByPage(1);
}
});
};
handleRepoFilter = (repo, shouldFetchData = true) => {
const { selectedRepos } = this.state;
let newSelectedRepos;
if (repo === null) {
newSelectedRepos = [];
} else {
const isSelected = selectedRepos.find(item => item.id === repo.id);
if (isSelected) {
newSelectedRepos = selectedRepos.filter(item => item.id !== repo.id);
} else {
newSelectedRepos = [...selectedRepos, repo];
}
}
this.setState({
selectedRepos: newSelectedRepos,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
searchUsers = (value) => {
return systemAdminAPI.sysAdminSearchUsers(value);
};
searchGroups = (value) => {
return systemAdminAPI.sysAdminSearchGroups(value);
};
searchRepos = (value) => {
return systemAdminAPI.sysAdminSearchRepos(value);
};
render() {
let { logList, currentPage, perPage, hasNextPage } = this.state;
let {
logList, currentPage, perPage, hasNextPage,
availableUsers, selectedFromUsers,
selectedToUsers, selectedToGroups,
selectedOperators,
availableRepos, selectedRepos,
openSelector
} = this.state;
const selectedToItems = [
...selectedToUsers,
...selectedToGroups.map(group => ({
id: group.to_group_id || group.id,
name: group.to_group_name || group.name,
to_group_id: group.to_group_id || group.id,
to_group_name: group.to_group_name || group.name
}))
];
return (
<Fragment>
<MainPanelTopbar {...this.props} />
@@ -209,16 +428,58 @@ class FIleTransferLogs extends Component {
<div className="cur-view-container">
<LogsNav currentItem="fileTransfer" />
<div className="cur-view-content">
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={logList}
currentPage={currentPage}
perPage={perPage}
hasNextPage={hasNextPage}
getLogsByPage={this.getLogsByPage}
resetPerPage={this.resetPerPage}
/>
<Fragment>
<div className="d-flex align-items-center mb-2">
<LogUserSelector
componentName="Transfer From"
items={availableUsers}
selectedItems={selectedFromUsers}
onSelect={this.handleFromUserFilter}
isOpen={openSelector === 'fromUser'}
onToggle={() => this.handleSelectorToggle('fromUser')}
searchUsersFunc={this.searchUsers}
searchGroupsFunc={this.searchGroups}
/>
<LogUserSelector
componentName="Transfer To"
items={availableUsers}
selectedItems={selectedToItems}
onSelect={this.handleToUserFilter}
isOpen={openSelector === 'toUser'}
onToggle={() => this.handleSelectorToggle('toUser')}
searchUsersFunc={this.searchUsers}
searchGroupsFunc={this.searchGroups}
/>
<LogUserSelector
componentName="Operator"
items={availableUsers}
selectedItems={selectedOperators}
onSelect={this.handleOperatorFilter}
isOpen={openSelector === 'operator'}
onToggle={() => this.handleSelectorToggle('operator')}
searchUsersFunc={this.searchUsers}
/>
<div className="mx-3"></div>
<LogRepoSelector
items={availableRepos}
selectedItems={selectedRepos}
onSelect={this.handleRepoFilter}
isOpen={openSelector === 'repo'}
onToggle={() => this.handleSelectorToggle('repo')}
searchReposFunc={this.searchRepos}
/>
</div>
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={logList}
currentPage={currentPage}
perPage={perPage}
hasNextPage={hasNextPage}
getLogsByPage={this.getLogsByPage}
resetPerPage={this.resetPerPage}
/>
</Fragment>
</div>
</div>
</div>

View File

@@ -15,6 +15,8 @@ import UserLink from '../user-link';
import ModalPortal from '../../../components/modal-portal';
import CommitDetails from '../../../components/dialog/commit-details';
import LogsExportExcelDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-logs-export-excel-dialog';
import LogUserSelector from '../../dashboard/log-user-selector';
import LogRepoSelector from '../../dashboard/log-repo-selector';
dayjs.extend(relativeTime);
@@ -173,6 +175,11 @@ class FileUpdateLogs extends Component {
currentPage: 1,
hasNextPage: false,
isExportExcelDialogOpen: false,
availableUsers: [],
selectedUsers: [],
availableRepos: [],
selectedRepos: [],
openSelector: null,
};
this.initPage = 1;
}
@@ -193,8 +200,10 @@ class FileUpdateLogs extends Component {
}
getLogsByPage = (page) => {
let { perPage } = this.state;
systemAdminAPI.sysAdminListFileUpdateLogs(page, perPage).then((res) => {
let { perPage, selectedUsers, selectedRepos } = this.state;
let emails = selectedUsers.map(user => user.email);
let repos = selectedRepos.map(repo => repo.id);
systemAdminAPI.sysAdminListFileUpdateLogs(page, perPage, { 'email': emails, 'repo': repos }).then((res) => {
this.setState({
logList: res.data.file_update_log_list,
loading: false,
@@ -215,8 +224,80 @@ class FileUpdateLogs extends Component {
}, () => this.getLogsByPage(this.initPage));
};
handleUserFilter = (user, shouldFetchData = true) => {
const { selectedUsers } = this.state;
let newSelectedUsers;
if (user === null) {
newSelectedUsers = selectedUsers;
} else {
const isSelected = selectedUsers.find(item => item.email === user.email);
if (isSelected) {
newSelectedUsers = selectedUsers.filter(item => item.email !== user.email);
} else {
newSelectedUsers = [...selectedUsers, user];
}
}
this.setState({
selectedUsers: newSelectedUsers,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
handleSelectorToggle = (selectorType) => {
const { openSelector } = this.state;
const wasOpen = openSelector === selectorType;
this.setState({
openSelector: wasOpen ? null : selectorType
}, () => {
if (wasOpen) {
this.getLogsByPage(1);
}
});
};
handleRepoFilter = (repo, shouldFetchData = true) => {
const { selectedRepos } = this.state;
let newSelectedRepos;
if (repo === null) {
newSelectedRepos = selectedRepos;
} else {
const isSelected = selectedRepos.find(item => item.id === repo.id);
if (isSelected) {
newSelectedRepos = selectedRepos.filter(item => item.id !== repo.id);
} else {
newSelectedRepos = [...selectedRepos, repo];
}
}
this.setState({
selectedRepos: newSelectedRepos,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
searchUsers = (value) => {
return systemAdminAPI.sysAdminSearchUsers(value);
};
searchRepos = (value) => {
return systemAdminAPI.sysAdminSearchRepos(value);
};
render() {
let { logList, currentPage, perPage, hasNextPage, isExportExcelDialogOpen } = this.state;
let { logList, currentPage, perPage, hasNextPage, isExportExcelDialogOpen, availableUsers, selectedUsers, availableRepos, selectedRepos } = this.state;
return (
<Fragment>
<MainPanelTopbar {...this.props}>
@@ -226,16 +307,38 @@ class FileUpdateLogs extends Component {
<div className="cur-view-container">
<LogsNav currentItem="fileUpdateLogs" />
<div className="cur-view-content">
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={logList}
currentPage={currentPage}
perPage={perPage}
hasNextPage={hasNextPage}
getLogsByPage={this.getLogsByPage}
resetPerPage={this.resetPerPage}
/>
<Fragment>
<div className="d-flex align-items-center mb-2">
<LogUserSelector
componentName="Users"
items={availableUsers}
selectedItems={selectedUsers}
onSelect={this.handleUserFilter}
isOpen={this.state.openSelector === 'user'}
onToggle={() => this.handleSelectorToggle('user')}
searchUsersFunc={this.searchUsers}
/>
<div className="mx-3"></div>
<LogRepoSelector
items={availableRepos}
selectedItems={selectedRepos}
onSelect={this.handleRepoFilter}
isOpen={this.state.openSelector === 'repo'}
onToggle={() => this.handleSelectorToggle('repo')}
searchReposFunc={this.searchRepos}
/>
</div>
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={logList}
currentPage={currentPage}
perPage={perPage}
hasNextPage={hasNextPage}
getLogsByPage={this.getLogsByPage}
resetPerPage={this.resetPerPage}
/>
</Fragment>
</div>
</div>
</div>

View File

@@ -3,15 +3,16 @@ import PropTypes from 'prop-types';
import { Link } from '@gatsbyjs/reach-router';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { seafileAPI } from '../../../utils/seafile-api';
import { gettext, siteRoot } from '../../../utils/constants';
import { Utils } from '../../../utils/utils';
import { systemAdminAPI } from '../../../utils/system-admin-api';
import EmptyTip from '../../../components/empty-tip';
import Loading from '../../../components/loading';
import Paginator from '../../../components/paginator';
import MainPanelTopbar from '../main-panel-topbar';
import UserLink from '../user-link';
import LogsNav from './logs-nav';
import LogUserSelector from '../../dashboard/log-user-selector';
dayjs.extend(relativeTime);
@@ -154,6 +155,11 @@ class GroupMemberAuditLogs extends Component {
perPage: 100,
currentPage: 1,
hasNextPage: false,
availableUsers: [],
selectedUsers: [],
selectedOperators: [],
selectedGroups: [],
openSelector: null,
};
this.initPage = 1;
}
@@ -170,8 +176,15 @@ class GroupMemberAuditLogs extends Component {
}
getLogsByPage = (page) => {
let { perPage } = this.state;
seafileAPI.sysAdminListGroupInviteLogs(page, perPage).then((res) => {
let { perPage, selectedUsers, selectedOperators, selectedGroups } = this.state;
const emails = {
'user_email': selectedUsers.map(user => user.email),
'operator_email': selectedOperators.map(user => user.email),
'group_id': selectedGroups.map(group => group.id)
};
systemAdminAPI.sysAdminListGroupInviteLogs(page, perPage, emails).then((res) => {
this.setState({
logList: res.data.group_invite_log_list,
loading: false,
@@ -186,6 +199,102 @@ class GroupMemberAuditLogs extends Component {
});
};
handleUserFilter = (user, shouldFetchData = true) => {
const { selectedUsers } = this.state;
let newSelectedUsers;
if (user === null) {
newSelectedUsers = selectedUsers;
} else {
const isSelected = selectedUsers.find(item => item.email === user.email);
if (isSelected) {
newSelectedUsers = selectedUsers.filter(item => item.email !== user.email);
} else {
newSelectedUsers = [...selectedUsers, user];
}
}
this.setState({
selectedUsers: newSelectedUsers,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
handleOperatorFilter = (user, shouldFetchData = true) => {
const { selectedOperators } = this.state;
let newSelectedUsers;
if (user === null) {
newSelectedUsers = selectedOperators;
} else {
const isSelected = selectedOperators.find(item => item.email === user.email);
if (isSelected) {
newSelectedUsers = selectedOperators.filter(item => item.email !== user.email);
} else {
newSelectedUsers = [...selectedOperators, user];
}
}
this.setState({
selectedOperators: newSelectedUsers,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
handleGroupFilter = (group, shouldFetchData = true) => {
const { selectedGroups } = this.state;
let newSelectedGroups;
if (group === null) {
newSelectedGroups = selectedGroups;
} else {
const isSelected = selectedGroups.find(item => item.id === group.id);
if (isSelected) {
newSelectedGroups = selectedGroups.filter(item => item.id !== group.id);
} else {
newSelectedGroups = [...selectedGroups, group];
}
}
this.setState({
selectedGroups: newSelectedGroups,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
handleSelectorToggle = (selectorType) => {
const { openSelector } = this.state;
const wasOpen = openSelector === selectorType;
this.setState({
openSelector: wasOpen ? null : selectorType
}, () => {
if (wasOpen) {
this.getLogsByPage(1);
}
});
};
searchUsers = (value) => {
return systemAdminAPI.sysAdminSearchUsers(value);
};
searchGroups = (value) => {
return systemAdminAPI.sysAdminSearchGroups(value);
};
resetPerPage = (newPerPage) => {
this.setState({
perPage: newPerPage,
@@ -193,7 +302,12 @@ class GroupMemberAuditLogs extends Component {
};
render() {
let { logList, currentPage, perPage, hasNextPage } = this.state;
let {
logList, currentPage, perPage, hasNextPage,
availableUsers, selectedUsers, selectedOperators, selectedGroups,
openSelector
} = this.state;
return (
<Fragment>
<MainPanelTopbar {...this.props} />
@@ -201,16 +315,47 @@ class GroupMemberAuditLogs extends Component {
<div className="cur-view-container">
<LogsNav currentItem="groupMember" />
<div className="cur-view-content">
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={logList}
currentPage={currentPage}
perPage={perPage}
hasNextPage={hasNextPage}
getLogsByPage={this.getLogsByPage}
resetPerPage={this.resetPerPage}
/>
<Fragment>
<div className="d-flex align-items-center mb-2">
<LogUserSelector
componentName="Member"
items={availableUsers}
selectedItems={selectedUsers}
onSelect={this.handleUserFilter}
isOpen={openSelector === 'user'}
onToggle={() => this.handleSelectorToggle('user')}
searchUsersFunc={this.searchUsers}
/>
<LogUserSelector
componentName="Group"
items={availableUsers}
selectedItems={selectedGroups}
onSelect={this.handleGroupFilter}
isOpen={openSelector === 'group'}
onToggle={() => this.handleSelectorToggle('group')}
searchGroupsFunc={this.searchGroups}
/>
<LogUserSelector
componentName="Operator"
items={availableUsers}
selectedItems={selectedOperators}
onSelect={this.handleOperatorFilter}
isOpen={openSelector === 'operator'}
onToggle={() => this.handleSelectorToggle('operator')}
searchUsersFunc={this.searchUsers}
/>
</div>
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={logList}
currentPage={currentPage}
perPage={perPage}
hasNextPage={hasNextPage}
getLogsByPage={this.getLogsByPage}
resetPerPage={this.resetPerPage}
/>
</Fragment>
</div>
</div>
</div>

View File

@@ -14,6 +14,7 @@ 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 LogUserSelector from '../../dashboard/log-user-selector';
dayjs.extend(relativeTime);
@@ -138,6 +139,9 @@ class LoginLogs extends Component {
currentPage: 1,
hasNextPage: false,
isExportExcelDialogOpen: false,
availableUsers: [],
selectedUsers: [],
isUserSelectorOpen: false,
};
this.initPage = 1;
}
@@ -158,8 +162,10 @@ class LoginLogs extends Component {
}
getLogsByPage = (page) => {
let { perPage } = this.state;
systemAdminAPI.sysAdminListLoginLogs(page, perPage).then((res) => {
let { perPage, selectedUsers } = this.state;
let emails = selectedUsers.map(user => user.email);
systemAdminAPI.sysAdminListLoginLogs(page, perPage, { 'email': emails }).then((res) => {
this.setState({
logList: res.data.login_log_list,
loading: false,
@@ -180,8 +186,44 @@ class LoginLogs extends Component {
}, () => this.getLogsByPage(this.initPage));
};
handleUserFilter = (user) => {
const { selectedUsers } = this.state;
let newSelectedUsers;
if (user === null) {
newSelectedUsers = selectedUsers;
} else {
const isSelected = selectedUsers.find(item => item.email === user.email);
if (isSelected) {
newSelectedUsers = selectedUsers.filter(item => item.email !== user.email);
} else {
newSelectedUsers = [...selectedUsers, user];
}
}
this.setState({
selectedUsers: newSelectedUsers,
currentPage: 1
});
};
toggleUserSelector = () => {
const { isUserSelectorOpen } = this.state;
this.setState({
isUserSelectorOpen: !isUserSelectorOpen
}, () => {
if (!this.state.isUserSelectorOpen) {
this.getLogsByPage(1);
}
});
};
searchUsers = (value) => {
return systemAdminAPI.sysAdminSearchUsers(value);
};
render() {
let { logList, currentPage, perPage, hasNextPage, isExportExcelDialogOpen } = this.state;
let { logList, currentPage, perPage, hasNextPage, isExportExcelDialogOpen, availableUsers, selectedUsers } = this.state;
return (
<Fragment>
<MainPanelTopbar {...this.props}>
@@ -191,16 +233,27 @@ class LoginLogs extends Component {
<div className="cur-view-container">
<LogsNav currentItem="loginLogs" />
<div className="cur-view-content">
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={logList}
currentPage={currentPage}
perPage={perPage}
hasNextPage={hasNextPage}
getLogsByPage={this.getLogsByPage}
resetPerPage={this.resetPerPage}
/>
<Fragment>
<LogUserSelector
componentName="Users"
items={availableUsers}
selectedItems={selectedUsers}
onSelect={this.handleUserFilter}
isOpen={this.state.isUserSelectorOpen}
onToggle={this.toggleUserSelector}
searchUsersFunc={this.searchUsers}
/>
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={logList}
currentPage={currentPage}
perPage={perPage}
hasNextPage={hasNextPage}
getLogsByPage={this.getLogsByPage}
resetPerPage={this.resetPerPage}
/>
</Fragment>
</div>
</div>
</div>

View File

@@ -15,6 +15,8 @@ import Paginator from '../../../components/paginator';
import MainPanelTopbar from '../main-panel-topbar';
import UserLink from '../user-link';
import LogsNav from './logs-nav';
import LogUserSelector from '../../dashboard/log-user-selector';
import LogRepoSelector from '../../dashboard/log-repo-selector';
dayjs.extend(relativeTime);
@@ -151,6 +153,13 @@ class SharePermissionLogs extends Component {
currentPage: 1,
hasNextPage: false,
isExportExcelDialogOpen: false,
availableUsers: [],
selectedFromUsers: [],
selectedToUsers: [],
selectedToGroups: [],
availableRepos: [],
selectedRepos: [],
openSelector: null,
};
this.initPage = 1;
}
@@ -171,8 +180,25 @@ class SharePermissionLogs extends Component {
}
getLogsByPage = (page) => {
let { perPage } = this.state;
systemAdminAPI.sysAdminListSharePermissionLogs(page, perPage).then((res) => {
let {
perPage,
selectedFromUsers,
selectedToUsers,
selectedToGroups,
selectedRepos
} = this.state;
const options = {
'from_email': selectedFromUsers.map(user => user.email),
'to_email': selectedToUsers.map(user => user.email),
'to_group': selectedToGroups.map(group => group.id),
'repo': selectedRepos.map(repo => repo.id)
};
systemAdminAPI.sysAdminListSharePermissionLogs(
page,
perPage,
options
).then((res) => {
this.setState({
logList: res.data.share_permission_log_list,
loading: false,
@@ -182,7 +208,7 @@ class SharePermissionLogs extends Component {
}).catch((error) => {
this.setState({
loading: false,
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
errorMsg: Utils.getErrorMsg(error, true)
});
});
};
@@ -193,8 +219,124 @@ class SharePermissionLogs extends Component {
}, () => this.getLogsByPage(this.initPage));
};
handleFromUserFilter = (user, shouldFetchData = true) => {
const { selectedFromUsers } = this.state;
let newSelectedUsers;
if (user === null) {
newSelectedUsers = selectedFromUsers;
} else {
const isSelected = selectedFromUsers.find(item => item.email === user.email);
if (isSelected) {
newSelectedUsers = selectedFromUsers.filter(item => item.email !== user.email);
} else {
newSelectedUsers = [...selectedFromUsers, user];
}
}
this.setState({
selectedFromUsers: newSelectedUsers,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
handleToUserFilter = (item, shouldFetchData = true) => {
const { selectedToUsers, selectedToGroups } = this.state;
let newSelectedUsers = selectedToUsers;
let newSelectedGroups = selectedToGroups;
if (item === null) {
newSelectedUsers = selectedToUsers;
newSelectedGroups = selectedToGroups;
} else {
if (item.email) {
const isSelected = selectedToUsers.find(user => user.email === item.email);
if (isSelected) {
newSelectedUsers = selectedToUsers.filter(user => user.email !== item.email);
} else {
newSelectedUsers = [...selectedToUsers, item];
}
} else {
const isSelected = selectedToGroups.find(group => group.id === item.id);
if (isSelected) {
newSelectedGroups = selectedToGroups.filter(group => group.id !== item.id);
} else {
newSelectedGroups = [...selectedToGroups, item];
}
}
}
this.setState({
selectedToUsers: newSelectedUsers,
selectedToGroups: newSelectedGroups,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
handleSelectorToggle = (selectorType) => {
const { openSelector } = this.state;
const wasOpen = openSelector === selectorType;
this.setState({
openSelector: wasOpen ? null : selectorType
}, () => {
if (wasOpen) {
this.getLogsByPage(1);
}
});
};
handleRepoFilter = (repo, shouldFetchData = true) => {
const { selectedRepos } = this.state;
let newSelectedRepos;
if (repo === null) {
newSelectedRepos = selectedRepos;
} else {
const isSelected = selectedRepos.find(item => item.id === repo.id);
if (isSelected) {
newSelectedRepos = selectedRepos.filter(item => item.id !== repo.id);
} else {
newSelectedRepos = [...selectedRepos, repo];
}
}
this.setState({
selectedRepos: newSelectedRepos,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
searchUsers = (value) => {
return systemAdminAPI.sysAdminSearchUsers(value);
};
searchRepos = (value) => {
return systemAdminAPI.sysAdminSearchRepos(value);
};
searchGroups = (value) => {
return systemAdminAPI.sysAdminSearchGroups(value);
};
render() {
let { logList, currentPage, perPage, hasNextPage, isExportExcelDialogOpen } = this.state;
let {
logList, currentPage, perPage, hasNextPage, isExportExcelDialogOpen,
availableUsers, selectedFromUsers, selectedToUsers,
selectedToGroups, availableRepos, selectedRepos, openSelector
} = this.state;
return (
<Fragment>
<MainPanelTopbar {...this.props}>
@@ -204,16 +346,48 @@ class SharePermissionLogs extends Component {
<div className="cur-view-container">
<LogsNav currentItem="sharePermissionLogs" />
<div className="cur-view-content">
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={logList}
currentPage={currentPage}
perPage={perPage}
hasNextPage={hasNextPage}
getLogsByPage={this.getLogsByPage}
resetPerPage={this.resetPerPage}
/>
<Fragment>
<div className="d-flex align-items-center mb-2">
<LogUserSelector
componentName="Share From"
items={availableUsers}
selectedItems={selectedFromUsers}
onSelect={this.handleFromUserFilter}
isOpen={openSelector === 'fromUser'}
onToggle={() => this.handleSelectorToggle('fromUser')}
searchUsersFunc={this.searchUsers}
/>
<LogUserSelector
componentName="Share To"
items={availableUsers}
selectedItems={[...selectedToUsers, ...selectedToGroups]}
onSelect={this.handleToUserFilter}
isOpen={openSelector === 'toUser'}
onToggle={() => this.handleSelectorToggle('toUser')}
searchUsersFunc={this.searchUsers}
searchGroupsFunc={this.searchGroups}
/>
<div className="mx-3"></div>
<LogRepoSelector
items={availableRepos}
selectedItems={selectedRepos}
onSelect={this.handleRepoFilter}
isOpen={openSelector === 'repo'}
onToggle={() => this.handleSelectorToggle('repo')}
searchReposFunc={this.searchRepos}
/>
</div>
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={logList}
currentPage={currentPage}
perPage={perPage}
hasNextPage={hasNextPage}
getLogsByPage={this.getLogsByPage}
resetPerPage={this.resetPerPage}
/>
</Fragment>
</div>
</div>
</div>

View File

@@ -2207,24 +2207,6 @@ class SeafileAPI {
return this._sendPostRequest(url, formData);
}
sysAdminListFileTransferLogs(page, perPage) {
const url = this.server + '/api/v2.1/admin/logs/repo-transfer-logs/';
let params = {
page: page,
per_page: perPage
};
return this.req.get(url, { params: params });
}
sysAdminListGroupInviteLogs(page, perPage) {
const url = this.server + '/api/v2.1/admin/logs/group-member-audit/';
let params = {
page: page,
per_page: perPage
};
return this.req.get(url, { params: params });
}
}
let seafileAPI = new SeafileAPI();

View File

@@ -680,46 +680,166 @@ class SystemAdminAPI {
return this.req.get(url);
}
sysAdminListLoginLogs(page, perPage) {
sysAdminListLoginLogs(page, perPage, options = {}) {
const url = this.server + '/api/v2.1/admin/logs/login-logs/';
let params = {
page: page,
per_page: perPage
per_page: perPage,
...options
};
return this.req.get(url, { params: params });
return this.req.get(url, {
params: params,
paramsSerializer: {
serialize: function (params) {
let list = [];
for (let key in params) {
if (Array.isArray(params[key])) {
for (let i = 0, len = params[key].length; i < len; i++) {
list.push(key + '=' + encodeURIComponent(params[key][i]));
}
} else {
list.push(key + '=' + encodeURIComponent(params[key]));
}
}
return list.join('&');
}
}
});
}
sysAdminListFileAccessLogs(page, perPage, email, repoID) {
sysAdminListFileAccessLogs(page, perPage, options = {}) {
const url = this.server + '/api/v2.1/admin/logs/file-access-logs/';
let params = {
page: page,
per_page: perPage
per_page: perPage,
...options
};
if (email != undefined) {
params.email = email;
}
if (repoID != undefined) {
params.repo_id = repoID;
}
return this.req.get(url, { params: params });
return this.req.get(url, {
params: params,
paramsSerializer: {
serialize: function (params) {
let list = [];
for (let key in params) {
if (Array.isArray(params[key])) {
for (let i = 0, len = params[key].length; i < len; i++) {
list.push(key + '=' + encodeURIComponent(params[key][i]));
}
} else {
list.push(key + '=' + encodeURIComponent(params[key]));
}
}
return list.join('&');
}
}
});
}
sysAdminListFileUpdateLogs(page, perPage) {
sysAdminListFileUpdateLogs(page, perPage, options = {}) {
const url = this.server + '/api/v2.1/admin/logs/file-update-logs/';
let params = {
page: page,
per_page: perPage
per_page: perPage,
...options
};
return this.req.get(url, { params: params });
return this.req.get(url, {
params: params,
paramsSerializer: {
serialize: function (params) {
let list = [];
for (let key in params) {
if (Array.isArray(params[key])) {
for (let i = 0, len = params[key].length; i < len; i++) {
list.push(key + '=' + encodeURIComponent(params[key][i]));
}
} else {
list.push(key + '=' + encodeURIComponent(params[key]));
}
}
return list.join('&');
}
}
});
}
sysAdminListSharePermissionLogs(page, perPage) {
sysAdminListSharePermissionLogs(page, perPage, options = {}) {
const url = this.server + '/api/v2.1/admin/logs/share-permission-logs/';
let params = {
page: page,
per_page: perPage
per_page: perPage,
...options
};
return this.req.get(url, { params: params });
return this.req.get(url, {
params: params,
paramsSerializer: {
serialize: function (params) {
let list = [];
for (let key in params) {
if (Array.isArray(params[key])) {
for (let i = 0, len = params[key].length; i < len; i++) {
list.push(key + '=' + encodeURIComponent(params[key][i]));
}
} else {
list.push(key + '=' + encodeURIComponent(params[key]));
}
}
return list.join('&');
}
}
});
}
sysAdminListFileTransferLogs(page, perPage, options = {}) {
const url = this.server + '/api/v2.1/admin/logs/repo-transfer-logs/';
let params = {
page: page,
per_page: perPage,
...options
};
return this.req.get(url, {
params: params,
paramsSerializer: {
serialize: function (params) {
let list = [];
for (let key in params) {
if (Array.isArray(params[key])) {
for (let i = 0, len = params[key].length; i < len; i++) {
list.push(key + '=' + encodeURIComponent(params[key][i]));
}
} else {
list.push(key + '=' + encodeURIComponent(params[key]));
}
}
return list.join('&');
}
}
});
}
sysAdminListGroupInviteLogs(page, perPage, options = {}) {
const url = this.server + '/api/v2.1/admin/logs/group-member-audit/';
let params = {
page: page,
per_page: perPage,
...options
};
return this.req.get(url, {
params: params,
paramsSerializer: {
serialize: function (params) {
let list = [];
for (let key in params) {
if (Array.isArray(params[key])) {
for (let i = 0, len = params[key].length; i < len; i++) {
list.push(key + '=' + encodeURIComponent(params[key][i]));
}
} else {
list.push(key + '=' + encodeURIComponent(params[key]));
}
}
return list.join('&');
}
}
});
}
sysAdminExportLogsExcel(start, end, logType) {