mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-07 18:03:48 +00:00
Import work weixin dep (#3803)
* dropdown import work weixin department * API import work weixin department * admin check group name conflict * work weixin department batch import * work weixin department import update return msg * work weixin department import not support batch * react work weixin department import * add IsProVersion work weixin department import * change style * check group name conflict
This commit is contained in:
@@ -0,0 +1,54 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
||||||
|
import Loading from '../loading';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
importDepartmentDialogToggle: PropTypes.func.isRequired,
|
||||||
|
onImportDepartmentSubmit: PropTypes.func.isRequired,
|
||||||
|
departmentsCount: PropTypes.number.isRequired,
|
||||||
|
membersCount: PropTypes.number.isRequired,
|
||||||
|
departmentName: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ImportWorkWeixinDepartmentDialog extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
isLoading : false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle = () => {
|
||||||
|
this.props.importDepartmentDialogToggle(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleSubmit = () => {
|
||||||
|
this.props.onImportDepartmentSubmit();
|
||||||
|
this.setState({ isLoading : true });
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { departmentsCount, membersCount, departmentName } = this.props;
|
||||||
|
return (
|
||||||
|
<Modal isOpen={true} toggle={this.toggle}>
|
||||||
|
<ModalHeader toggle={this.toggle}>
|
||||||
|
<span>{'导入部门 '}</span><span className="op-target" title={departmentName}>{departmentName}</span>
|
||||||
|
</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<p>{'将要导入 '}<strong>{departmentsCount}</strong>{' 个部门,其中包括 '}<strong>{membersCount}</strong>{' 个成员'}</p>
|
||||||
|
{this.state.isLoading && <Loading/>}
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button color="secondary" onClick={this.toggle}>{'取消'}</Button>
|
||||||
|
<Button color="primary" onClick={this.handleSubmit}>{'导入'}</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportWorkWeixinDepartmentDialog.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default ImportWorkWeixinDepartmentDialog;
|
@@ -23,8 +23,9 @@
|
|||||||
height: 140px;
|
height: 140px;
|
||||||
}
|
}
|
||||||
.dir-content-nav {
|
.dir-content-nav {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
width: 24%;
|
||||||
}
|
}
|
||||||
.dir-content-nav:hover {
|
.dir-content-nav:hover {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@@ -42,6 +43,8 @@
|
|||||||
}
|
}
|
||||||
.tree-node-inner {
|
.tree-node-inner {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
padding-right: 1rem;
|
||||||
}
|
}
|
||||||
.tree-node-inner i {
|
.tree-node-inner i {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@@ -2,10 +2,12 @@ import React, { Component, Fragment } from 'react';
|
|||||||
import { Button } from 'reactstrap';
|
import { Button } from 'reactstrap';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
import { gettext, siteRoot } from '../../utils/constants';
|
import { gettext, siteRoot, isPro } from '../../utils/constants';
|
||||||
import toaster from '../../components/toast';
|
import toaster from '../../components/toast';
|
||||||
import Account from '../../components/common/account';
|
import Account from '../../components/common/account';
|
||||||
import { WorkWeixinDepartmentMembersList, WorkWeixinDepartmentsTreePanel } from './work-weixin';
|
import { WorkWeixinDepartmentMembersList, WorkWeixinDepartmentsTreePanel } from './work-weixin';
|
||||||
|
import ImportWorkWeixinDepartmentDialog from '../../components/dialog/import-work-weixin-department-dialog';
|
||||||
|
|
||||||
import '../../css/work-weixin-departments.css';
|
import '../../css/work-weixin-departments.css';
|
||||||
|
|
||||||
class WorkWeixinDepartments extends Component {
|
class WorkWeixinDepartments extends Component {
|
||||||
@@ -22,6 +24,10 @@ class WorkWeixinDepartments extends Component {
|
|||||||
newUsersTempObj: {},
|
newUsersTempObj: {},
|
||||||
isCheckedAll: false,
|
isCheckedAll: false,
|
||||||
canCheckUserIds: [],
|
canCheckUserIds: [],
|
||||||
|
isImportDepartmentDialogShow: false,
|
||||||
|
importDepartment: null,
|
||||||
|
importDepartmentChildrenCount: 0,
|
||||||
|
importDepartmentMembersCount: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,13 +56,20 @@ class WorkWeixinDepartments extends Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getWorkWeixinDepartmentsList = () => {
|
getWorkWeixinDepartmentsList = (departmentID) => {
|
||||||
seafileAPI.adminListWorkWeixinDepartments().then((res) => {
|
seafileAPI.adminListWorkWeixinDepartments(departmentID).then((res) => {
|
||||||
|
if (!departmentID) {
|
||||||
let departmentsTree = this.getDepartmentsTree(res.data.department);
|
let departmentsTree = this.getDepartmentsTree(res.data.department);
|
||||||
this.setState({
|
this.setState({
|
||||||
isTreeLoading: false,
|
isTreeLoading: false,
|
||||||
departmentsTree: departmentsTree,
|
departmentsTree: departmentsTree,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
importDepartmentChildrenCount: res.data.department.length,
|
||||||
|
importDepartmentMembersCount: this.state.membersTempObj[departmentID].length,
|
||||||
|
});
|
||||||
|
}
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.handleError(error);
|
this.handleError(error);
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -213,6 +226,59 @@ class WorkWeixinDepartments extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
importDepartmentDialogToggle = (importDepartment) => {
|
||||||
|
this.setState({
|
||||||
|
isImportDepartmentDialogShow: !this.state.isImportDepartmentDialogShow,
|
||||||
|
importDepartment: importDepartment,
|
||||||
|
}, () => {
|
||||||
|
if (importDepartment) {
|
||||||
|
this.getWorkWeixinDepartmentsList(importDepartment.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onImportDepartmentSubmit = () => {
|
||||||
|
let importDepartment = this.state.importDepartment;
|
||||||
|
if (!importDepartment) return;
|
||||||
|
seafileAPI.adminImportWorkWeixinDepartment(importDepartment.id).then((res) => {
|
||||||
|
this.setState({
|
||||||
|
isMembersListLoading: true,
|
||||||
|
checkedDepartmentId: importDepartment.id,
|
||||||
|
membersTempObj: {},
|
||||||
|
membersList: [],
|
||||||
|
newUsersTempObj: {},
|
||||||
|
isCheckedAll: false,
|
||||||
|
canCheckUserIds: [],
|
||||||
|
});
|
||||||
|
this.getWorkWeixinDepartmentMembersList(importDepartment.id);
|
||||||
|
this.importDepartmentDialogToggle(null);
|
||||||
|
if (res.data.success) {
|
||||||
|
this.handleImportDepartmentSubmitSuccess(res.data.success);
|
||||||
|
}
|
||||||
|
if (res.data.failed) {
|
||||||
|
this.handleImportDepartmentSubmitFailed(res.data.failed);
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
this.handleError(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleImportDepartmentSubmitSuccess = (successes) => {
|
||||||
|
for (let i = 0, len = successes.length; i < len; i++) {
|
||||||
|
let success = successes[i];
|
||||||
|
let successMsg = success.type === 'department' ? '部门 ' + success.department_name + ' 导入成功' : success.api_user_name + ' 导入成功' ;
|
||||||
|
toaster.success(successMsg, { duration: 3 });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleImportDepartmentSubmitFailed = (fails) => {
|
||||||
|
for (let i = 0, len = fails.length; i < len; i++) {
|
||||||
|
let fail = fails[i];
|
||||||
|
let failName = fail.type === 'department' ? fail.department_name : fail.api_user_name;
|
||||||
|
toaster.danger(failName + ' ' + fail.msg, { duration: 3} );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
handleError = (e) => {
|
handleError = (e) => {
|
||||||
if (e.response) {
|
if (e.response) {
|
||||||
toaster.danger(e.response.data.error_msg || e.response.data.detail || gettext('Error'), {duration: 3});
|
toaster.danger(e.response.data.error_msg || e.response.data.detail || gettext('Error'), {duration: 3});
|
||||||
@@ -222,7 +288,7 @@ class WorkWeixinDepartments extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.getWorkWeixinDepartmentsList();
|
this.getWorkWeixinDepartmentsList(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNav() {
|
renderNav() {
|
||||||
@@ -242,6 +308,8 @@ class WorkWeixinDepartments extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { isImportDepartmentDialogShow, isTreeLoading, importDepartment, importDepartmentChildrenCount, importDepartmentMembersCount } = this.state;
|
||||||
|
let canImportDepartment = !!(isPro && isImportDepartmentDialogShow && !isTreeLoading && importDepartment);
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{this.renderNav()}
|
{this.renderNav()}
|
||||||
@@ -256,6 +324,7 @@ class WorkWeixinDepartments extends Component {
|
|||||||
isTreeLoading={this.state.isTreeLoading}
|
isTreeLoading={this.state.isTreeLoading}
|
||||||
onChangeDepartment={this.onChangeDepartment}
|
onChangeDepartment={this.onChangeDepartment}
|
||||||
checkedDepartmentId={this.state.checkedDepartmentId}
|
checkedDepartmentId={this.state.checkedDepartmentId}
|
||||||
|
importDepartmentDialogToggle={this.importDepartmentDialogToggle}
|
||||||
/>
|
/>
|
||||||
<div className="dir-content-resize"></div>
|
<div className="dir-content-resize"></div>
|
||||||
<WorkWeixinDepartmentMembersList
|
<WorkWeixinDepartmentMembersList
|
||||||
@@ -271,6 +340,15 @@ class WorkWeixinDepartments extends Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{canImportDepartment &&
|
||||||
|
<ImportWorkWeixinDepartmentDialog
|
||||||
|
importDepartmentDialogToggle={this.importDepartmentDialogToggle}
|
||||||
|
onImportDepartmentSubmit={this.onImportDepartmentSubmit}
|
||||||
|
departmentsCount={importDepartmentChildrenCount}
|
||||||
|
membersCount={importDepartmentMembersCount}
|
||||||
|
departmentName={importDepartment.name}
|
||||||
|
/>
|
||||||
|
}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { Dropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'reactstrap';
|
||||||
|
import { gettext, isPro } from '../../../utils/constants';
|
||||||
|
|
||||||
const WorkWeixinDepartmentsTreeNodePropTypes = {
|
const WorkWeixinDepartmentsTreeNodePropTypes = {
|
||||||
index: PropTypes.number,
|
index: PropTypes.number,
|
||||||
@@ -8,6 +10,7 @@ const WorkWeixinDepartmentsTreeNodePropTypes = {
|
|||||||
isChildrenShow: PropTypes.bool.isRequired,
|
isChildrenShow: PropTypes.bool.isRequired,
|
||||||
onChangeDepartment: PropTypes.func.isRequired,
|
onChangeDepartment: PropTypes.func.isRequired,
|
||||||
checkedDepartmentId: PropTypes.number.isRequired,
|
checkedDepartmentId: PropTypes.number.isRequired,
|
||||||
|
importDepartmentDialogToggle: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class WorkWeixinDepartmentsTreeNode extends Component {
|
class WorkWeixinDepartmentsTreeNode extends Component {
|
||||||
@@ -16,6 +19,8 @@ class WorkWeixinDepartmentsTreeNode extends Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
isChildrenShow: false,
|
isChildrenShow: false,
|
||||||
|
dropdownOpen: false,
|
||||||
|
active: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +32,18 @@ class WorkWeixinDepartmentsTreeNode extends Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dropdownToggle = () => {
|
||||||
|
this.setState({ dropdownOpen: !this.state.dropdownOpen });
|
||||||
|
};
|
||||||
|
|
||||||
|
onMouseEnter = () => {
|
||||||
|
this.setState({ active: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
onMouseLeave = () => {
|
||||||
|
this.setState({ active: false });
|
||||||
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (this.props.index === 0) {
|
if (this.props.index === 0) {
|
||||||
this.setState({ isChildrenShow: true });
|
this.setState({ isChildrenShow: true });
|
||||||
@@ -44,6 +61,7 @@ class WorkWeixinDepartmentsTreeNode extends Component {
|
|||||||
isChildrenShow={this.state.isChildrenShow}
|
isChildrenShow={this.state.isChildrenShow}
|
||||||
onChangeDepartment={this.props.onChangeDepartment}
|
onChangeDepartment={this.props.onChangeDepartment}
|
||||||
checkedDepartmentId={this.props.checkedDepartmentId}
|
checkedDepartmentId={this.props.checkedDepartmentId}
|
||||||
|
importDepartmentDialogToggle={this.props.importDepartmentDialogToggle}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -63,9 +81,37 @@ class WorkWeixinDepartmentsTreeNode extends Component {
|
|||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{isChildrenShow &&
|
{isChildrenShow &&
|
||||||
<div className={nodeInnerClass} onClick={() => this.props.onChangeDepartment(department.id)}>
|
<div
|
||||||
|
className={nodeInnerClass}
|
||||||
|
onClick={() => this.props.onChangeDepartment(department.id)}
|
||||||
|
onMouseEnter={this.onMouseEnter}
|
||||||
|
onMouseLeave={this.onMouseLeave}
|
||||||
|
>
|
||||||
<i className={toggleClass} onClick={(e) => this.toggleChildren(e)}></i>{' '}
|
<i className={toggleClass} onClick={(e) => this.toggleChildren(e)}></i>{' '}
|
||||||
<span className="tree-node-text">{department.name}</span>
|
<span className="tree-node-text">{department.name}</span>
|
||||||
|
{isPro &&
|
||||||
|
<Dropdown
|
||||||
|
isOpen={this.state.dropdownOpen}
|
||||||
|
toggle={this.dropdownToggle}
|
||||||
|
direction="down"
|
||||||
|
style={this.state.active ? {} : { opacity: 0 }}
|
||||||
|
>
|
||||||
|
<DropdownToggle
|
||||||
|
tag='i'
|
||||||
|
className='fa fa-ellipsis-v cursor-pointer attr-action-icon'
|
||||||
|
title={gettext('More Operations')}
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-expanded={this.state.dropdownOpen}
|
||||||
|
>
|
||||||
|
</DropdownToggle>
|
||||||
|
<DropdownMenu className="drop-list" right={true}>
|
||||||
|
<DropdownItem
|
||||||
|
onClick={this.props.importDepartmentDialogToggle.bind(this, department)}
|
||||||
|
id={department.id}
|
||||||
|
>{'导入部门'}</DropdownItem>
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{this.state.isChildrenShow &&
|
{this.state.isChildrenShow &&
|
||||||
|
@@ -8,6 +8,7 @@ const WorkWeixinDepartmentsTreePanelPropTypes = {
|
|||||||
departmentsTree: PropTypes.array.isRequired,
|
departmentsTree: PropTypes.array.isRequired,
|
||||||
onChangeDepartment: PropTypes.func.isRequired,
|
onChangeDepartment: PropTypes.func.isRequired,
|
||||||
checkedDepartmentId: PropTypes.number.isRequired,
|
checkedDepartmentId: PropTypes.number.isRequired,
|
||||||
|
importDepartmentDialogToggle: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class WorkWeixinDepartmentsTreePanel extends Component {
|
class WorkWeixinDepartmentsTreePanel extends Component {
|
||||||
@@ -33,6 +34,7 @@ class WorkWeixinDepartmentsTreePanel extends Component {
|
|||||||
isChildrenShow={true}
|
isChildrenShow={true}
|
||||||
onChangeDepartment={this.props.onChangeDepartment}
|
onChangeDepartment={this.props.onChangeDepartment}
|
||||||
checkedDepartmentId={this.props.checkedDepartmentId}
|
checkedDepartmentId={this.props.checkedDepartmentId}
|
||||||
|
importDepartmentDialogToggle={this.props.importDepartmentDialogToggle}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@@ -5,6 +5,7 @@ import logging
|
|||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from seaserv import seafile_api, ccnet_api
|
||||||
from rest_framework.authentication import SessionAuthentication
|
from rest_framework.authentication import SessionAuthentication
|
||||||
from rest_framework.permissions import IsAdminUser
|
from rest_framework.permissions import IsAdminUser
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
@@ -13,6 +14,8 @@ from rest_framework import status
|
|||||||
from seahub.api2.authentication import TokenAuthentication
|
from seahub.api2.authentication import TokenAuthentication
|
||||||
from seahub.api2.throttling import UserRateThrottle
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
from seahub.api2.utils import api_error
|
from seahub.api2.utils import api_error
|
||||||
|
from seahub.api2.permissions import IsProVersion
|
||||||
|
|
||||||
from seahub.work_weixin.utils import handler_work_weixin_api_response, \
|
from seahub.work_weixin.utils import handler_work_weixin_api_response, \
|
||||||
get_work_weixin_access_token, admin_work_weixin_departments_check, \
|
get_work_weixin_access_token, admin_work_weixin_departments_check, \
|
||||||
update_work_weixin_user_info
|
update_work_weixin_user_info
|
||||||
@@ -21,10 +24,13 @@ from seahub.work_weixin.settings import WORK_WEIXIN_DEPARTMENTS_URL, \
|
|||||||
from seahub.base.accounts import User
|
from seahub.base.accounts import User
|
||||||
from seahub.utils.auth import gen_user_virtual_id
|
from seahub.utils.auth import gen_user_virtual_id
|
||||||
from seahub.auth.models import SocialAuthUser
|
from seahub.auth.models import SocialAuthUser
|
||||||
|
from seahub.group.utils import validate_group_name
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
WORK_WEIXIN_DEPARTMENT_FIELD = 'department'
|
WORK_WEIXIN_DEPARTMENT_FIELD = 'department'
|
||||||
WORK_WEIXIN_DEPARTMENT_MEMBERS_FIELD = 'userlist'
|
WORK_WEIXIN_DEPARTMENT_MEMBERS_FIELD = 'userlist'
|
||||||
|
DEPARTMENT_OWNER = 'system admin'
|
||||||
|
|
||||||
|
|
||||||
# # uid = corpid + '_' + userid
|
# # uid = corpid + '_' + userid
|
||||||
# from social_django.models import UserSocialAuth
|
# from social_django.models import UserSocialAuth
|
||||||
@@ -149,7 +155,6 @@ def _handler_work_weixin_user_data(api_user, social_auth_queryset):
|
|||||||
|
|
||||||
|
|
||||||
def _import_user_from_work_weixin(email, api_user):
|
def _import_user_from_work_weixin(email, api_user):
|
||||||
|
|
||||||
api_user['username'] = email
|
api_user['username'] = email
|
||||||
uid = WORK_WEIXIN_UID_PREFIX + api_user.get('userid')
|
uid = WORK_WEIXIN_UID_PREFIX + api_user.get('userid')
|
||||||
try:
|
try:
|
||||||
@@ -204,3 +209,235 @@ class AdminWorkWeixinUsersBatch(APIView):
|
|||||||
failed.append(error_data)
|
failed.append(error_data)
|
||||||
|
|
||||||
return Response({'success': success, 'failed': failed})
|
return Response({'success': success, 'failed': failed})
|
||||||
|
|
||||||
|
|
||||||
|
class AdminWorkWeixinDepartmentsImport(APIView):
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
throttle_classes = (UserRateThrottle,)
|
||||||
|
permission_classes = (IsAdminUser,)
|
||||||
|
|
||||||
|
def _list_departments_from_work_weixin(self, access_token, department_id):
|
||||||
|
data = {
|
||||||
|
'access_token': access_token,
|
||||||
|
'id': department_id,
|
||||||
|
}
|
||||||
|
api_response = requests.get(WORK_WEIXIN_DEPARTMENTS_URL, params=data)
|
||||||
|
api_response_dic = handler_work_weixin_api_response(api_response)
|
||||||
|
|
||||||
|
if not api_response_dic:
|
||||||
|
logger.error('can not get work weixin departments response')
|
||||||
|
return None
|
||||||
|
|
||||||
|
if WORK_WEIXIN_DEPARTMENT_FIELD not in api_response_dic:
|
||||||
|
logger.error(json.dumps(api_response_dic))
|
||||||
|
logger.error('can not get department list in work weixin departments response')
|
||||||
|
return None
|
||||||
|
|
||||||
|
return api_response_dic[WORK_WEIXIN_DEPARTMENT_FIELD]
|
||||||
|
|
||||||
|
def _list_department_members_from_work_weixin(self, access_token, department_id):
|
||||||
|
data = {
|
||||||
|
'access_token': access_token,
|
||||||
|
'department_id': department_id,
|
||||||
|
'fetch_child': 1,
|
||||||
|
}
|
||||||
|
api_response = requests.get(WORK_WEIXIN_DEPARTMENT_MEMBERS_URL, params=data)
|
||||||
|
api_response_dic = handler_work_weixin_api_response(api_response)
|
||||||
|
|
||||||
|
if not api_response_dic:
|
||||||
|
logger.error('can not get work weixin department members response')
|
||||||
|
return None
|
||||||
|
|
||||||
|
if WORK_WEIXIN_DEPARTMENT_MEMBERS_FIELD not in api_response_dic:
|
||||||
|
logger.error(json.dumps(api_response_dic))
|
||||||
|
logger.error('can not get userlist in work weixin department members response')
|
||||||
|
return None
|
||||||
|
|
||||||
|
return api_response_dic[WORK_WEIXIN_DEPARTMENT_MEMBERS_FIELD]
|
||||||
|
|
||||||
|
def _admin_check_group_name_conflict(self, new_group_name):
|
||||||
|
checked_groups = ccnet_api.search_groups(new_group_name, -1, -1)
|
||||||
|
|
||||||
|
for g in checked_groups:
|
||||||
|
if g.group_name == new_group_name:
|
||||||
|
return True, g
|
||||||
|
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
def _api_department_success_msg(self, department_obj_id, department_obj_name, group_id):
|
||||||
|
return {
|
||||||
|
'type': 'department',
|
||||||
|
'department_id': department_obj_id,
|
||||||
|
'department_name': department_obj_name,
|
||||||
|
'group_id': group_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _api_department_failed_msg(self, department_obj_id, department_obj_name, msg):
|
||||||
|
return {
|
||||||
|
'type': 'department',
|
||||||
|
'department_id': department_obj_id,
|
||||||
|
'department_name': department_obj_name,
|
||||||
|
'msg': msg,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _api_user_success_msg(self, email, api_user_name, department_obj_id, group_id):
|
||||||
|
return {
|
||||||
|
'type': 'user',
|
||||||
|
'email': email,
|
||||||
|
'api_user_name': api_user_name,
|
||||||
|
'department_id': department_obj_id,
|
||||||
|
'group_id': group_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _api_user_failed_msg(self, email, api_user_name, department_obj_id, msg):
|
||||||
|
return {
|
||||||
|
'type': 'user',
|
||||||
|
'email': email,
|
||||||
|
'api_user_name': api_user_name,
|
||||||
|
'department_id': department_obj_id,
|
||||||
|
'msg': msg,
|
||||||
|
}
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
"""import department from work weixin
|
||||||
|
|
||||||
|
permission: IsProVersion
|
||||||
|
"""
|
||||||
|
# argument check
|
||||||
|
department_id = request.data.get('work_weixin_department_id')
|
||||||
|
try:
|
||||||
|
department_id = int(department_id)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
error_msg = 'work_weixin_department_ids invalid.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
# is pro version and work weixin check
|
||||||
|
if not IsProVersion or not admin_work_weixin_departments_check():
|
||||||
|
error_msg = 'Feature is not enabled.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
access_token = get_work_weixin_access_token()
|
||||||
|
if not access_token:
|
||||||
|
logger.error('can not get work weixin access_token')
|
||||||
|
error_msg = '获取企业微信组织架构失败'
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# list departments from work weixin
|
||||||
|
api_department_list = self._list_departments_from_work_weixin(access_token, department_id)
|
||||||
|
if api_department_list is None:
|
||||||
|
error_msg = '获取企业微信组织架构失败'
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# list department members from work weixin
|
||||||
|
api_user_list = self._list_department_members_from_work_weixin(access_token, department_id)
|
||||||
|
if api_user_list is None:
|
||||||
|
error_msg = '获取企业微信组织架构成员失败'
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# main
|
||||||
|
success = list()
|
||||||
|
failed = list()
|
||||||
|
department_map_to_group_dict = dict()
|
||||||
|
|
||||||
|
for index, department_obj in enumerate(api_department_list):
|
||||||
|
# check department argument
|
||||||
|
new_group_name = department_obj.get('name')
|
||||||
|
department_obj_id = department_obj.get('id')
|
||||||
|
if department_obj_id is None or not new_group_name or not validate_group_name(new_group_name):
|
||||||
|
failed_msg = self._api_department_failed_msg(
|
||||||
|
department_obj_id, new_group_name, '部门参数错误')
|
||||||
|
failed.append(failed_msg)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# check parent group
|
||||||
|
if index == 0:
|
||||||
|
parent_group_id = -1
|
||||||
|
else:
|
||||||
|
parent_department_id = department_obj.get('parentid')
|
||||||
|
parent_group_id = department_map_to_group_dict.get(parent_department_id)
|
||||||
|
|
||||||
|
if parent_group_id is None:
|
||||||
|
failed_msg = self._api_department_failed_msg(
|
||||||
|
department_obj_id, new_group_name, '父级部门不存在')
|
||||||
|
failed.append(failed_msg)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# check department exist by group name
|
||||||
|
exist, exist_group = self._admin_check_group_name_conflict(new_group_name)
|
||||||
|
if exist:
|
||||||
|
department_map_to_group_dict[department_obj_id] = exist_group.id
|
||||||
|
failed_msg = self._api_department_failed_msg(
|
||||||
|
department_obj_id, new_group_name, '部门已存在')
|
||||||
|
failed.append(failed_msg)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# import department
|
||||||
|
try:
|
||||||
|
group_id = ccnet_api.create_group(
|
||||||
|
new_group_name, DEPARTMENT_OWNER, parent_group_id=parent_group_id)
|
||||||
|
|
||||||
|
seafile_api.set_group_quota(group_id, -2)
|
||||||
|
|
||||||
|
department_map_to_group_dict[department_obj_id] = group_id
|
||||||
|
success_msg = self._api_department_success_msg(
|
||||||
|
department_obj_id, new_group_name, group_id)
|
||||||
|
success.append(success_msg)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
failed_msg = self._api_department_failed_msg(
|
||||||
|
department_obj_id, new_group_name, '部门导入失败')
|
||||||
|
failed.append(failed_msg)
|
||||||
|
|
||||||
|
# todo filter ccnet User database
|
||||||
|
social_auth_queryset = SocialAuthUser.objects.filter(
|
||||||
|
provider=WORK_WEIXIN_PROVIDER, uid__contains=WORK_WEIXIN_UID_PREFIX)
|
||||||
|
|
||||||
|
# import api_user
|
||||||
|
for api_user in api_user_list:
|
||||||
|
uid = WORK_WEIXIN_UID_PREFIX + api_user.get('userid', '')
|
||||||
|
api_user['contact_email'] = api_user['email']
|
||||||
|
api_user_name = api_user.get('name')
|
||||||
|
|
||||||
|
# determine the user exists
|
||||||
|
if social_auth_queryset.filter(uid=uid).exists():
|
||||||
|
email = social_auth_queryset.get(uid=uid).username
|
||||||
|
else:
|
||||||
|
# create user
|
||||||
|
email = gen_user_virtual_id()
|
||||||
|
create_user_success = _import_user_from_work_weixin(email, api_user)
|
||||||
|
if not create_user_success:
|
||||||
|
failed_msg = self._api_user_failed_msg(
|
||||||
|
'', api_user_name, department_id, '导入用户失败')
|
||||||
|
failed.append(failed_msg)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# bind user to department
|
||||||
|
api_user_department_list = api_user.get('department')
|
||||||
|
for department_obj_id in api_user_department_list:
|
||||||
|
group_id = department_map_to_group_dict.get(department_obj_id)
|
||||||
|
if group_id is None:
|
||||||
|
# the api_user also exist in the brother department which not import
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ccnet_api.is_group_user(group_id, email):
|
||||||
|
failed_msg = self._api_user_failed_msg(
|
||||||
|
email, api_user_name, department_obj_id, '部门成员已存在')
|
||||||
|
failed.append(failed_msg)
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
ccnet_api.group_add_member(group_id, DEPARTMENT_OWNER, email)
|
||||||
|
success_msg = self._api_user_success_msg(
|
||||||
|
email, api_user_name, department_obj_id, group_id)
|
||||||
|
success.append(success_msg)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
failed_msg = self._api_user_failed_msg(
|
||||||
|
email, api_user_name, department_id, '导入部门成员失败')
|
||||||
|
failed.append(failed_msg)
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'success': success,
|
||||||
|
'failed': failed,
|
||||||
|
})
|
||||||
|
@@ -141,7 +141,7 @@ from seahub.api2.endpoints.admin.user_activities import UserActivitiesView
|
|||||||
from seahub.api2.endpoints.admin.file_scan_records import AdminFileScanRecords
|
from seahub.api2.endpoints.admin.file_scan_records import AdminFileScanRecords
|
||||||
from seahub.api2.endpoints.admin.notifications import AdminNotificationsView
|
from seahub.api2.endpoints.admin.notifications import AdminNotificationsView
|
||||||
from seahub.api2.endpoints.admin.work_weixin import AdminWorkWeixinDepartments, \
|
from seahub.api2.endpoints.admin.work_weixin import AdminWorkWeixinDepartments, \
|
||||||
AdminWorkWeixinDepartmentMembers, AdminWorkWeixinUsersBatch
|
AdminWorkWeixinDepartmentMembers, AdminWorkWeixinUsersBatch, AdminWorkWeixinDepartmentsImport
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^accounts/', include('seahub.base.registration_urls')),
|
url(r'^accounts/', include('seahub.base.registration_urls')),
|
||||||
@@ -549,6 +549,7 @@ urlpatterns = [
|
|||||||
url(r'^api/v2.1/admin/work-weixin/departments/$', AdminWorkWeixinDepartments.as_view(), name='api-v2.1-admin-work-weixin-departments'),
|
url(r'^api/v2.1/admin/work-weixin/departments/$', AdminWorkWeixinDepartments.as_view(), name='api-v2.1-admin-work-weixin-departments'),
|
||||||
url(r'^api/v2.1/admin/work-weixin/departments/(?P<department_id>\d+)/members/$', AdminWorkWeixinDepartmentMembers.as_view(), name='api-v2.1-admin-work-weixin-department-members'),
|
url(r'^api/v2.1/admin/work-weixin/departments/(?P<department_id>\d+)/members/$', AdminWorkWeixinDepartmentMembers.as_view(), name='api-v2.1-admin-work-weixin-department-members'),
|
||||||
url(r'^api/v2.1/admin/work-weixin/users/batch/$', AdminWorkWeixinUsersBatch.as_view(), name='api-v2.1-admin-work-weixin-users'),
|
url(r'^api/v2.1/admin/work-weixin/users/batch/$', AdminWorkWeixinUsersBatch.as_view(), name='api-v2.1-admin-work-weixin-users'),
|
||||||
|
url(r'^api/v2.1/admin/work-weixin/departments/import/$', AdminWorkWeixinDepartmentsImport.as_view(), name='api-v2.1-admin-work-weixin-department-import'),
|
||||||
|
|
||||||
### system admin ###
|
### system admin ###
|
||||||
url(r'^sysadmin/$', sysadmin, name='sysadmin'),
|
url(r'^sysadmin/$', sysadmin, name='sysadmin'),
|
||||||
|
Reference in New Issue
Block a user