diff --git a/frontend/src/css/log-filter.css b/frontend/src/css/log-filter.css
index 79f8613977..612ff92a2c 100644
--- a/frontend/src/css/log-filter.css
+++ b/frontend/src/css/log-filter.css
@@ -12,7 +12,6 @@
.cur-activity-modifiers {
margin-left: -0.5rem;
- padding: 0.15rem 0;
}
.cur-activity-modifiers:hover {
@@ -23,7 +22,7 @@
.cur-activity-modifiers .toggle-icon {
color: #999;
}
-
+
.activity-modifier-selector-container {
width: 320px;
background: #fff;
diff --git a/frontend/src/pages/dashboard/log-repo-selector.js b/frontend/src/pages/dashboard/log-repo-selector.js
index 49c826a307..6799490d5a 100644
--- a/frontend/src/pages/dashboard/log-repo-selector.js
+++ b/frontend/src/pages/dashboard/log-repo-selector.js
@@ -3,13 +3,15 @@ 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';
+import { systemAdminAPI } from '../../utils/system-admin-api';
class LogRepoSelector extends Component {
constructor(props) {
super(props);
this.state = {
- isOpen: false,
query: '',
isLoading: false,
searchResults: []
@@ -27,25 +29,14 @@ class LogRepoSelector extends Component {
}
handleClickOutside = (e) => {
- const { isOpen } = this.state;
- if (isOpen && !this.repoSelector.contains(e.target)) {
- this.togglePopover();
+ if (this.props.isOpen && !this.repoSelector.contains(e.target)) {
+ this.props.onToggle();
}
};
- togglePopover = () => {
- const { isOpen } = this.state;
- if (isOpen) {
- this.props.onSelect(null, true);
- }
- this.setState({
- isOpen: !isOpen
- });
- };
-
onToggleClick = (e) => {
e.stopPropagation();
- this.togglePopover();
+ this.props.onToggle();
};
onQueryChange = (e) => {
@@ -60,15 +51,18 @@ class LogRepoSelector extends Component {
this.setState({ isLoading: true });
setTimeout(() => {
if (this.finalValue === value) {
- const filteredItems = this.props.items.filter(item =>
- item.name.toLowerCase().includes(value.trim().toLowerCase())
- );
- this.setState({
- searchResults: filteredItems,
- isLoading: false
+ systemAdminAPI.sysAdminSearchRepos(value).then((res) => {
+ this.setState({
+ searchResults: res.data.repo_list,
+ isLoading: false
+ });
+ }).catch(error => {
+ this.setState({ isLoading: false });
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
});
}
- }, 300);
+ }, 500);
} else {
this.setState({ searchResults: [] });
}
@@ -80,12 +74,12 @@ class LogRepoSelector extends Component {
};
render() {
- const { isOpen, query, isLoading, searchResults } = this.state;
- const { selectedItems } = this.props;
+ const { query, isLoading, searchResults } = this.state;
+ const { selectedItems, isOpen } = this.props;
const displayItems = query.trim() ? searchResults : this.props.items;
return (
-
+
{selectedItems.length > 0 ? (
<>
@@ -123,10 +117,10 @@ class LogRepoSelector extends Component {
) : (
displayItems.map((item, index) => {
- const isSelected = selectedItems.some(selected => selected.repo_id === item.repo_id);
+ const isSelected = selectedItems.some(selected => selected.id === item.id);
return (
- {this.toggleSelectItem(e, item);}}
>
@@ -149,7 +143,9 @@ class LogRepoSelector extends Component {
LogRepoSelector.propTypes = {
items: PropTypes.array.isRequired,
selectedItems: PropTypes.array.isRequired,
- onSelect: PropTypes.func.isRequired
+ onSelect: PropTypes.func.isRequired,
+ isOpen: PropTypes.bool.isRequired,
+ onToggle: PropTypes.func.isRequired
};
export default LogRepoSelector;
diff --git a/frontend/src/pages/dashboard/log-user-selector.js b/frontend/src/pages/dashboard/log-user-selector.js
index 568adc49ed..61d510dfc8 100644
--- a/frontend/src/pages/dashboard/log-user-selector.js
+++ b/frontend/src/pages/dashboard/log-user-selector.js
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { Input } from 'reactstrap';
import { gettext } from '../../utils/constants';
import '../../css/log-filter.css';
-import { systemAdminAPI } from '../../utils/system-admin-api'
+import { systemAdminAPI } from '../../utils/system-admin-api';
import { Utils } from '../../utils/utils';
import toaster from '../../components/toast';
@@ -12,7 +12,6 @@ class LogUserSelector extends Component {
constructor(props) {
super(props);
this.state = {
- isOpen: false,
query: '',
searchResults: [],
isLoading: false
@@ -30,25 +29,14 @@ class LogUserSelector extends Component {
}
handleClickOutside = (e) => {
- const { isOpen } = this.state;
- if (isOpen && !this.userSelector.contains(e.target)) {
- this.togglePopover();
+ if (this.props.isOpen && !this.userSelector.contains(e.target)) {
+ this.props.onToggle();
}
};
- togglePopover = () => {
- const { isOpen } = this.state;
- if (isOpen) {
- this.props.onSelect(null, true);
- }
- this.setState({
- isOpen: !isOpen
- });
- };
-
onToggleClick = (e) => {
e.stopPropagation();
- this.togglePopover();
+ this.props.onToggle();
};
onQueryChange = (e) => {
@@ -86,12 +74,12 @@ class LogUserSelector extends Component {
};
render() {
- const { isOpen, query, searchResults, isLoading } = this.state;
- const { selectedItems } = this.props;
+ const { query, isLoading, searchResults } = this.state;
+ const { selectedItems, isOpen } = this.props;
const displayItems = query.trim() ? searchResults : this.props.items;
return (
-
+
{selectedItems.length > 0 ? (
<>
@@ -133,8 +121,8 @@ class LogUserSelector extends Component {
displayItems.map((item, index) => {
const isSelected = selectedItems.some(selected => selected.email === item.email);
return (
- {this.toggleSelectItem(e, item);}}
>
@@ -157,7 +145,9 @@ class LogUserSelector extends Component {
LogUserSelector.propTypes = {
items: PropTypes.array.isRequired,
selectedItems: PropTypes.array.isRequired,
- onSelect: PropTypes.func.isRequired
+ onSelect: PropTypes.func.isRequired,
+ isOpen: PropTypes.bool.isRequired,
+ onToggle: PropTypes.func.isRequired
};
export default LogUserSelector;
diff --git a/frontend/src/pages/sys-admin/logs-page/file-access-logs.js b/frontend/src/pages/sys-admin/logs-page/file-access-logs.js
index b979e29992..e04343dccf 100644
--- a/frontend/src/pages/sys-admin/logs-page/file-access-logs.js
+++ b/frontend/src/pages/sys-admin/logs-page/file-access-logs.js
@@ -13,11 +13,9 @@ 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 LogUserSelector from '../../dashboard/log-user-selector';
import LogRepoSelector from '../../dashboard/log-repo-selector';
dayjs.extend(relativeTime);
@@ -39,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
@@ -56,7 +46,6 @@ class Content extends Component {
render() {
const {
loading, errorMsg, items,
- userFilteredBy, repoFilteredBy,
perPage, currentPage, hasNextPage
} = this.props;
if (loading) {
@@ -70,20 +59,6 @@ class Content extends Component {
);
const table = (
-
- {userFilteredBy && (
-
- )}
- {repoFilteredBy && (
-
- )}
-
@@ -103,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}
/>);
})}
@@ -137,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
};
@@ -151,7 +118,6 @@ class Item extends Component {
super(props);
this.state = {
isHighlighted: false,
- isOpIconShown: false
};
}
@@ -159,7 +125,6 @@ class Item extends Component {
if (!this.props.isFreezed) {
this.setState({
isHighlighted: true,
- isOpIconShown: true
});
}
};
@@ -168,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 (
- {isOpIconShown && !userFilteredBy && (
-
- )}
|
{item.event_type} |
{item.ip}{' / '}{item.device || '--'} |
{dayjs(item.time).fromNow()} |
{item.repo_name ? item.repo_name : gettext('Deleted')}
- {isOpIconShown && item.repo_name && !repoFilteredBy && (
-
- )}
|
{item.file_or_dir_name} |
@@ -232,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 {
@@ -252,6 +187,9 @@ class FileAccessLogs extends Component {
isExportExcelDialogOpen: false,
availableUsers: [],
selectedUsers: [],
+ availableRepos: [],
+ selectedRepos: [],
+ openSelector: null,
};
this.initPage = 1;
}
@@ -266,8 +204,6 @@ 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.getAvailableUsers();
this.getLogsByPage(this.state.currentPage);
@@ -275,9 +211,9 @@ class FileAccessLogs extends Component {
}
getLogsByPage = (page) => {
- const { perPage, userFilteredBy, repoFilteredBy, selectedUsers } = this.state;
+ const { perPage, selectedUsers, selectedRepos } = this.state;
let emails = selectedUsers.map(user => user.email);
- systemAdminAPI.sysAdminListFileAccessLogs(page, perPage, emails, repoFilteredBy).then((res) => {
+ systemAdminAPI.sysAdminListFileAccessLogs(page, perPage, emails, selectedRepos).then((res) => {
this.setState({
logList: res.data.file_access_log_list,
loading: false,
@@ -310,24 +246,6 @@ class FileAccessLogs extends Component {
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 });
- });
- };
-
getAvailableUsers = () => {
systemAdminAPI.sysAdminListUsers().then((res) => {
this.setState({
@@ -343,7 +261,7 @@ class FileAccessLogs extends Component {
handleUserFilter = (user, shouldFetchData = true) => {
const { selectedUsers } = this.state;
let newSelectedUsers;
-
+
if (user === null) {
newSelectedUsers = selectedUsers;
} else {
@@ -365,14 +283,65 @@ class FileAccessLogs extends Component {
});
};
+ getAvailableRepos = () => {
+ systemAdminAPI.sysAdminListRepos().then((res) => {
+ this.setState({
+ availableRepos: res.data.repos
+ });
+ }).catch((error) => {
+ this.setState({
+ errorMsg: Utils.getErrorMsg(error, true)
+ });
+ });
+ };
+
+ 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);
+ }
+ });
+ };
+
+ handleSelectorToggle = (selectorType) => {
+ const { openSelector } = this.state;
+ const wasOpen = openSelector === selectorType;
+
+ this.setState({
+ openSelector: wasOpen ? null : selectorType
+ }, () => {
+ if (wasOpen) {
+ this.getLogsByPage();
+ }
+ });
+ };
+
render() {
const {
logList,
- userFilteredBy, repoFilteredBy,
currentPage, perPage, hasNextPage,
isExportExcelDialogOpen,
availableUsers,
- selectedUsers
+ selectedUsers,
+ availableRepos,
+ selectedRepos
} = this.state;
return (
@@ -384,25 +353,33 @@ class FileAccessLogs extends Component {
-
+ this.handleSelectorToggle('user')}
+ />
+
+ this.handleSelectorToggle('repo')}
+ />
+
+
-
diff --git a/frontend/src/pages/sys-admin/logs-page/file-transfer-log.js b/frontend/src/pages/sys-admin/logs-page/file-transfer-log.js
index a47fd9f7f0..11827ade1c 100644
--- a/frontend/src/pages/sys-admin/logs-page/file-transfer-log.js
+++ b/frontend/src/pages/sys-admin/logs-page/file-transfer-log.js
@@ -207,7 +207,7 @@ class FIleTransferLogs extends Component {
handleUserFilter = (user, shouldFetchData = true) => {
const { selectedUsers } = this.state;
let newSelectedUsers;
-
+
if (user === null) {
newSelectedUsers = selectedUsers;
} else {
@@ -239,12 +239,12 @@ class FIleTransferLogs extends Component {
-
+
{
const { selectedUsers } = this.state;
let newSelectedUsers;
-
+
if (user === null) {
newSelectedUsers = selectedUsers;
} else {
@@ -259,10 +259,10 @@ class FileUpdateLogs extends Component {
this.getLogsByPage(this.initPage));
};
- handleUserFilter = (user, shouldFetchData = true) => {
+ handleUserFilter = (user) => {
const { selectedUsers } = this.state;
let newSelectedUsers;
-
+
if (user === null) {
newSelectedUsers = selectedUsers;
} else {
@@ -216,8 +217,15 @@ class LoginLogs extends Component {
this.setState({
selectedUsers: newSelectedUsers,
currentPage: 1
+ });
+ };
+
+ toggleUserSelector = () => {
+ const { isUserSelectorOpen } = this.state;
+ this.setState({
+ isUserSelectorOpen: !isUserSelectorOpen
}, () => {
- if (shouldFetchData) {
+ if (!this.state.isUserSelectorOpen) {
this.getLogsByPage(1);
}
});
@@ -236,10 +244,11 @@ class LoginLogs extends Component {
{
const { selectedUsers } = this.state;
let newSelectedUsers;
-
+
if (user === null) {
newSelectedUsers = selectedUsers;
} else {
@@ -234,12 +234,12 @@ class SharePermissionLogs extends Component {
-
+
repo.id).join(',');
}
return this.req.get(url, { params: params });
}
diff --git a/seahub/api2/endpoints/admin/logs.py b/seahub/api2/endpoints/admin/logs.py
index c6f455697a..1710aeba80 100644
--- a/seahub/api2/endpoints/admin/logs.py
+++ b/seahub/api2/endpoints/admin/logs.py
@@ -120,16 +120,17 @@ class AdminLogsFileAccessLogs(APIView):
if not is_valid_email(user_selected):
error_msg = 'email %s invalid.' % user_selected
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
-
- repo_id_selected = request.GET.get('repo_id', None)
- if repo_id_selected and not is_valid_repo_id_format(repo_id_selected):
- error_msg = 'repo_id %s invalid.' % repo_id_selected
- return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+ repos = request.GET.get('repos')
+ repos = repos.split(',') if repos else []
+ for repo_selected in repos:
+ if not is_valid_repo_id_format(repo_selected):
+ error_msg = 'repo_id %s invalid.' % repo_selected
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
start = per_page * (current_page - 1)
limit = per_page + 1
- events = get_log_events_by_type_users_repo('file_audit', emails, repo_id_selected, start, limit) or []
+ events = get_log_events_by_type_users_repo('file_audit', emails, repos, start, limit) or []
if len(events) > per_page:
events = events[:per_page]