mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-12 13:24:52 +00:00
Group to department (#6411)
* change group to department * update group quota * admin change group * optimize code * Update change-group-dialog.js * update * code-optimize * code-optimize * Update groups.py * update * fix group-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:
49
frontend/src/components/dialog/change-group-dialog.js
Normal file
49
frontend/src/components/dialog/change-group-dialog.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Button, Modal, ModalHeader, ModalFooter, ModalBody } from 'reactstrap';
|
||||||
|
import { Utils } from '../../utils/utils';
|
||||||
|
import { gettext } from '../../utils/constants';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
groupName: PropTypes.string.isRequired,
|
||||||
|
changeGroup2Department: PropTypes.func.isRequired,
|
||||||
|
toggleDialog: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChangeGroupDialog extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
selectedOption: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
submit = () => {
|
||||||
|
this.props.changeGroup2Department();
|
||||||
|
this.props.toggleDialog();
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const groupName = '<span class="op-target">' + Utils.HTMLescape(this.props.groupName) + '</span>';
|
||||||
|
const msg = gettext('Are you sure to change Group {placeholder} to Department ?').replace('{placeholder}', groupName);
|
||||||
|
return (
|
||||||
|
<Modal isOpen={true} toggle={this.props.toggleDialog}>
|
||||||
|
<ModalHeader toggle={this.props.toggleDialog}>
|
||||||
|
{gettext('Change group to departmen')}
|
||||||
|
</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<p dangerouslySetInnerHTML={{ __html: msg }}></p>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button color="secondary" onClick={this.props.toggleDialog}>{gettext('Cancel')}</Button>
|
||||||
|
<Button color="primary" onClick={this.submit}>{gettext('Submit')}</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangeGroupDialog.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default ChangeGroupDialog;
|
@@ -8,6 +8,8 @@ import { Utils } from '../../utils/utils';
|
|||||||
import toaster from '../../components/toast';
|
import toaster from '../../components/toast';
|
||||||
import OrgGroupInfo from '../../models/org-group';
|
import OrgGroupInfo from '../../models/org-group';
|
||||||
import MainPanelTopbar from './main-panel-topbar';
|
import MainPanelTopbar from './main-panel-topbar';
|
||||||
|
import ChangeGroupDialog from '../../components/dialog/change-group-dialog';
|
||||||
|
import { orgAdminAPI } from '../../utils/org-admin-api';
|
||||||
|
|
||||||
class Search extends React.Component {
|
class Search extends React.Component {
|
||||||
|
|
||||||
@@ -132,6 +134,24 @@ class OrgGroups extends Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
changeGroupItem = (group) => {
|
||||||
|
orgAdminAPI.orgAdminGroup2Department(orgID, group.id).then(res => {
|
||||||
|
let newGroupList = this.state.orgGroups.map(item => {
|
||||||
|
if (item.id == group.id) {
|
||||||
|
item = new OrgGroupInfo(res.data);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
orgGroups: newGroupList
|
||||||
|
});
|
||||||
|
toaster.success(gettext('Successfully Change the group.'));
|
||||||
|
}).catch(error => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
searchItems = (keyword) => {
|
searchItems = (keyword) => {
|
||||||
navigate(`${siteRoot}org/groupadmin/search-groups/?query=${encodeURIComponent(keyword)}`);
|
navigate(`${siteRoot}org/groupadmin/search-groups/?query=${encodeURIComponent(keyword)}`);
|
||||||
};
|
};
|
||||||
@@ -173,6 +193,7 @@ class OrgGroups extends Component {
|
|||||||
onFreezedItem={this.onFreezedItem}
|
onFreezedItem={this.onFreezedItem}
|
||||||
onUnfreezedItem={this.onUnfreezedItem}
|
onUnfreezedItem={this.onUnfreezedItem}
|
||||||
deleteGroupItem={this.deleteGroupItem}
|
deleteGroupItem={this.deleteGroupItem}
|
||||||
|
changeGroupItem={this.changeGroupItem}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -197,6 +218,7 @@ const GroupItemPropTypes = {
|
|||||||
onFreezedItem: PropTypes.func.isRequired,
|
onFreezedItem: PropTypes.func.isRequired,
|
||||||
onUnfreezedItem: PropTypes.func.isRequired,
|
onUnfreezedItem: PropTypes.func.isRequired,
|
||||||
deleteGroupItem: PropTypes.func.isRequired,
|
deleteGroupItem: PropTypes.func.isRequired,
|
||||||
|
changeGroupItem: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class GroupItem extends React.Component {
|
class GroupItem extends React.Component {
|
||||||
@@ -206,7 +228,8 @@ class GroupItem extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
highlight: false,
|
highlight: false,
|
||||||
showMenu: false,
|
showMenu: false,
|
||||||
isItemMenuShow: false
|
isItemMenuShow: false,
|
||||||
|
isChangeDialogOpen: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,6 +276,16 @@ class GroupItem extends React.Component {
|
|||||||
toggleDelete = () => {
|
toggleDelete = () => {
|
||||||
this.props.deleteGroupItem(this.props.group);
|
this.props.deleteGroupItem(this.props.group);
|
||||||
};
|
};
|
||||||
|
toggleChange = () => {
|
||||||
|
this.props.changeGroupItem(this.props.group);
|
||||||
|
};
|
||||||
|
|
||||||
|
toggleChangeDialog = (e) => {
|
||||||
|
if (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
this.setState({ isChangeDialogOpen: !this.state.isChangeDialogOpen });
|
||||||
|
};
|
||||||
|
|
||||||
renderGroupHref = (group) => {
|
renderGroupHref = (group) => {
|
||||||
let groupInfoHref;
|
let groupInfoHref;
|
||||||
@@ -304,11 +337,19 @@ class GroupItem extends React.Component {
|
|||||||
/>
|
/>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownItem onClick={this.toggleDelete}>{gettext('Delete')}</DropdownItem>
|
<DropdownItem onClick={this.toggleDelete}>{gettext('Delete')}</DropdownItem>
|
||||||
|
<DropdownItem onClick={this.toggleChangeDialog}>{gettext('Change to department')}</DropdownItem>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
}
|
}
|
||||||
|
{this.state.isChangeDialogOpen &&
|
||||||
|
<ChangeGroupDialog
|
||||||
|
groupName={group.groupName}
|
||||||
|
changeGroup2Department={this.toggleChange}
|
||||||
|
toggleDialog={this.toggleChangeDialog} />
|
||||||
|
}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ import Paginator from '../../../components/paginator';
|
|||||||
import OpMenu from '../../../components/dialog/op-menu';
|
import OpMenu from '../../../components/dialog/op-menu';
|
||||||
import CommonOperationConfirmationDialog from '../../../components/dialog/common-operation-confirmation-dialog';
|
import CommonOperationConfirmationDialog from '../../../components/dialog/common-operation-confirmation-dialog';
|
||||||
import SysAdminTransferGroupDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-group-transfer-dialog';
|
import SysAdminTransferGroupDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-group-transfer-dialog';
|
||||||
|
import ChangeGroupDialog from '../../../components/dialog/change-group-dialog';
|
||||||
import UserLink from '../user-link';
|
import UserLink from '../user-link';
|
||||||
|
|
||||||
class Content extends Component {
|
class Content extends Component {
|
||||||
@@ -70,6 +71,7 @@ class Content extends Component {
|
|||||||
onUnfreezedItem={this.onUnfreezedItem}
|
onUnfreezedItem={this.onUnfreezedItem}
|
||||||
deleteGroup={this.props.deleteGroup}
|
deleteGroup={this.props.deleteGroup}
|
||||||
transferGroup={this.props.transferGroup}
|
transferGroup={this.props.transferGroup}
|
||||||
|
changeGroup2Department={this.props.changeGroup2Department}
|
||||||
/>);
|
/>);
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -102,6 +104,7 @@ Content.propTypes = {
|
|||||||
getListByPage: PropTypes.func.isRequired,
|
getListByPage: PropTypes.func.isRequired,
|
||||||
deleteGroup: PropTypes.func.isRequired,
|
deleteGroup: PropTypes.func.isRequired,
|
||||||
transferGroup: PropTypes.func.isRequired,
|
transferGroup: PropTypes.func.isRequired,
|
||||||
|
changeGroup2Department: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Item extends Component {
|
class Item extends Component {
|
||||||
@@ -112,7 +115,8 @@ class Item extends Component {
|
|||||||
isOpIconShown: false,
|
isOpIconShown: false,
|
||||||
highlight: false,
|
highlight: false,
|
||||||
isDeleteDialogOpen: false,
|
isDeleteDialogOpen: false,
|
||||||
isTransferDialogOpen: false
|
isTransferDialogOpen: false,
|
||||||
|
isChangeDialogOpen: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +154,9 @@ class Item extends Component {
|
|||||||
case 'Transfer':
|
case 'Transfer':
|
||||||
this.toggleTransferDialog();
|
this.toggleTransferDialog();
|
||||||
break;
|
break;
|
||||||
|
case 'Change':
|
||||||
|
this.toggleChangeDialog();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -169,6 +176,12 @@ class Item extends Component {
|
|||||||
this.setState({ isTransferDialogOpen: !this.state.isTransferDialogOpen });
|
this.setState({ isTransferDialogOpen: !this.state.isTransferDialogOpen });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
toggleChangeDialog = (e) => {
|
||||||
|
if (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
this.setState({ isChangeDialogOpen: !this.state.isChangeDialogOpen });
|
||||||
|
};
|
||||||
deleteGroup = () => {
|
deleteGroup = () => {
|
||||||
this.props.deleteGroup(this.props.item.id);
|
this.props.deleteGroup(this.props.item.id);
|
||||||
};
|
};
|
||||||
@@ -177,6 +190,10 @@ class Item extends Component {
|
|||||||
this.props.transferGroup(this.props.item.id, receiver);
|
this.props.transferGroup(this.props.item.id, receiver);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
changeGroup = () => {
|
||||||
|
this.props.changeGroup2Department(this.props.item.id);
|
||||||
|
};
|
||||||
|
|
||||||
translateOperations = (item) => {
|
translateOperations = (item) => {
|
||||||
let translateResult = '';
|
let translateResult = '';
|
||||||
switch (item) {
|
switch (item) {
|
||||||
@@ -186,6 +203,9 @@ class Item extends Component {
|
|||||||
case 'Transfer':
|
case 'Transfer':
|
||||||
translateResult = gettext('Transfer');
|
translateResult = gettext('Transfer');
|
||||||
break;
|
break;
|
||||||
|
case 'Change':
|
||||||
|
translateResult = gettext('Change to department');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return translateResult;
|
return translateResult;
|
||||||
@@ -193,7 +213,7 @@ class Item extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { item } = this.props;
|
const { item } = this.props;
|
||||||
const { isOpIconShown, isDeleteDialogOpen, isTransferDialogOpen } = this.state;
|
const { isOpIconShown, isDeleteDialogOpen, isTransferDialogOpen, isChangeDialogOpen } = this.state;
|
||||||
|
|
||||||
let groupName = '<span class="op-target">' + Utils.HTMLescape(item.name) + '</span>';
|
let groupName = '<span class="op-target">' + Utils.HTMLescape(item.name) + '</span>';
|
||||||
let deleteDialogMsg = gettext('Are you sure you want to delete {placeholder} ?').replace('{placeholder}', groupName);
|
let deleteDialogMsg = gettext('Are you sure you want to delete {placeholder} ?').replace('{placeholder}', groupName);
|
||||||
@@ -218,7 +238,7 @@ class Item extends Component {
|
|||||||
<td>
|
<td>
|
||||||
{(isOpIconShown && item.owner != 'system admin') &&
|
{(isOpIconShown && item.owner != 'system admin') &&
|
||||||
<OpMenu
|
<OpMenu
|
||||||
operations={['Delete', 'Transfer']}
|
operations={['Delete', 'Transfer', 'Change']}
|
||||||
translateOperations={this.translateOperations}
|
translateOperations={this.translateOperations}
|
||||||
onMenuItemClick={this.onMenuItemClick}
|
onMenuItemClick={this.onMenuItemClick}
|
||||||
onFreezedItem={this.props.onFreezedItem}
|
onFreezedItem={this.props.onFreezedItem}
|
||||||
@@ -243,6 +263,13 @@ class Item extends Component {
|
|||||||
toggleDialog={this.toggleTransferDialog}
|
toggleDialog={this.toggleTransferDialog}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
{isChangeDialogOpen &&
|
||||||
|
<ChangeGroupDialog
|
||||||
|
groupName={item.name}
|
||||||
|
changeGroup2Department={this.changeGroup}
|
||||||
|
toggleDialog={this.toggleChangeDialog}
|
||||||
|
/>
|
||||||
|
}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ import SysAdminCreateGroupDialog from '../../../components/dialog/sysadmin-dialo
|
|||||||
import MainPanelTopbar from '../main-panel-topbar';
|
import MainPanelTopbar from '../main-panel-topbar';
|
||||||
import Search from '../search';
|
import Search from '../search';
|
||||||
import Content from './groups-content';
|
import Content from './groups-content';
|
||||||
|
import { systemAdminAPI } from '../../../utils/system-admin-api';
|
||||||
|
|
||||||
class Groups extends Component {
|
class Groups extends Component {
|
||||||
|
|
||||||
@@ -109,6 +110,24 @@ class Groups extends Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
changeGroup2Department = (groupID) => {
|
||||||
|
systemAdminAPI.adminGroup2Department(groupID).then((res) => {
|
||||||
|
let newGroupList = this.state.groupList.map(item => {
|
||||||
|
if (item.id == groupID) {
|
||||||
|
item = res.data;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
groupList: newGroupList
|
||||||
|
});
|
||||||
|
toaster.success(gettext('Successfully Change the group.'));
|
||||||
|
}).catch((error) => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
getSearch = () => {
|
getSearch = () => {
|
||||||
return <Search
|
return <Search
|
||||||
placeholder={gettext('Search groups by name')}
|
placeholder={gettext('Search groups by name')}
|
||||||
@@ -144,6 +163,7 @@ class Groups extends Component {
|
|||||||
pageInfo={this.state.pageInfo}
|
pageInfo={this.state.pageInfo}
|
||||||
deleteGroup={this.deleteGroup}
|
deleteGroup={this.deleteGroup}
|
||||||
transferGroup={this.transferGroup}
|
transferGroup={this.transferGroup}
|
||||||
|
changeGroup2Department={this.changeGroup2Department}
|
||||||
getListByPage={this.getGroupListByPage}
|
getListByPage={this.getGroupListByPage}
|
||||||
resetPerPage={this.resetPerPage}
|
resetPerPage={this.resetPerPage}
|
||||||
curPerPage={this.state.perPage}
|
curPerPage={this.state.perPage}
|
||||||
|
58
frontend/src/utils/org-admin-api.js
Normal file
58
frontend/src/utils/org-admin-api.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import cookie from 'react-cookies';
|
||||||
|
import { siteRoot } from './constants';
|
||||||
|
|
||||||
|
class OrgAdminAPI {
|
||||||
|
|
||||||
|
init({ server, username, password, token }) {
|
||||||
|
this.server = server;
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
this.token = token;
|
||||||
|
if (this.token && this.server) {
|
||||||
|
this.req = axios.create({
|
||||||
|
baseURL: this.server,
|
||||||
|
headers: { 'Authorization': 'Token ' + this.token },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
initForSeahubUsage({ siteRoot, xcsrfHeaders }) {
|
||||||
|
if (siteRoot && siteRoot.charAt(siteRoot.length - 1) === '/') {
|
||||||
|
var server = siteRoot.substring(0, siteRoot.length - 1);
|
||||||
|
this.server = server;
|
||||||
|
} else {
|
||||||
|
this.server = siteRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.req = axios.create({
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': xcsrfHeaders,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sendPostRequest(url, form) {
|
||||||
|
if (form.getHeaders) {
|
||||||
|
return this.req.post(url, form, {
|
||||||
|
headers: form.getHeaders()
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return this.req.post(url, form);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
orgAdminGroup2Department(orgID, groupID) {
|
||||||
|
var url = this.server + '/api/v2.1/org/' + orgID + '/admin/groups/' + groupID + '/group-to-department/';
|
||||||
|
return this.req.post(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let orgAdminAPI = new OrgAdminAPI();
|
||||||
|
let xcsrfHeaders = cookie.load('sfcsrftoken');
|
||||||
|
orgAdminAPI.initForSeahubUsage({ siteRoot, xcsrfHeaders });
|
||||||
|
|
||||||
|
export { orgAdminAPI };
|
@@ -81,6 +81,11 @@ class SystemAdminAPI {
|
|||||||
return this.req.get(url);
|
return this.req.get(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adminGroup2Department(groupID) {
|
||||||
|
const url = this.server + '/api/v2.1/admin/groups/' + groupID + '/group-to-department/';
|
||||||
|
return this.req.post(url);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let systemAdminAPI = new SystemAdminAPI();
|
let systemAdminAPI = new SystemAdminAPI();
|
||||||
|
@@ -26,6 +26,8 @@ from seahub.api2.utils import api_error
|
|||||||
from seahub.api2.throttling import UserRateThrottle
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
from seahub.api2.authentication import TokenAuthentication
|
from seahub.api2.authentication import TokenAuthentication
|
||||||
from seahub.share.models import ExtraGroupsSharePermission
|
from seahub.share.models import ExtraGroupsSharePermission
|
||||||
|
from seahub.utils.ccnet_db import CcnetDB
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -398,3 +400,44 @@ class AdminDepartments(APIView):
|
|||||||
|
|
||||||
return Response(result)
|
return Response(result)
|
||||||
|
|
||||||
|
|
||||||
|
class AdminGroupToDeptView(APIView):
|
||||||
|
"""group to department"""
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAdminUser,)
|
||||||
|
throttle_classes = (UserRateThrottle,)
|
||||||
|
|
||||||
|
def post(self, request, group_id):
|
||||||
|
""" Admin change a group
|
||||||
|
|
||||||
|
group to department
|
||||||
|
|
||||||
|
Permission checking:
|
||||||
|
1. Admin user;
|
||||||
|
"""
|
||||||
|
if not request.user.admin_permissions.can_manage_group():
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
|
||||||
|
|
||||||
|
# recourse check
|
||||||
|
group_id = int(group_id)
|
||||||
|
group = ccnet_api.get_group(group_id)
|
||||||
|
if group.creator_name == 'system admin':
|
||||||
|
error_msg = 'Group %s is already a department' % group_id
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
if not group:
|
||||||
|
error_msg = 'Group %d not found.' % group_id
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# group to department
|
||||||
|
try:
|
||||||
|
ccnet_db = CcnetDB()
|
||||||
|
ccnet_db.change_groups_into_departments(group_id)
|
||||||
|
seafile_api.set_group_quota(group_id, -2)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
error_msg = 'Internal Server Error'
|
||||||
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||||
|
|
||||||
|
group_info = get_group_info(group_id)
|
||||||
|
return Response(group_info)
|
||||||
|
@@ -6,7 +6,7 @@ from rest_framework.views import APIView
|
|||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.authentication import SessionAuthentication
|
from rest_framework.authentication import SessionAuthentication
|
||||||
|
|
||||||
from seaserv import ccnet_api
|
from seaserv import ccnet_api, seafile_api
|
||||||
|
|
||||||
from seahub.api2.permissions import IsProVersion, IsOrgAdminUser
|
from seahub.api2.permissions import IsProVersion, IsOrgAdminUser
|
||||||
from seahub.api2.throttling import UserRateThrottle
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
@@ -18,6 +18,8 @@ from seahub.avatar.templatetags.group_avatar_tags import api_grp_avatar_url, get
|
|||||||
from seahub.base.templatetags.seahub_tags import email2nickname, email2contact_email
|
from seahub.base.templatetags.seahub_tags import email2nickname, email2contact_email
|
||||||
from seahub.utils.ccnet_db import CcnetDB
|
from seahub.utils.ccnet_db import CcnetDB
|
||||||
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
|
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
|
||||||
|
from seahub.admin_log.signals import admin_operation
|
||||||
|
from seahub.admin_log.models import GROUP_TRANSFER
|
||||||
|
|
||||||
from pysearpc import SearpcError
|
from pysearpc import SearpcError
|
||||||
|
|
||||||
@@ -247,3 +249,48 @@ class OrgAdminDepartments(APIView):
|
|||||||
result.append(department_info)
|
result.append(department_info)
|
||||||
|
|
||||||
return Response(result)
|
return Response(result)
|
||||||
|
|
||||||
|
|
||||||
|
class OrgAdminGroupToDeptView(APIView):
|
||||||
|
"""org group to department"""
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsProVersion, IsOrgAdminUser)
|
||||||
|
throttle_classes = (UserRateThrottle,)
|
||||||
|
|
||||||
|
def post(self, request, org_id, group_id):
|
||||||
|
# resource check
|
||||||
|
org_id = int(org_id)
|
||||||
|
if not ccnet_api.get_org_by_id(org_id):
|
||||||
|
error_msg = 'Organization %s not found.' % org_id
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
group_id = int(group_id)
|
||||||
|
if get_org_id_by_group(group_id) != org_id:
|
||||||
|
error_msg = 'Group %s not found.' % group_id
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
group = ccnet_api.get_group(group_id)
|
||||||
|
if group.creator_name == 'system admin':
|
||||||
|
error_msg = 'Group %s is already a department' % group_id
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# group to department
|
||||||
|
ccnet_db = CcnetDB()
|
||||||
|
ccnet_db.change_groups_into_departments(group_id)
|
||||||
|
seafile_api.set_group_quota(group_id, -2)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
error_msg = 'Internal Server Error'
|
||||||
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||||
|
|
||||||
|
group = ccnet_api.get_group(group_id)
|
||||||
|
group_info = {
|
||||||
|
"id": group.id,
|
||||||
|
"group_name": group.group_name,
|
||||||
|
"ctime": timestamp_to_isoformat_timestr(group.timestamp),
|
||||||
|
"creator_email": group.creator_name,
|
||||||
|
"creator_name": email2nickname(group.creator_name),
|
||||||
|
'creator_contact_email': email2contact_email(group.creator_name),
|
||||||
|
}
|
||||||
|
return Response(group_info)
|
||||||
|
@@ -13,7 +13,8 @@ from .api.group_members import AdminGroupMembers, AdminGroupMember
|
|||||||
from .api.admin.users import OrgAdminUser, OrgAdminUsers, OrgAdminSearchUser, \
|
from .api.admin.users import OrgAdminUser, OrgAdminUsers, OrgAdminSearchUser, \
|
||||||
OrgAdminImportUsers, OrgAdminInviteUser
|
OrgAdminImportUsers, OrgAdminInviteUser
|
||||||
from .api.admin.user_set_password import OrgAdminUserSetPassword
|
from .api.admin.user_set_password import OrgAdminUserSetPassword
|
||||||
from .api.admin.groups import OrgAdminGroups, OrgAdminGroup, OrgAdminSearchGroup, OrgAdminDepartments
|
from .api.admin.groups import OrgAdminGroups, OrgAdminGroup, OrgAdminSearchGroup, \
|
||||||
|
OrgAdminDepartments, OrgAdminGroupToDeptView
|
||||||
from .api.admin.repos import OrgAdminRepos, OrgAdminRepo
|
from .api.admin.repos import OrgAdminRepos, OrgAdminRepo
|
||||||
from .api.admin.trash_libraries import OrgAdminTrashLibraries, OrgAdminTrashLibrary
|
from .api.admin.trash_libraries import OrgAdminTrashLibraries, OrgAdminTrashLibrary
|
||||||
from .api.admin.info import OrgAdminInfo
|
from .api.admin.info import OrgAdminInfo
|
||||||
@@ -72,6 +73,7 @@ urlpatterns = [
|
|||||||
path('<int:org_id>/admin/search-group/', OrgAdminSearchGroup.as_view(), name='api-v2.1-org-admin-search-group'),
|
path('<int:org_id>/admin/search-group/', OrgAdminSearchGroup.as_view(), name='api-v2.1-org-admin-search-group'),
|
||||||
path('<int:org_id>/admin/groups/<int:group_id>/', OrgAdminGroup.as_view(), name='api-admin-group'),
|
path('<int:org_id>/admin/groups/<int:group_id>/', OrgAdminGroup.as_view(), name='api-admin-group'),
|
||||||
path('<int:org_id>/admin/groups/<int:group_id>/libraries/', AdminGroupLibraries.as_view(), name='api-admin-group-libraries'),
|
path('<int:org_id>/admin/groups/<int:group_id>/libraries/', AdminGroupLibraries.as_view(), name='api-admin-group-libraries'),
|
||||||
|
path('<int:org_id>/admin/groups/<int:group_id>/group-to-department/', OrgAdminGroupToDeptView.as_view(), name='api-admin-group-to-department'),
|
||||||
re_path(r'^(?P<org_id>\d+)/admin/groups/(?P<group_id>\d+)/libraries/(?P<repo_id>[-0-9a-f]{36})/$', AdminGroupLibrary.as_view(), name='api-admin-group-library'),
|
re_path(r'^(?P<org_id>\d+)/admin/groups/(?P<group_id>\d+)/libraries/(?P<repo_id>[-0-9a-f]{36})/$', AdminGroupLibrary.as_view(), name='api-admin-group-library'),
|
||||||
|
|
||||||
path('<int:org_id>/admin/groups/<int:group_id>/group-owned-libraries/', AdminGroupOwnedLibraries.as_view(), name='api-admin-group-owned-libraries'),
|
path('<int:org_id>/admin/groups/<int:group_id>/group-owned-libraries/', AdminGroupOwnedLibraries.as_view(), name='api-admin-group-owned-libraries'),
|
||||||
|
@@ -151,7 +151,8 @@ from seahub.api2.endpoints.admin.system_library import AdminSystemLibrary, \
|
|||||||
AdminSystemLibraryUploadLink
|
AdminSystemLibraryUploadLink
|
||||||
from seahub.api2.endpoints.admin.default_library import AdminDefaultLibrary
|
from seahub.api2.endpoints.admin.default_library import AdminDefaultLibrary
|
||||||
from seahub.api2.endpoints.admin.trash_libraries import AdminTrashLibraries, AdminTrashLibrary
|
from seahub.api2.endpoints.admin.trash_libraries import AdminTrashLibraries, AdminTrashLibrary
|
||||||
from seahub.api2.endpoints.admin.groups import AdminGroups, AdminGroup, AdminSearchGroup, AdminDepartments
|
from seahub.api2.endpoints.admin.groups import AdminGroups, AdminGroup, AdminSearchGroup, \
|
||||||
|
AdminDepartments, AdminGroupToDeptView
|
||||||
from seahub.api2.endpoints.admin.group_libraries import AdminGroupLibraries, AdminGroupLibrary
|
from seahub.api2.endpoints.admin.group_libraries import AdminGroupLibraries, AdminGroupLibrary
|
||||||
from seahub.api2.endpoints.admin.group_members import AdminGroupMembers, AdminGroupMember
|
from seahub.api2.endpoints.admin.group_members import AdminGroupMembers, AdminGroupMember
|
||||||
from seahub.api2.endpoints.admin.shares import AdminShares
|
from seahub.api2.endpoints.admin.shares import AdminShares
|
||||||
@@ -655,6 +656,7 @@ urlpatterns = [
|
|||||||
re_path(r'^api/v2.1/admin/groups/(?P<group_id>\d+)/members/(?P<email>[^/]+)/$', AdminGroupMember.as_view(), name='api-v2.1-admin-group-member'),
|
re_path(r'^api/v2.1/admin/groups/(?P<group_id>\d+)/members/(?P<email>[^/]+)/$', AdminGroupMember.as_view(), name='api-v2.1-admin-group-member'),
|
||||||
re_path(r'^api/v2.1/admin/groups/(?P<group_id>\d+)/group-owned-libraries/$', AdminGroupOwnedLibraries.as_view(), name='api-v2.1-admin-group-owned-libraries'),
|
re_path(r'^api/v2.1/admin/groups/(?P<group_id>\d+)/group-owned-libraries/$', AdminGroupOwnedLibraries.as_view(), name='api-v2.1-admin-group-owned-libraries'),
|
||||||
re_path(r'^api/v2.1/admin/groups/(?P<group_id>\d+)/group-owned-libraries/(?P<repo_id>[-0-9a-f]{36})/$', AdminGroupOwnedLibrary.as_view(), name='api-v2.1-admin-owned-group-library'),
|
re_path(r'^api/v2.1/admin/groups/(?P<group_id>\d+)/group-owned-libraries/(?P<repo_id>[-0-9a-f]{36})/$', AdminGroupOwnedLibrary.as_view(), name='api-v2.1-admin-owned-group-library'),
|
||||||
|
re_path(r'^api/v2.1/admin/groups/(?P<group_id>\d+)/group-to-department/', AdminGroupToDeptView.as_view(), name='api-v2.1-admin-group-to-department'),
|
||||||
|
|
||||||
## admin::departments
|
## admin::departments
|
||||||
re_path(r'api/v2.1/admin/departments/$', AdminDepartments.as_view(), name='api-v2.1-admin-departments'),
|
re_path(r'api/v2.1/admin/departments/$', AdminDepartments.as_view(), name='api-v2.1-admin-departments'),
|
||||||
|
@@ -163,7 +163,6 @@ class CcnetDB:
|
|||||||
|
|
||||||
return users, total_count
|
return users, total_count
|
||||||
|
|
||||||
|
|
||||||
def get_group_ids_admins_map(self, group_ids):
|
def get_group_ids_admins_map(self, group_ids):
|
||||||
group_admins = {}
|
group_admins = {}
|
||||||
group_ids_str = ','.join(str(id) for id in group_ids)
|
group_ids_str = ','.join(str(id) for id in group_ids)
|
||||||
@@ -183,3 +182,21 @@ class CcnetDB:
|
|||||||
else:
|
else:
|
||||||
group_admins[group_id] = [user]
|
group_admins[group_id] = [user]
|
||||||
return group_admins
|
return group_admins
|
||||||
|
|
||||||
|
def change_groups_into_departments(self, group_id):
|
||||||
|
sql = f"""
|
||||||
|
UPDATE `{self.db_name}`.`Group` g
|
||||||
|
SET
|
||||||
|
g.creator_name = 'system admin',
|
||||||
|
g.parent_group_id = -1
|
||||||
|
WHERE
|
||||||
|
g.group_id = {group_id}
|
||||||
|
"""
|
||||||
|
structure_sql = f"""
|
||||||
|
INSERT INTO `{self.db_name}`.`GroupStructure` (group_id, path)
|
||||||
|
VALUES ('{group_id}', '{group_id}')
|
||||||
|
"""
|
||||||
|
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute(sql)
|
||||||
|
cursor.execute(structure_sql)
|
||||||
|
Reference in New Issue
Block a user