mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-09 02:42:47 +00:00
Add department transfer function to the administrator interface (#6014)
* Add the function of transferring departments in the administrator interface * fix admin get all departments * update * Update departments.py * update * update * add department repo transfer * Update groups.py * update --------- 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:
@@ -1,19 +1,34 @@
|
||||
import React from 'react';
|
||||
import React, {Fragment} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
||||
import {
|
||||
Button,
|
||||
Modal,
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
Nav,
|
||||
NavItem,
|
||||
NavLink,
|
||||
TabContent,
|
||||
TabPane
|
||||
} from 'reactstrap';
|
||||
import makeAnimated from 'react-select/animated';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { gettext, isPro } from '../../utils/constants';
|
||||
import {gettext, isPro, orgID} from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import toaster from '../toast';
|
||||
import UserSelect from '../user-select';
|
||||
import { SeahubSelect } from '../common/select';
|
||||
import '../../css/transfer-dialog.css';
|
||||
|
||||
const propTypes = {
|
||||
itemName: PropTypes.string.isRequired,
|
||||
toggleDialog: PropTypes.func.isRequired,
|
||||
submit: PropTypes.func.isRequired,
|
||||
canTransferToDept: PropTypes.bool
|
||||
canTransferToDept: PropTypes.bool,
|
||||
isOrgAdmin: PropTypes.bool,
|
||||
isSysAdmin: PropTypes.bool,
|
||||
|
||||
};
|
||||
|
||||
class TransferDialog extends React.Component {
|
||||
@@ -23,23 +38,54 @@ class TransferDialog extends React.Component {
|
||||
selectedOption: null,
|
||||
errorMsg: [],
|
||||
transferToUser: true,
|
||||
transferToGroup: false,
|
||||
activeTab: 'transUser'
|
||||
};
|
||||
this.options = [];
|
||||
}
|
||||
|
||||
handleSelectChange = (option) => {
|
||||
this.setState({selectedOption: option});
|
||||
this.setState({ selectedOption: option });
|
||||
};
|
||||
|
||||
submit = () => {
|
||||
let user = this.state.selectedOption;
|
||||
this.props.submit(user);
|
||||
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (isPro) {
|
||||
if (this.props.isOrgAdmin) {
|
||||
seafileAPI.orgAdminListDepartments(orgID).then((res) => {
|
||||
for (let i = 0; i < res.data.length; i++) {
|
||||
let obj = {};
|
||||
obj.value = res.data[i].name;
|
||||
obj.email = res.data[i].email;
|
||||
obj.label = res.data[i].name;
|
||||
this.options.push(obj);
|
||||
}
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
else if (this.props.isSysAdmin) {
|
||||
seafileAPI.sysAdminListDepartments().then((res) => {
|
||||
for (let i = 0; i < res.data.length; i++) {
|
||||
let obj = {};
|
||||
obj.value = res.data[i].name;
|
||||
obj.email = res.data[i].email;
|
||||
obj.label = res.data[i].name;
|
||||
this.options.push(obj);
|
||||
}
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
else{
|
||||
seafileAPI.listDepartments().then((res) => {
|
||||
for (let i = 0 ; i < res.data.length; i++) {
|
||||
for (let i = 0; i < res.data.length; i++) {
|
||||
let obj = {};
|
||||
obj.value = res.data[i].name;
|
||||
obj.email = res.data[i].email;
|
||||
@@ -56,49 +102,85 @@ class TransferDialog extends React.Component {
|
||||
onClick = () => {
|
||||
this.setState({
|
||||
transferToUser: !this.state.transferToUser,
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
toggle = (tab) => {
|
||||
if (this.state.activeTab !== tab) {
|
||||
this.setState({ activeTab: tab });
|
||||
}
|
||||
};
|
||||
|
||||
renderTransContent = () => {
|
||||
let activeTab = this.state.activeTab;
|
||||
let canTransferToDept = true;
|
||||
if (this.props.canTransferToDept != undefined) {
|
||||
canTransferToDept = this.props.canTransferToDept;
|
||||
}
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="transfer-dialog-side">
|
||||
<Nav pills>
|
||||
<NavItem role="tab" aria-selected={activeTab === 'transUser'} aria-controls="transfer-user-panel">
|
||||
<NavLink className={activeTab === 'transUser' ? 'active' : ''} onClick={(this.toggle.bind(this, 'transUser'))} tabIndex="0" onKeyDown={this.onTabKeyDown}>
|
||||
{gettext('Transfer to user')}
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
{isPro &&
|
||||
<NavItem role="tab" aria-selected={activeTab === 'transDepart'} aria-controls="transfer-depart-panel">
|
||||
<NavLink className={activeTab === 'transDepart' ? 'active' : ''} onClick={this.toggle.bind(this, 'transDepart')} tabIndex="0" onKeyDown={this.onTabKeyDown}>
|
||||
{gettext('Transfer to department')}
|
||||
</NavLink>
|
||||
</NavItem>}
|
||||
</Nav>
|
||||
</div>
|
||||
<div className="transfer-dialog-main">
|
||||
<TabContent activeTab={this.state.activeTab}>
|
||||
<Fragment>
|
||||
<TabPane tabId="transUser" role="tabpanel" id="transfer-user-panel">
|
||||
<UserSelect
|
||||
ref="userSelect"
|
||||
isMulti={false}
|
||||
className="reviewer-select"
|
||||
placeholder={gettext('Select a user')}
|
||||
onSelectChange={this.handleSelectChange}
|
||||
/>
|
||||
</TabPane>
|
||||
{isPro && canTransferToDept &&
|
||||
<TabPane tabId="transDepart" role="tabpanel" id="transfer-depart-panel">
|
||||
<SeahubSelect
|
||||
isClearable
|
||||
maxMenuHeight={200}
|
||||
hideSelectedOptions={true}
|
||||
components={makeAnimated()}
|
||||
placeholder={gettext('Select a department')}
|
||||
options={this.options}
|
||||
onChange={this.handleSelectChange}
|
||||
value={this.state.selectedOption}
|
||||
/>
|
||||
</TabPane>}
|
||||
</Fragment>
|
||||
</TabContent>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
render() {
|
||||
|
||||
|
||||
const { itemName: repoName } = this.props;
|
||||
let title = gettext('Transfer Library {library_name}');
|
||||
title = title.replace('{library_name}', '<span class="op-target text-truncate mx-1">' + Utils.HTMLescape(repoName) + '</span>');
|
||||
|
||||
|
||||
return (
|
||||
<Modal isOpen={true} toggle={this.props.toggleDialog}>
|
||||
<Modal isOpen={true} style={{maxWidth: '720px'}} toggle={this.props.toggleDialog} className="transfer-dialog">
|
||||
<ModalHeader toggle={this.props.toggleDialog}>
|
||||
<span dangerouslySetInnerHTML={{__html: title}} className="d-flex mw-100"></span>
|
||||
<span dangerouslySetInnerHTML={{ __html: title }} className="d-flex mw-100"></span>
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
{this.state.transferToUser ?
|
||||
<UserSelect
|
||||
ref="userSelect"
|
||||
isMulti={false}
|
||||
className="reviewer-select"
|
||||
placeholder={gettext('Select a user')}
|
||||
onSelectChange={this.handleSelectChange}
|
||||
/> :
|
||||
<SeahubSelect
|
||||
isClearable
|
||||
maxMenuHeight={200}
|
||||
hideSelectedOptions={true}
|
||||
components={makeAnimated()}
|
||||
placeholder={gettext('Select a department')}
|
||||
options={this.options}
|
||||
onChange={this.handleSelectChange}
|
||||
value={this.state.selectedOption}
|
||||
/>
|
||||
}
|
||||
{isPro && canTransferToDept &&
|
||||
<span role="button" tabIndex="0" className="action-link" onClick={this.onClick} onKeyDown={Utils.onKeyDown}>{this.state.transferToUser ?
|
||||
gettext('Transfer to department'): gettext('Transfer to user')}
|
||||
</span>
|
||||
}
|
||||
<ModalBody className="transfer-dialog-content" role="tablist">
|
||||
{this.renderTransContent()}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="secondary" onClick={this.props.toggleDialog}>{gettext('Cancel')}</Button>
|
||||
|
52
frontend/src/css/transfer-dialog.css
Normal file
52
frontend/src/css/transfer-dialog.css
Normal file
@@ -0,0 +1,52 @@
|
||||
.transfer-dialog .transfer-dialog-content {
|
||||
padding: 0;
|
||||
min-height: 22.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.transfer-dialog .transfer-dialog-content {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.transfer-dialog-content .transfer-dialog-side {
|
||||
flex-basis: 29%;
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.transfer-dialog .nav .nav-item .nav-link {
|
||||
padding: 0.3125rem 0.25rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.transfer-dialog-content .transfer-dialog-side {
|
||||
padding: 12px 8px;
|
||||
border: 0;
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
.transfer-dialog-side .nav {
|
||||
flex-direction: column;
|
||||
}
|
||||
.transfer-dialog-side .nav-pills .nav-item .nav-link {
|
||||
width: 100%;
|
||||
padding: 0.3125rem 0.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.transfer-dialog-content .transfer-dialog-main {
|
||||
display: flex;
|
||||
flex-basis: 78%;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.transfer-dialog-content .transfer-dialog-main .tab-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.transfer-dialog-content .transfer-dialog-main .tab-pane {
|
||||
height: 100%;
|
||||
}
|
@@ -200,7 +200,8 @@ class RepoItem extends React.Component {
|
||||
highlight: false,
|
||||
showMenu: false,
|
||||
isItemMenuShow: false,
|
||||
isTransferDialogShow: false
|
||||
isTransferDialogShow: false,
|
||||
orgAdmin: true
|
||||
};
|
||||
}
|
||||
|
||||
@@ -291,7 +292,7 @@ class RepoItem extends React.Component {
|
||||
render() {
|
||||
let { repo } = this.props;
|
||||
|
||||
let isOperationMenuShow = this.state.showMenu && !repo.isDepartmentRepo;
|
||||
let isOperationMenuShow = this.state.showMenu;
|
||||
return (
|
||||
<Fragment>
|
||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
@@ -326,6 +327,7 @@ class RepoItem extends React.Component {
|
||||
itemName={repo.repoName}
|
||||
submit={this.onTransferRepo}
|
||||
toggleDialog={this.toggleTransfer}
|
||||
isOrgAdmin={true}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
|
@@ -280,6 +280,12 @@ class Item extends Component {
|
||||
getOperations = () => {
|
||||
const { repo } = this.props;
|
||||
let operations = ['Delete', 'Transfer'];
|
||||
const index = repo.owner_email.indexOf('@seafile_group');
|
||||
let isGroupOwnedRepo = index != -1;
|
||||
if (isGroupOwnedRepo) {
|
||||
operations = ['Transfer'];
|
||||
return operations;
|
||||
}
|
||||
if (!repo.encrypted) {
|
||||
operations.push('Share');
|
||||
}
|
||||
@@ -319,7 +325,7 @@ class Item extends Component {
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
{(!isGroupOwnedRepo && isOpIconShown) &&
|
||||
{(isOpIconShown) &&
|
||||
<OpMenu
|
||||
operations={this.getOperations()}
|
||||
translateOperations={this.translateOperations}
|
||||
@@ -359,8 +365,8 @@ class Item extends Component {
|
||||
<TransferDialog
|
||||
itemName={repo.name}
|
||||
submit={this.onTransferRepo}
|
||||
canTransferToDept={false}
|
||||
toggleDialog={this.toggleTransferDialog}
|
||||
isSysAdmin={true}
|
||||
/>
|
||||
</ModalPortal>
|
||||
}
|
||||
|
@@ -11,8 +11,11 @@ from django.utils.translation import gettext as _
|
||||
from seaserv import seafile_api, ccnet_api
|
||||
from pysearpc import SearpcError
|
||||
|
||||
from seahub.avatar.settings import GROUP_AVATAR_DEFAULT_SIZE
|
||||
from seahub.avatar.templatetags.group_avatar_tags import get_default_group_avatar_url, api_grp_avatar_url
|
||||
from seahub.base.accounts import User
|
||||
from seahub.base.templatetags.seahub_tags import email2nickname
|
||||
from seahub.settings import CLOUD_MODE, MULTI_TENANCY
|
||||
from seahub.utils import is_valid_username, is_pro_version
|
||||
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
|
||||
from seahub.group.utils import is_group_member, is_group_admin, \
|
||||
@@ -351,3 +354,47 @@ class AdminSearchGroup(APIView):
|
||||
result.append(group_info)
|
||||
|
||||
return Response({"group_list": result})
|
||||
|
||||
|
||||
class AdminDepartments(APIView):
|
||||
"""
|
||||
List all departments
|
||||
"""
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsAdminUser,)
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def get(self, request):
|
||||
try:
|
||||
all_groups = ccnet_api.list_all_departments()
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
error_msg = 'Internal Server Error'
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||
|
||||
try:
|
||||
avatar_size = int(request.GET.get('avatar_size', GROUP_AVATAR_DEFAULT_SIZE))
|
||||
except ValueError:
|
||||
avatar_size = GROUP_AVATAR_DEFAULT_SIZE
|
||||
|
||||
|
||||
result = []
|
||||
for group in all_groups:
|
||||
try:
|
||||
avatar_url, is_default, date_uploaded = api_grp_avatar_url(group.id, avatar_size)
|
||||
except:
|
||||
avatar_url = get_default_group_avatar_url()
|
||||
created_at = timestamp_to_isoformat_timestr(group.timestamp)
|
||||
department_info = {
|
||||
"id": group.id,
|
||||
"email": '%s@seafile_group' % str(group.id),
|
||||
"parent_group_id": group.parent_group_id,
|
||||
"name": group.group_name,
|
||||
"owner": group.creator_name,
|
||||
"created_at": created_at,
|
||||
"avatar_url": request.build_absolute_uri(avatar_url),
|
||||
}
|
||||
result.append(department_info)
|
||||
|
||||
return Response(result)
|
||||
|
||||
|
@@ -8,6 +8,7 @@ from rest_framework.views import APIView
|
||||
from rest_framework import status
|
||||
from django.template.defaultfilters import filesizeformat
|
||||
from django.utils.translation import gettext as _
|
||||
import seaserv
|
||||
from seaserv import ccnet_api, seafile_api
|
||||
|
||||
from seahub.api2.authentication import TokenAuthentication
|
||||
@@ -26,6 +27,7 @@ from seahub.utils import is_valid_dirent_name, is_valid_email
|
||||
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
|
||||
|
||||
from seahub.api2.endpoints.group_owned_libraries import get_group_id_by_repo_owner
|
||||
from seahub.constants import PERMISSION_READ_WRITE
|
||||
|
||||
try:
|
||||
from seahub.settings import MULTI_TENANCY
|
||||
@@ -342,7 +344,7 @@ class AdminLibrary(APIView):
|
||||
|
||||
new_owner = request.data.get('owner', None)
|
||||
if new_owner:
|
||||
if not is_valid_email(new_owner):
|
||||
if not is_valid_email(new_owner) and '@seafile_group' not in new_owner:
|
||||
error_msg = 'owner invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
@@ -419,7 +421,14 @@ class AdminLibrary(APIView):
|
||||
pub_repos = seafile_api.list_inner_pub_repos_by_owner(repo_owner)
|
||||
|
||||
# transfer repo
|
||||
seafile_api.set_repo_owner(repo_id, new_owner)
|
||||
if '@seafile_group' in new_owner:
|
||||
group_id = int(new_owner.split('@')[0])
|
||||
if seaserv.is_org_group(group_id):
|
||||
error_msg = 'Can not transfer library to an organization department %s' % new_owner
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
seafile_api.transfer_repo_to_group(repo_id, group_id, PERMISSION_READ_WRITE)
|
||||
else:
|
||||
seafile_api.set_repo_owner(repo_id, new_owner)
|
||||
|
||||
# reshare repo to user
|
||||
for shared_user in shared_users:
|
||||
|
@@ -13,7 +13,10 @@ from seahub.api2.throttling import UserRateThrottle
|
||||
from seahub.api2.authentication import TokenAuthentication
|
||||
from seahub.api2.utils import api_error
|
||||
from seahub.api2.endpoints.admin.groups import AdminGroup as SysAdminGroup
|
||||
from seahub.avatar.settings import GROUP_AVATAR_DEFAULT_SIZE
|
||||
from seahub.avatar.templatetags.group_avatar_tags import api_grp_avatar_url, get_default_group_avatar_url
|
||||
from seahub.base.templatetags.seahub_tags import email2nickname, email2contact_email
|
||||
from seahub.utils.ccnet_db import CcnetDB
|
||||
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
|
||||
|
||||
from pysearpc import SearpcError
|
||||
@@ -202,3 +205,45 @@ class OrgAdminSearchGroup(APIView):
|
||||
groups_list.append(group)
|
||||
|
||||
return Response({'group_list': groups_list})
|
||||
|
||||
|
||||
class OrgAdminDepartments(APIView):
|
||||
"""
|
||||
List all departments of the current organization
|
||||
"""
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsProVersion, IsOrgAdminUser)
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def get(self, request, org_id):
|
||||
try:
|
||||
db_api = CcnetDB()
|
||||
departments = db_api.list_org_departments(int(org_id))
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
error_msg = 'Internal Server Error'
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||
|
||||
try:
|
||||
avatar_size = int(request.GET.get('avatar_size', GROUP_AVATAR_DEFAULT_SIZE))
|
||||
except ValueError:
|
||||
avatar_size = GROUP_AVATAR_DEFAULT_SIZE
|
||||
result = []
|
||||
for group in departments:
|
||||
try:
|
||||
avatar_url, is_default, date_uploaded = api_grp_avatar_url(group.id, avatar_size)
|
||||
except:
|
||||
avatar_url = get_default_group_avatar_url()
|
||||
created_at = timestamp_to_isoformat_timestr(group.timestamp)
|
||||
department_info = {
|
||||
"id": group.id,
|
||||
"email": '%s@seafile_group' % str(group.id),
|
||||
"parent_group_id": group.parent_group_id,
|
||||
"name": group.group_name,
|
||||
"owner": group.creator_name,
|
||||
"created_at": created_at,
|
||||
"avatar_url": request.build_absolute_uri(avatar_url),
|
||||
}
|
||||
result.append(department_info)
|
||||
|
||||
return Response(result)
|
||||
|
@@ -19,11 +19,13 @@ from seahub.group.utils import group_id_to_name
|
||||
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
|
||||
from seahub.utils import is_valid_email
|
||||
from seahub.signals import repo_deleted
|
||||
from seahub.constants import PERMISSION_READ_WRITE
|
||||
|
||||
from seahub.organizations.views import is_org_repo, org_user_exists
|
||||
|
||||
from pysearpc import SearpcError
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -128,12 +130,11 @@ class OrgAdminRepo(APIView):
|
||||
"""Transfer an organization library
|
||||
"""
|
||||
new_owner = request.data.get('email', None)
|
||||
|
||||
if not new_owner:
|
||||
error_msg = 'Email invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
if not is_valid_email(new_owner):
|
||||
if not is_valid_email(new_owner) and not '@seafile_group' in new_owner:
|
||||
error_msg = 'Email invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
@@ -143,9 +144,10 @@ class OrgAdminRepo(APIView):
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
# permission checking
|
||||
if not org_user_exists(org_id, new_owner):
|
||||
error_msg = 'User %s not in org %s.' % (new_owner, org_id)
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
if '@seafile_group' not in new_owner:
|
||||
if not org_user_exists(org_id, new_owner):
|
||||
error_msg = 'User %s not in org %s.' % (new_owner, org_id)
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
repo = seafile_api.get_repo(repo_id)
|
||||
if not repo:
|
||||
@@ -166,8 +168,17 @@ class OrgAdminRepo(APIView):
|
||||
|
||||
# get all pub repos
|
||||
pub_repos = seafile_api.list_org_inner_pub_repos_by_owner(org_id, repo_owner)
|
||||
|
||||
seafile_api.set_org_repo_owner(org_id, repo_id, new_owner)
|
||||
# transfer repo
|
||||
if '@seafile_group' in new_owner:
|
||||
group_id = int(new_owner.split('@')[0])
|
||||
if seaserv.is_org_group(group_id):
|
||||
group_org_id = ccnet_api.get_org_id_by_group(group_id)
|
||||
if org_id != group_org_id:
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
seafile_api.org_transfer_repo_to_group(repo_id, org_id, group_id, PERMISSION_READ_WRITE)
|
||||
else:
|
||||
seafile_api.set_org_repo_owner(org_id, repo_id, new_owner)
|
||||
|
||||
# reshare repo to user
|
||||
for shared_user in shared_users:
|
||||
|
@@ -13,7 +13,7 @@ from .api.group_members import AdminGroupMembers, AdminGroupMember
|
||||
from .api.admin.users import OrgAdminUser, OrgAdminUsers, OrgAdminSearchUser, \
|
||||
OrgAdminImportUsers, OrgAdminInviteUser
|
||||
from .api.admin.user_set_password import OrgAdminUserSetPassword
|
||||
from .api.admin.groups import OrgAdminGroups, OrgAdminGroup, OrgAdminSearchGroup
|
||||
from .api.admin.groups import OrgAdminGroups, OrgAdminGroup, OrgAdminSearchGroup, OrgAdminDepartments
|
||||
from .api.admin.repos import OrgAdminRepos, OrgAdminRepo
|
||||
from .api.admin.info import OrgAdminInfo
|
||||
from .api.admin.links import OrgAdminLinks, OrgAdminLink
|
||||
@@ -95,5 +95,6 @@ urlpatterns = [
|
||||
path('admin/logs/file-access/', OrgAdminLogsFileAccess.as_view(), name='api-v2.1-org-admin-logs-file-access'),
|
||||
path('admin/logs/file-update/', OrgAdminLogsFileUpdate.as_view(), name='api-v2.1-org-admin-logs-file-update'),
|
||||
path('admin/logs/repo-permission/', OrgAdminLogsPermAudit.as_view(), name='api-v2.1-org-admin-logs-repo-permission'),
|
||||
path('<int:org_id>/admin/departments/', OrgAdminDepartments.as_view(), name='api-v2.1-org-admin-departments'),
|
||||
]
|
||||
|
||||
|
@@ -150,7 +150,7 @@ from seahub.api2.endpoints.admin.system_library import AdminSystemLibrary, \
|
||||
AdminSystemLibraryUploadLink
|
||||
from seahub.api2.endpoints.admin.default_library import AdminDefaultLibrary
|
||||
from seahub.api2.endpoints.admin.trash_libraries import AdminTrashLibraries, AdminTrashLibrary
|
||||
from seahub.api2.endpoints.admin.groups import AdminGroups, AdminGroup, AdminSearchGroup
|
||||
from seahub.api2.endpoints.admin.groups import AdminGroups, AdminGroup, AdminSearchGroup, AdminDepartments
|
||||
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.shares import AdminShares
|
||||
@@ -635,6 +635,9 @@ urlpatterns = [
|
||||
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'),
|
||||
|
||||
## admin::departments
|
||||
re_path(r'api/v2.1/admin/departments/$', AdminDepartments.as_view(), name='api-v2.1-admin-departments'),
|
||||
|
||||
## admin::shares
|
||||
re_path(r'^api/v2.1/admin/shares/$', AdminShares.as_view(), name='api-v2.1-admin-shares'),
|
||||
|
||||
|
@@ -22,3 +22,56 @@ def get_ccnet_db_name():
|
||||
error_msg = 'Failed to init ccnet db, only mysql db supported.'
|
||||
return None, error_msg
|
||||
return db_name, None
|
||||
|
||||
|
||||
import os
|
||||
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')
|
||||
self.creator_name = kwargs.get('creator_name')
|
||||
self.timestamp = kwargs.get('timestamp')
|
||||
self.parent_group_id = kwargs.get('parent_group_id')
|
||||
|
||||
class CcnetDB:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.db_name = get_ccnet_db_name()[0]
|
||||
|
||||
|
||||
def list_org_departments(self, org_id):
|
||||
sql = f"""
|
||||
SELECT
|
||||
g.group_id, group_name, creator_name, timestamp, type, parent_group_id
|
||||
FROM
|
||||
`{self.db_name}`.`OrgGroup` o
|
||||
LEFT JOIN
|
||||
`{self.db_name}`.`Group` g
|
||||
ON o.group_id=g.group_id
|
||||
WHERE
|
||||
org_id={org_id} AND parent_group_id<>0;
|
||||
"""
|
||||
groups = []
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(sql)
|
||||
for item in cursor.fetchall():
|
||||
group_id = item[0]
|
||||
group_name = item[1]
|
||||
creator_name = item[2]
|
||||
timestamp=item[3]
|
||||
parent_group_id = item[5]
|
||||
params = {
|
||||
'group_id':group_id,
|
||||
'group_name': group_name,
|
||||
'creator_name': creator_name,
|
||||
'timestamp': timestamp,
|
||||
'parent_group_id': parent_group_id
|
||||
}
|
||||
group_obj = CcnetGroup(**params)
|
||||
groups.append(group_obj)
|
||||
return groups
|
||||
|
Reference in New Issue
Block a user