diff --git a/frontend/src/components/select-editor/sysadmin-user-membership-editor.js b/frontend/src/components/select-editor/sysadmin-user-membership-editor.js new file mode 100644 index 0000000000..7a858129dc --- /dev/null +++ b/frontend/src/components/select-editor/sysadmin-user-membership-editor.js @@ -0,0 +1,41 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { gettext } from '../../utils/constants'; +import SelectEditor from './select-editor'; + +const propTypes = { + isTextMode: PropTypes.bool.isRequired, + isEditIconShow: PropTypes.bool.isRequired, + statusOptions: PropTypes.array.isRequired, + currentStatus: PropTypes.string.isRequired, + onStatusChanged: PropTypes.func.isRequired +}; + +class SysAdminUserMembershipEditor extends React.Component { + + translateStatus = (status) => { + switch (status) { + case 'is_org_staff': + return gettext('Admin'); + case 'not_is_org_staff': + return gettext('Member'); + } + } + + render() { + return ( + + ); + } +} + +SysAdminUserMembershipEditor.propTypes = propTypes; + +export default SysAdminUserMembershipEditor; diff --git a/frontend/src/pages/sys-admin/orgs/org-users.js b/frontend/src/pages/sys-admin/orgs/org-users.js index af80de93cd..b2e921a770 100644 --- a/frontend/src/pages/sys-admin/orgs/org-users.js +++ b/frontend/src/pages/sys-admin/orgs/org-users.js @@ -8,6 +8,7 @@ import toaster from '../../../components/toast'; import EmptyTip from '../../../components/empty-tip'; import Loading from '../../../components/loading'; import SysAdminUserStatusEditor from '../../../components/select-editor/sysadmin-user-status-editor'; +import SysAdminUserMembershipEditor from '../../../components/select-editor/sysadmin-user-membership-editor'; import SysAdminAddUserDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-add-user-dialog'; import CommonOperationConfirmationDialog from '../../../components/dialog/common-operation-confirmation-dialog'; import OpMenu from '../../../components/dialog/op-menu'; @@ -50,9 +51,10 @@ class Content extends Component { {gettext('Name')} - {gettext('Status')} - {gettext('Space Used')} - {gettext('Created At')}{' / '}{gettext('Last Login')} + {gettext('Status')} + {gettext('Membership')} + {gettext('Space Used')} + {gettext('Created At')}{' / '}{gettext('Last Login')} {/* Operations */} @@ -65,6 +67,7 @@ class Content extends Component { onFreezedItem={this.onFreezedItem} onUnfreezedItem={this.onUnfreezedItem} updateStatus={this.props.updateStatus} + updateMembership={this.props.updateMembership} deleteUser={this.props.deleteUser} />); })} @@ -146,6 +149,10 @@ class Item extends Component { this.props.updateStatus(this.props.item.email, statusValue); } + updateMembership= (membershipValue) => { + this.props.updateMembership(this.props.item.email, membershipValue); + } + deleteUser = () => { const { item } = this.props; this.props.deleteUser(item.org_id, item.email); @@ -195,6 +202,15 @@ class Item extends Component { onStatusChanged={this.updateStatus} /> + + + {`${Utils.bytesToSize(item.quota_usage)} / ${item.quota_total > 0 ? Utils.bytesToSize(item.quota_total) : '--'}`} {moment(item.create_time).format('YYYY-MM-DD HH:mm:ss')}{' / '}{item.last_login ? moment(item.last_login).fromNow() : '--'} @@ -311,6 +327,22 @@ class OrgUsers extends Component { }); } + updateMembership = (email, membershipValue) => { + const isOrgStaff = membershipValue == 'is_org_staff'; + seafileAPI.sysAdminUpdateOrgUser(this.props.orgID, email, 'is_org_staff', isOrgStaff).then(res => { + let newUserList = this.state.userList.map(item => { + if (item.email == email) { + item.is_org_staff = res.data.is_org_staff; + } + return item; + }); + this.setState({userList: newUserList}); + }).catch((error) => { + let errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + }); + } + render() { const { isAddUserDialogOpen, orgName } = this.state; return ( @@ -331,6 +363,7 @@ class OrgUsers extends Component { errorMsg={this.state.errorMsg} items={this.state.userList} updateStatus={this.updateStatus} + updateMembership={this.updateMembership} deleteUser={this.deleteUser} /> diff --git a/seahub/api2/endpoints/admin/org_users.py b/seahub/api2/endpoints/admin/org_users.py index 6c8bde1dda..03c38c77e8 100644 --- a/seahub/api2/endpoints/admin/org_users.py +++ b/seahub/api2/endpoints/admin/org_users.py @@ -61,6 +61,8 @@ def get_org_user_info(org_id, user_obj): if last_login: user_info['last_login'] = datetime_to_isoformat_timestr(last_login) + user_info['is_org_staff'] = True if ccnet_api.is_org_staff(org_id, email) == 1 else False + return user_info def check_org_user(func): @@ -352,6 +354,28 @@ class AdminOrgUser(APIView): seafile_api.set_org_user_quota(org_id, email, user_quota) + # update is_org_staff + is_org_staff = request.data.get("is_org_staff", '') + if is_org_staff: + + is_org_staff = is_org_staff.lower() + if is_org_staff not in ('true', 'false'): + error_msg = 'is_org_staff invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if is_org_staff == 'true': + if ccnet_api.is_org_staff(org_id, email): + error_msg = '%s is already organization staff.' % email + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + ccnet_api.set_org_staff(org_id, email) + else: + if not ccnet_api.is_org_staff(org_id, email): + error_msg = '%s is not organization staff.' % email + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + ccnet_api.unset_org_staff(org_id, email) + user_info = get_org_user_info(org_id, user) user_info['active'] = user.is_active return Response(user_info)