From d8971b9598c9b14db3c5901cf4396ed9a787f699 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=AD=99=E6=B0=B8=E5=BC=BA?=
<11704063+s-yongqiang@user.noreply.gitee.com>
Date: Sun, 28 Apr 2024 18:22:33 +0800
Subject: [PATCH] admin add ilter api for users
---
.../src/components/dialog/dropdown-dialog.js | 93 ++++++++++++++++++
.../pages/sys-admin/users/users-content.js | 79 ++++++++++-----
frontend/src/pages/sys-admin/users/users.js | 68 +++++++++++--
seahub/api2/endpoints/admin/users.py | 26 ++++-
seahub/utils/ccnet_db.py | 95 ++++++++++++++++++-
5 files changed, 321 insertions(+), 40 deletions(-)
create mode 100644 frontend/src/components/dialog/dropdown-dialog.js
diff --git a/frontend/src/components/dialog/dropdown-dialog.js b/frontend/src/components/dialog/dropdown-dialog.js
new file mode 100644
index 00000000000..b5d963fe025
--- /dev/null
+++ b/frontend/src/components/dialog/dropdown-dialog.js
@@ -0,0 +1,93 @@
+import React, { Component } from 'react';
+import { gettext } from '../../utils/constants';
+import PropTypes from 'prop-types';
+const { availableRoles } = window.sysadmin.pageOptions;
+
+class DropdownComponent extends Component {
+ constructor(props) {
+ super(props);
+ }
+
+
+ handleStatusChange = (event) => {
+ this.props.handleFilterActive(event.target.value);
+ };
+
+ handleRoleChange = (event) => {
+ this.props.handleFilterRole(event.target.value);
+ };
+
+ translateRole = (role) => {
+ switch (role) {
+ case 'default':
+ return gettext('Default');
+ case 'guest':
+ return gettext('Guest');
+ default:
+ return role;
+ }
+ };
+
+ render() {
+ const styles = {
+ filterBar: {
+ marginTop: '16px',
+ marginBottom: '8px',
+ display: 'flex',
+ alignItems: 'center'
+ },
+ filterContainer: {
+ marginRight: '20px',
+ },
+ filterLabel: {
+ marginRight: '8px',
+ fontSize: '14px',
+ color: '#000'
+ },
+ filterSelect: {
+ height: '30px',
+ backgroundColor: '#F5F5F5',
+ border: '1px solid #ddd',
+ color: '#333',
+ fontSize: '14px'
+ }
+ };
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+DropdownComponent.propTypes = {
+ loading: PropTypes.bool,
+ curPerPage: PropTypes.number,
+ sortBy: PropTypes.string,
+ currentPage: PropTypes.number,
+ sortOrder: PropTypes.string,
+ handleFilterActive: PropTypes.func,
+ handleFilterRole: PropTypes.func,
+ role: PropTypes.string,
+ isActive: PropTypes.string,
+ availableRoles: PropTypes.array,
+};
+export default DropdownComponent;
diff --git a/frontend/src/pages/sys-admin/users/users-content.js b/frontend/src/pages/sys-admin/users/users-content.js
index 47b352c317c..67e9f4c7bdc 100644
--- a/frontend/src/pages/sys-admin/users/users-content.js
+++ b/frontend/src/pages/sys-admin/users/users-content.js
@@ -14,18 +14,21 @@ import OpMenu from '../../../components/dialog/op-menu';
import SysAdminUserSetQuotaDialog from '../../../components/dialog/sysadmin-dialog/set-quota';
import CommonOperationConfirmationDialog from '../../../components/dialog/common-operation-confirmation-dialog';
import UserLink from '../user-link';
-
+import DropdownComponent from '../../../components/dialog/dropdown-dialog';
const { availableRoles, availableAdminRoles, institutions } = window.sysadmin.pageOptions;
+
+
class Content extends Component {
constructor(props) {
super(props);
this.state = {
- isItemFreezed: false
+ isItemFreezed: false,
};
}
+
toggleItemFreezed = (isFreezed) => {
this.setState({ isItemFreezed: isFreezed });
};
@@ -39,33 +42,47 @@ class Content extends Component {
};
getPreviousPage = () => {
- this.props.getListByPage(this.props.currentPage - 1);
+ this.props.getListByPage(this.props.currentPage - 1, this.props.is_active, this.props.role);
};
getNextPage = () => {
- this.props.getListByPage(this.props.currentPage + 1);
+ this.props.getListByPage(this.props.currentPage + 1, this.props.is_active, this.props.role);
};
sortByQuotaUsage = (e) => {
e.preventDefault();
this.props.sortByQuotaUsage();
};
-
render() {
const {
isAdmin, loading, errorMsg, items, isAllUsersSelected,
curPerPage, hasNextPage, currentPage,
sortBy, sortOrder
} = this.props;
+
if (loading) {
return ;
} else if (errorMsg) {
return {errorMsg}
;
} else {
const emptyTip = (
-
- {gettext('No users')}
-
+
+ {this.props.currentItem == 'database' &&
+
+
+ }
+
+
+ {gettext('No users')}
+
+
+
);
@@ -116,6 +133,14 @@ class Content extends Component {
const table = (
+ {this.props.currentItem=='database' &&
+
+ }
+
@@ -129,21 +154,24 @@ class Content extends Component {
{items.map((item, index) => {
- return ( );
+ if (index < this.props.curPerPage) {
+ return ( );
+ }
+ return null;
})}
@@ -188,6 +216,11 @@ Content.propTypes = {
curPerPage: PropTypes.number,
hasNextPage: PropTypes.bool,
sortOrder: PropTypes.string,
+ is_active: PropTypes.string,
+ role: PropTypes.string,
+ currentItem: PropTypes.string,
+ handleFilterActive: PropTypes.func,
+ handleFilterRole: PropTypes.func
};
class Item extends Component {
diff --git a/frontend/src/pages/sys-admin/users/users.js b/frontend/src/pages/sys-admin/users/users.js
index 598ef5a380f..37e53d9ae26 100644
--- a/frontend/src/pages/sys-admin/users/users.js
+++ b/frontend/src/pages/sys-admin/users/users.js
@@ -43,7 +43,9 @@ class Users extends Component {
isAddUserDialogOpen: false,
isBatchSetQuotaDialogOpen: false,
isBatchDeleteUserDialogOpen: false,
- isBatchAddAdminDialogOpen: false
+ isBatchAddAdminDialogOpen: false,
+ is_active: null,
+ role: null,
};
}
@@ -55,15 +57,16 @@ class Users extends Component {
const {
currentPage, perPage,
sortBy = '',
- sortOrder = 'asc'
+ sortOrder = 'asc',
+ is_active, role
} = this.state;
this.setState({
perPage: parseInt(urlParams.get('per_page') || perPage),
currentPage: parseInt(urlParams.get('page') || currentPage),
sortBy: urlParams.get('order_by') || sortBy,
- sortOrder: urlParams.get('direction') || sortOrder
+ sortOrder: urlParams.get('direction') || sortOrder,
}, () => {
- this.getUsersListByPage(this.state.currentPage);
+ this.getUsersListByPage(this.state.currentPage, is_active, role);
});
}
}
@@ -160,10 +163,10 @@ class Users extends Component {
});
};
- getUsersListByPage = (page) => {
+ getUsersListByPage = (page, is_active, role) => {
const { perPage, sortBy, sortOrder } = this.state;
const { isLDAPImported } = this.props;
- seafileAPI.sysAdminListUsers(page, perPage, isLDAPImported, sortBy, sortOrder).then(res => {
+ seafileAPI.sysAdminListUsers(page, perPage, isLDAPImported, sortBy, sortOrder, is_active, role).then(res => {
let users = res.data.data.map(user => {return new SysAdminUser(user);});
this.setState({
userList: users,
@@ -179,6 +182,47 @@ class Users extends Component {
});
};
+ handleUserListSelect = (users, page, hasNextPage) => {
+ this.setState({
+ userList: users,
+ loading: false,
+ hasNextPage: hasNextPage,
+ currentPage: page
+ });
+ };
+
+
+ updateURL = (page, perPage) => {
+ let url = new URL(location.href);
+ let searchParams = new URLSearchParams(url.search);
+ searchParams.set('page', page);
+ searchParams.set('per_page', perPage);
+ url.search = searchParams.toString();
+ navigate(url.toString());
+ };
+
+ handleFilterActive = (is_active) => {
+ this.setState({
+ is_active: is_active,
+ currentPage: 1
+ }, ()=>{
+ const { currentPage, perPage, is_active, role } = this.state;
+ this.updateURL(currentPage, perPage);
+ this.getUsersListByPage(currentPage, is_active, role);
+ });
+ };
+
+ handleFilterRole = (role) => {
+ this.setState({
+ role: role,
+ currentPage: 1
+ }, ()=>{
+ const { currentPage,perPage, is_active, role } = this.state;
+ this.updateURL(currentPage, perPage);
+ this.getUsersListByPage(currentPage, is_active, role);
+ });
+ };
+
sortByQuotaUsage = () => {
this.setState({
sortBy: 'quota_usage',
@@ -187,13 +231,13 @@ class Users extends Component {
}, () => {
let url = new URL(location.href);
let searchParams = new URLSearchParams(url.search);
- const { currentPage, sortBy, sortOrder } = this.state;
+ const { currentPage, sortBy, sortOrder, is_active, role } = this.state;
searchParams.set('page', currentPage);
searchParams.set('order_by', sortBy);
searchParams.set('direction', sortOrder);
url.search = searchParams.toString();
navigate(url.toString());
- this.getUsersListByPage(currentPage);
+ this.getUsersListByPage(currentPage, is_active, role);
});
};
@@ -309,7 +353,7 @@ class Users extends Component {
this.setState({
perPage: perPage
}, () => {
- this.getUsersListByPage(1);
+ this.getUsersListByPage(1, this.state.is_active, this.state.role);
});
};
@@ -468,6 +512,8 @@ class Users extends Component {
currentPage={this.state.currentPage}
hasNextPage={this.state.hasNextPage}
curPerPage={this.state.perPage}
+ is_active={this.state.is_active}
+ role={this.state.role}
resetPerPage={this.resetPerPage}
getListByPage={this.getUsersListByPage}
updateUser={this.updateUser}
@@ -477,6 +523,10 @@ class Users extends Component {
onUserSelected={this.onUserSelected}
isAllUsersSelected={this.isAllUsersSelected}
toggleSelectAllUsers={this.toggleSelectAllUsers}
+ handleUserListSelect={this.handleUserListSelect}
+ handleFilterRole={this.handleFilterRole}
+ handleFilterActive={this.handleFilterActive}
+ currentItem={this.getCurrentNavItem()}
/>
diff --git a/seahub/api2/endpoints/admin/users.py b/seahub/api2/endpoints/admin/users.py
index d4b8f861888..a0fc009f900 100644
--- a/seahub/api2/endpoints/admin/users.py
+++ b/seahub/api2/endpoints/admin/users.py
@@ -46,6 +46,7 @@
datetime_to_isoformat_timestr
from seahub.utils.user_permissions import get_user_role
from seahub.utils.repo import normalize_repo_status_code
+from seahub.utils.ccnet_db import CcnetDB
from seahub.constants import DEFAULT_ADMIN
from seahub.role_permissions.models import AdminRole
from seahub.role_permissions.utils import get_available_roles
@@ -549,7 +550,7 @@ class AdminUsers(APIView):
throttle_classes = (UserRateThrottle, )
def get_info_of_users_order_by_quota_usage(self, source, direction,
- page, per_page):
+ page, per_page, is_active=None, role=None):
# get user's quota usage info
user_usage_dict = {}
@@ -581,6 +582,13 @@ def get_info_of_users_order_by_quota_usage(self, source, direction,
# sort
users.sort(key=lambda item: item.quota_usage,
reverse=direction == 'desc')
+ if is_active == '1':
+ users = [u for u in users if u.is_active]
+ elif is_active == '0':
+ users = [u for u in users if not u.is_active]
+
+ if role:
+ users = [u for u in users if get_user_role(u) == role]
data = []
MULTI_INSTITUTION = getattr(settings, 'MULTI_INSTITUTION', False)
@@ -633,12 +641,15 @@ def get(self, request):
try:
page = int(request.GET.get('page', '1'))
per_page = int(request.GET.get('per_page', '25'))
+ is_active = request.GET.get('is_active', None)
+ role = request.GET.get('role', None)
except ValueError:
page = 1
per_page = 25
+ is_active, role = None, None
start = (page - 1) * per_page
-
+ limit = per_page + 1
source = request.GET.get('source', 'DB').lower().strip()
if source not in ['db', 'ldapimport']:
# source: 'DB' or 'LDAPImport', default is 'DB'
@@ -671,7 +682,10 @@ def get(self, request):
data = self.get_info_of_users_order_by_quota_usage(source,
direction,
page,
- per_page)
+ per_page,
+ is_active,
+ role,
+ )
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
@@ -680,7 +694,11 @@ def get(self, request):
result = {'data': data, 'total_count': total_count}
return Response(result)
else:
- users = ccnet_api.get_emailusers('DB', start, per_page)
+ try:
+ ccnet_db = CcnetDB()
+ users, total_count = ccnet_db.list_eligible_users(start, limit, is_active, role)
+ except Exception:
+ users = ccnet_api.get_emailusers('DB', start, per_page)
elif source == 'ldapimport':
ldap_users_count = multi_ldap_users_count = 0
diff --git a/seahub/utils/ccnet_db.py b/seahub/utils/ccnet_db.py
index e38ef5067a4..c0626986ab8 100644
--- a/seahub/utils/ccnet_db.py
+++ b/seahub/utils/ccnet_db.py
@@ -28,8 +28,9 @@ def get_ccnet_db_name():
import configparser
from django.db import connection
+
class CcnetGroup(object):
-
+
def __init__(self, **kwargs):
self.id = kwargs.get('group_id')
self.group_name = kwargs.get('group_name')
@@ -37,13 +38,25 @@ def __init__(self, **kwargs):
self.timestamp = kwargs.get('timestamp')
self.parent_group_id = kwargs.get('parent_group_id')
+
+class CcnetUsers(object):
+
+ def __init__(self, **kwargs):
+ self.user_id = kwargs.get('user_id')
+ self.email = kwargs.get('email')
+ self.is_staff = kwargs.get('is_staff')
+ self.is_active = kwargs.get('is_active')
+ self.ctime = kwargs.get('ctime')
+ self.role = kwargs.get('role')
+ self.passwd = kwargs.get('passwd')
+
+
class CcnetDB:
def __init__(self):
self.db_name = get_ccnet_db_name()[0]
-
def list_org_departments(self, org_id):
sql = f"""
SELECT
@@ -63,10 +76,10 @@ def list_org_departments(self, org_id):
group_id = item[0]
group_name = item[1]
creator_name = item[2]
- timestamp=item[3]
+ timestamp = item[3]
parent_group_id = item[5]
params = {
- 'group_id':group_id,
+ 'group_id': group_id,
'group_name': group_name,
'creator_name': creator_name,
'timestamp': timestamp,
@@ -75,3 +88,77 @@ def list_org_departments(self, org_id):
group_obj = CcnetGroup(**params)
groups.append(group_obj)
return groups
+
+ def list_eligible_users(self, start, limit, is_active=None, role=None):
+
+ def status(is_active):
+ return 'AND t1.is_active=%s ' % is_active
+
+ def is_role(role):
+ if role == 'default':
+ return 'AND (t2.role is null or t2.role = "default")'
+ else:
+ return 'AND t2.role = "%s"' % role
+
+ search_clause = ''
+ if is_active:
+ search_clause += status(is_active)
+ if role:
+ search_clause += is_role(role)
+
+ count_sql = f"""
+ SELECT count(1)
+ FROM
+ `{self.db_name}`.`EmailUser` t1
+ LEFT JOIN
+ `{self.db_name}`.`UserRole` t2
+ ON
+ t1.email = t2.email
+ WHERE
+ t1.email NOT LIKE '%%@seafile_group' %s
+ ORDER BY t1.id
+ """ % search_clause
+
+ sql = f"""
+ SELECT t1.id, t1.email, t1.is_staff, t1.is_active, t1.ctime, t2.role, t1.passwd
+ FROM
+ `{self.db_name}`.`EmailUser` t1
+ LEFT JOIN
+ `{self.db_name}`.`UserRole` t2
+ ON
+ t1.email = t2.email
+ WHERE
+ t1.email NOT LIKE '%%@seafile_group' %s
+ ORDER BY t1.id
+ LIMIT {limit} OFFSET {start}
+ """ % search_clause
+
+ users = []
+ total = 0
+ with connection.cursor() as cursor:
+ cursor.execute(count_sql)
+ cursor.execute(count_sql)
+ total_count = int(cursor.fetchone()[0])
+
+ cursor.execute(sql)
+ for item in cursor.fetchall():
+ user_id = item[0]
+ email = item[1]
+ is_staff = item[2]
+ is_active = item[3]
+ ctime = item[4]
+ role = item[5]
+ passwd = item[6]
+ params = {
+ 'user_id': user_id,
+ 'email': email,
+ 'is_staff': is_staff,
+ 'is_active': is_active,
+ 'ctime': ctime,
+ 'role': role,
+ 'passwd': passwd
+ }
+ users_obj = CcnetUsers(**params)
+ users.append(users_obj)
+
+ return users, total_count