diff --git a/frontend/src/components/dialog/import-members-dialog.js b/frontend/src/components/dialog/import-members-dialog.js
new file mode 100644
index 0000000000..9700611ef4
--- /dev/null
+++ b/frontend/src/components/dialog/import-members-dialog.js
@@ -0,0 +1,68 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Alert, Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
+import { gettext, siteRoot } from '../../utils/constants';
+
+const propTypes = {
+ toggleImportMembersDialog: PropTypes.func.isRequired,
+ importMembersInBatch: PropTypes.func.isRequired,
+};
+
+class ImportMembersDialog extends React.Component {
+ constructor(props) {
+ super(props);
+ this.fileInputRef = React.createRef();
+ this.state = {
+ errorMsg: ''
+ };
+ }
+
+ toggle = () => {
+ this.props.toggleImportMembersDialog();
+ }
+
+ openFileInput = () => {
+ this.fileInputRef.current.click();
+ }
+
+ uploadFile = (e) => {
+ // no file selected
+ if (!this.fileInputRef.current.files.length) {
+ return;
+ }
+ // check file extension
+ let fileName = this.fileInputRef.current.files[0].name;
+ if(fileName.substr(fileName.lastIndexOf('.') + 1) != 'xlsx') {
+ this.setState({
+ errorMsg: gettext('Please choose a .xlsx file.')
+ });
+ return;
+ }
+ const file = this.fileInputRef.current.files[0];
+ this.props.importMembersInBatch(file);
+ this.toggle();
+ }
+
+ render() {
+ let { errorMsg } = this.state;
+ return (
+
+ {gettext('Import members from a .xlsx file')}
+
+
+ {gettext('Download an example file')}
+
+
+ {errorMsg && {errorMsg}}
+
+
+
+
+
+ );
+ }
+}
+
+ImportMembersDialog.propTypes = propTypes;
+
+export default ImportMembersDialog;
diff --git a/frontend/src/pages/groups/group-view.js b/frontend/src/pages/groups/group-view.js
index c16c351c8d..d0bd60e1e8 100644
--- a/frontend/src/pages/groups/group-view.js
+++ b/frontend/src/pages/groups/group-view.js
@@ -18,7 +18,7 @@ import CreateDepartmentRepoDialog from '../../components/dialog/create-departmen
import DismissGroupDialog from '../../components/dialog/dismiss-group-dialog';
import RenameGroupDialog from '../../components/dialog/rename-group-dialog';
import TransferGroupDialog from '../../components/dialog/transfer-group-dialog';
-// import ImportMembersDialog from '../../components/dialog/import-members-dialog';
+import ImportMembersDialog from '../../components/dialog/import-members-dialog';
import ManageMembersDialog from '../../components/dialog/manage-members-dialog';
import LeaveGroupDialog from '../../components/dialog/leave-group-dialog';
import SharedRepoListView from '../../components/shared-repo-list-view/shared-repo-list-view';
@@ -63,7 +63,7 @@ class GroupView extends React.Component {
showRenameGroupDialog: false,
showDismissGroupDialog: false,
showTransferGroupDialog: false,
- // showImportMembersDialog: false,
+ showImportMembersDialog: false,
showManageMembersDialog: false,
groupMembers: [],
isShowDetails: false,
@@ -283,11 +283,24 @@ class GroupView extends React.Component {
});
}
- // toggleImportMembersDialog= () => {
- // this.setState({
- // showImportMembersDialog: !this.state.showImportMembersDialog
- // });
- // }
+ toggleImportMembersDialog= () => {
+ this.setState({
+ showImportMembersDialog: !this.state.showImportMembersDialog
+ });
+ }
+
+ importMembersInBatch= (file) => {
+ toaster.notify(gettext('It may take some time, please wait.'));
+ seafileAPI.importGroupMembersViaFile(this.state.currentGroup.id, file).then((res) => {
+ res.data.failed.map(item => {
+ const msg = `${item.email}: ${item.error_msg}`;
+ toaster.danger(msg);
+ });
+ }).catch((error) => {
+ let errMsg = Utils.getErrorMsg(error);
+ toaster.danger(errMsg);
+ });
+ }
toggleManageMembersDialog = () => {
this.setState({
@@ -464,7 +477,7 @@ class GroupView extends React.Component {
}
{(this.state.isStaff || this.state.isOwner) &&
}
@@ -596,13 +609,12 @@ class GroupView extends React.Component {
onGroupChanged={this.props.onGroupChanged}
/>
}
- {/* this.state.showImportMembersDialog &&
+ { this.state.showImportMembersDialog &&
- */}
+ }
{this.state.showManageMembersDialog &&
\d+)/group-owned-libraries/(?P[-0-9a-f]{36})/$', GroupOwnedLibrary.as_view(), name='api-v2.1-owned-group-library'),
url(r'^api/v2.1/groups/(?P\d+)/members/$', GroupMembers.as_view(), name='api-v2.1-group-members'),
url(r'^api/v2.1/groups/(?P\d+)/members/bulk/$', GroupMembersBulk.as_view(), name='api-v2.1-group-members-bulk'),
+ url(r'^api/v2.1/groups/(?P\d+)/members/import/$', GroupMembersImport.as_view(), name='api-v2.1-group-members-import'),
+ url(r'^api/v2.1/group-members-import-example/$', GroupMembersImportExample.as_view(), name='api-v2.1-group-members-import-example'),
url(r'^api/v2.1/groups/(?P\d+)/members/(?P[^/]+)/$', GroupMember.as_view(), name='api-v2.1-group-member'),
url(r'^api/v2.1/search-group/$', SearchGroup.as_view(), name='api-v2.1-search-group'),