forked from casdoor/casdoor
fix: add excel import support for groups, permissions, and roles (#4585)
This commit is contained in:
@@ -15,6 +15,9 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/xlsx"
|
||||
)
|
||||
|
||||
@@ -36,44 +39,30 @@ func getPermissionMap(owner string) (map[string]*Permission, error) {
|
||||
func UploadPermissions(owner string, path string) (bool, error) {
|
||||
table := xlsx.ReadXlsxFile(path)
|
||||
|
||||
oldUserMap, err := getPermissionMap(owner)
|
||||
if len(table) == 0 {
|
||||
return false, fmt.Errorf("empty table")
|
||||
}
|
||||
|
||||
for idx, row := range table[0] {
|
||||
splitRow := strings.Split(row, "#")
|
||||
if len(splitRow) > 1 {
|
||||
table[0][idx] = splitRow[1]
|
||||
}
|
||||
}
|
||||
|
||||
uploadedPermissions, err := StringArrayToStruct[Permission](table)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
oldPermissionMap, err := getPermissionMap(owner)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
newPermissions := []*Permission{}
|
||||
for index, line := range table {
|
||||
if index == 0 || parseLineItem(&line, 0) == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
permission := &Permission{
|
||||
Owner: parseLineItem(&line, 0),
|
||||
Name: parseLineItem(&line, 1),
|
||||
CreatedTime: parseLineItem(&line, 2),
|
||||
DisplayName: parseLineItem(&line, 3),
|
||||
|
||||
Users: parseListItem(&line, 4),
|
||||
Roles: parseListItem(&line, 5),
|
||||
Domains: parseListItem(&line, 6),
|
||||
|
||||
Model: parseLineItem(&line, 7),
|
||||
Adapter: parseLineItem(&line, 8),
|
||||
ResourceType: parseLineItem(&line, 9),
|
||||
|
||||
Resources: parseListItem(&line, 10),
|
||||
Actions: parseListItem(&line, 11),
|
||||
|
||||
Effect: parseLineItem(&line, 12),
|
||||
IsEnabled: parseLineItemBool(&line, 13),
|
||||
|
||||
Submitter: parseLineItem(&line, 14),
|
||||
Approver: parseLineItem(&line, 15),
|
||||
ApproveTime: parseLineItem(&line, 16),
|
||||
State: parseLineItem(&line, 17),
|
||||
}
|
||||
|
||||
if _, ok := oldUserMap[permission.GetId()]; !ok {
|
||||
for _, permission := range uploadedPermissions {
|
||||
if _, ok := oldPermissionMap[permission.GetId()]; !ok {
|
||||
newPermissions = append(newPermissions, permission)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/xlsx"
|
||||
)
|
||||
|
||||
@@ -36,30 +39,30 @@ func getRoleMap(owner string) (map[string]*Role, error) {
|
||||
func UploadRoles(owner string, path string) (bool, error) {
|
||||
table := xlsx.ReadXlsxFile(path)
|
||||
|
||||
oldUserMap, err := getRoleMap(owner)
|
||||
if len(table) == 0 {
|
||||
return false, fmt.Errorf("empty table")
|
||||
}
|
||||
|
||||
for idx, row := range table[0] {
|
||||
splitRow := strings.Split(row, "#")
|
||||
if len(splitRow) > 1 {
|
||||
table[0][idx] = splitRow[1]
|
||||
}
|
||||
}
|
||||
|
||||
uploadedRoles, err := StringArrayToStruct[Role](table)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
oldRoleMap, err := getRoleMap(owner)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
newRoles := []*Role{}
|
||||
for index, line := range table {
|
||||
if index == 0 || parseLineItem(&line, 0) == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
role := &Role{
|
||||
Owner: parseLineItem(&line, 0),
|
||||
Name: parseLineItem(&line, 1),
|
||||
CreatedTime: parseLineItem(&line, 2),
|
||||
DisplayName: parseLineItem(&line, 3),
|
||||
|
||||
Users: parseListItem(&line, 4),
|
||||
Roles: parseListItem(&line, 5),
|
||||
Domains: parseListItem(&line, 6),
|
||||
IsEnabled: parseLineItemBool(&line, 7),
|
||||
}
|
||||
|
||||
if _, ok := oldUserMap[role.GetId()]; !ok {
|
||||
for _, role := range uploadedRoles {
|
||||
if _, ok := oldRoleMap[role.GetId()]; !ok {
|
||||
newRoles = append(newRoles, role)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Table, Tooltip, Upload} from "antd";
|
||||
import {Button, Modal, Table, Tooltip, Upload} from "antd";
|
||||
import {UploadOutlined} from "@ant-design/icons";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
@@ -22,6 +22,7 @@ import * as GroupBackend from "./backend/GroupBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./common/modal/PopconfirmModal";
|
||||
import * as XLSX from "xlsx";
|
||||
|
||||
class GroupListPage extends BaseListPage {
|
||||
constructor(props) {
|
||||
@@ -89,38 +90,106 @@ class GroupListPage extends BaseListPage {
|
||||
}
|
||||
|
||||
uploadFile(info) {
|
||||
const {status, response: res} = info.file;
|
||||
if (status === "done") {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", "Groups uploaded successfully, refreshing the page");
|
||||
const {pagination} = this.state;
|
||||
this.fetch({pagination});
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to upload")}: ${res.msg}`);
|
||||
}
|
||||
const {status, msg} = info;
|
||||
if (status === "ok") {
|
||||
Setting.showMessage("success", "Groups uploaded successfully, refreshing the page");
|
||||
const {pagination} = this.state;
|
||||
this.fetch({pagination});
|
||||
} else if (status === "error") {
|
||||
Setting.showMessage("error", i18next.t("general:Failed to upload"));
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to upload")}: ${msg}`);
|
||||
}
|
||||
this.setState({uploadJsonData: [], uploadColumns: [], showUploadModal: false});
|
||||
}
|
||||
|
||||
generateDownloadTemplate() {
|
||||
const groupObj = {};
|
||||
const items = Setting.getGroupColumns();
|
||||
items.forEach((item) => {
|
||||
groupObj[item] = null;
|
||||
});
|
||||
const worksheet = XLSX.utils.json_to_sheet([groupObj]);
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
|
||||
XLSX.writeFile(workbook, "import-group.xlsx", {compression: true});
|
||||
}
|
||||
|
||||
renderUpload() {
|
||||
const uploadThis = this;
|
||||
const props = {
|
||||
name: "file",
|
||||
accept: ".xlsx",
|
||||
method: "post",
|
||||
action: `${Setting.ServerUrl}/api/upload-groups`,
|
||||
withCredentials: true,
|
||||
onChange: (info) => {
|
||||
this.uploadFile(info);
|
||||
showUploadList: false,
|
||||
beforeUpload: (file) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const binary = e.target.result;
|
||||
|
||||
try {
|
||||
const workbook = XLSX.read(binary, {type: "array"});
|
||||
if (!workbook.SheetNames || workbook.SheetNames.length === 0) {
|
||||
Setting.showMessage("error", i18next.t("general:No sheets found in file"));
|
||||
return;
|
||||
}
|
||||
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
const jsonData = XLSX.utils.sheet_to_json(worksheet);
|
||||
this.setState({uploadJsonData: jsonData, file: file});
|
||||
|
||||
const columns = Setting.getGroupColumns().map(el => {
|
||||
return {title: el.split("#")[0], dataIndex: el, key: el};
|
||||
});
|
||||
this.setState({uploadColumns: columns}, () => {this.setState({showUploadModal: true});});
|
||||
} catch (err) {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to upload")}: ${err.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
reader.onerror = (error) => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to upload")}: ${error?.message || error}`);
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Upload {...props}>
|
||||
<Button icon={<UploadOutlined />} id="upload-button" size="small">
|
||||
{i18next.t("group:Upload (.xlsx)")}
|
||||
</Button>
|
||||
</Upload>
|
||||
<>
|
||||
<Upload {...props}>
|
||||
<Button icon={<UploadOutlined />} id="upload-button" size="small">
|
||||
{i18next.t("general:Upload (.xlsx)")}
|
||||
</Button>
|
||||
</Upload>
|
||||
<Modal title={i18next.t("general:Upload (.xlsx)")}
|
||||
width={"100%"}
|
||||
closable={true}
|
||||
open={this.state.showUploadModal}
|
||||
okText={i18next.t("general:Click to Upload")}
|
||||
onOk = {() => {
|
||||
const formData = new FormData();
|
||||
formData.append("file", this.state.file);
|
||||
fetch(`${Setting.ServerUrl}/api/upload-groups`, {
|
||||
method: "post",
|
||||
body: formData,
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {uploadThis.uploadFile(res);})
|
||||
.catch((error) => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to upload")}: ${error.message}`);
|
||||
});
|
||||
}}
|
||||
cancelText={i18next.t("general:Cancel")}
|
||||
onCancel={() => {this.setState({showUploadModal: false, uploadJsonData: [], uploadColumns: []});}}
|
||||
>
|
||||
<div style={{marginRight: "34px"}}>
|
||||
<Table scroll={{x: "max-content"}} dataSource={this.state.uploadJsonData} columns={this.state.uploadColumns} />
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -269,6 +338,7 @@ class GroupListPage extends BaseListPage {
|
||||
<div>
|
||||
{i18next.t("general:Groups")}
|
||||
<Button style={{marginRight: "15px"}} type="primary" size="small" onClick={this.addGroup.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
<Button style={{marginRight: "15px"}} type="primary" size="small" onClick={this.generateDownloadTemplate}>{i18next.t("general:Download template")} </Button>
|
||||
{
|
||||
this.renderUpload()
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Switch, Table, Upload} from "antd";
|
||||
import {Button, Modal, Switch, Table, Upload} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as PermissionBackend from "./backend/PermissionBackend";
|
||||
@@ -22,6 +22,7 @@ import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./common/modal/PopconfirmModal";
|
||||
import {UploadOutlined} from "@ant-design/icons";
|
||||
import * as XLSX from "xlsx";
|
||||
|
||||
class PermissionListPage extends BaseListPage {
|
||||
newPermission() {
|
||||
@@ -85,38 +86,106 @@ class PermissionListPage extends BaseListPage {
|
||||
}
|
||||
|
||||
uploadPermissionFile(info) {
|
||||
const {status, response: res} = info.file;
|
||||
if (status === "done") {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", "Users uploaded successfully, refreshing the page");
|
||||
|
||||
const {pagination} = this.state;
|
||||
this.fetch({pagination});
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to sync")}: ${res.msg}`);
|
||||
}
|
||||
const {status, msg} = info;
|
||||
if (status === "ok") {
|
||||
Setting.showMessage("success", "Permissions uploaded successfully, refreshing the page");
|
||||
const {pagination} = this.state;
|
||||
this.fetch({pagination});
|
||||
} else if (status === "error") {
|
||||
Setting.showMessage("error", i18next.t("general:Failed to upload"));
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to upload")}: ${msg}`);
|
||||
}
|
||||
this.setState({uploadJsonData: [], uploadColumns: [], showUploadModal: false});
|
||||
}
|
||||
|
||||
generateDownloadTemplate() {
|
||||
const permissionObj = {};
|
||||
const items = Setting.getPermissionColumns();
|
||||
items.forEach((item) => {
|
||||
permissionObj[item] = null;
|
||||
});
|
||||
const worksheet = XLSX.utils.json_to_sheet([permissionObj]);
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
|
||||
XLSX.writeFile(workbook, "import-permission.xlsx", {compression: true});
|
||||
}
|
||||
|
||||
renderPermissionUpload() {
|
||||
const uploadThis = this;
|
||||
const props = {
|
||||
name: "file",
|
||||
accept: ".xlsx",
|
||||
method: "post",
|
||||
action: `${Setting.ServerUrl}/api/upload-permissions`,
|
||||
withCredentials: true,
|
||||
onChange: (info) => {
|
||||
this.uploadPermissionFile(info);
|
||||
showUploadList: false,
|
||||
beforeUpload: (file) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const binary = e.target.result;
|
||||
|
||||
try {
|
||||
const workbook = XLSX.read(binary, {type: "array"});
|
||||
if (!workbook.SheetNames || workbook.SheetNames.length === 0) {
|
||||
Setting.showMessage("error", i18next.t("general:No sheets found in file"));
|
||||
return;
|
||||
}
|
||||
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
const jsonData = XLSX.utils.sheet_to_json(worksheet);
|
||||
this.setState({uploadJsonData: jsonData, file: file});
|
||||
|
||||
const columns = Setting.getPermissionColumns().map(el => {
|
||||
return {title: el.split("#")[0], dataIndex: el, key: el};
|
||||
});
|
||||
this.setState({uploadColumns: columns}, () => {this.setState({showUploadModal: true});});
|
||||
} catch (err) {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to upload")}: ${err.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
reader.onerror = (error) => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to upload")}: ${error?.message || error}`);
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Upload {...props}>
|
||||
<Button icon={<UploadOutlined />} id="upload-button" size="small">
|
||||
{i18next.t("user:Upload (.xlsx)")}
|
||||
</Button></Upload>
|
||||
<>
|
||||
<Upload {...props}>
|
||||
<Button icon={<UploadOutlined />} id="upload-button" size="small">
|
||||
{i18next.t("general:Upload (.xlsx)")}
|
||||
</Button>
|
||||
</Upload>
|
||||
<Modal title={i18next.t("general:Upload (.xlsx)")}
|
||||
width={"100%"}
|
||||
closable={true}
|
||||
open={this.state.showUploadModal}
|
||||
okText={i18next.t("general:Click to Upload")}
|
||||
onOk = {() => {
|
||||
const formData = new FormData();
|
||||
formData.append("file", this.state.file);
|
||||
fetch(`${Setting.ServerUrl}/api/upload-permissions`, {
|
||||
method: "post",
|
||||
body: formData,
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {uploadThis.uploadPermissionFile(res);})
|
||||
.catch((error) => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to upload")}: ${error.message}`);
|
||||
});
|
||||
}}
|
||||
cancelText={i18next.t("general:Cancel")}
|
||||
onCancel={() => {this.setState({showUploadModal: false, uploadJsonData: [], uploadColumns: []});}}
|
||||
>
|
||||
<div style={{marginRight: "34px"}}>
|
||||
<Table scroll={{x: "max-content"}} dataSource={this.state.uploadJsonData} columns={this.state.uploadColumns} />
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -408,6 +477,7 @@ class PermissionListPage extends BaseListPage {
|
||||
<div>
|
||||
{i18next.t("general:Permissions")}
|
||||
<Button id="add-button" style={{marginRight: "15px"}} type="primary" size="small" onClick={this.addPermission.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
<Button style={{marginRight: "15px"}} type="primary" size="small" onClick={this.generateDownloadTemplate}>{i18next.t("general:Download template")} </Button>
|
||||
{
|
||||
this.renderPermissionUpload()
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Switch, Table, Upload} from "antd";
|
||||
import {Button, Modal, Switch, Table, Upload} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as RoleBackend from "./backend/RoleBackend";
|
||||
@@ -22,6 +22,7 @@ import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./common/modal/PopconfirmModal";
|
||||
import {UploadOutlined} from "@ant-design/icons";
|
||||
import * as XLSX from "xlsx";
|
||||
|
||||
class RoleListPage extends BaseListPage {
|
||||
newRole() {
|
||||
@@ -77,39 +78,106 @@ class RoleListPage extends BaseListPage {
|
||||
}
|
||||
|
||||
uploadRoleFile(info) {
|
||||
const {status, response: res} = info.file;
|
||||
if (status === "done") {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", "Users uploaded successfully, refreshing the page");
|
||||
|
||||
const {pagination} = this.state;
|
||||
this.fetch({pagination});
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to sync")}: ${res.msg}`);
|
||||
}
|
||||
const {status, msg} = info;
|
||||
if (status === "ok") {
|
||||
Setting.showMessage("success", "Roles uploaded successfully, refreshing the page");
|
||||
const {pagination} = this.state;
|
||||
this.fetch({pagination});
|
||||
} else if (status === "error") {
|
||||
Setting.showMessage("error", i18next.t("general:Failed to upload"));
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to upload")}: ${msg}`);
|
||||
}
|
||||
this.setState({uploadJsonData: [], uploadColumns: [], showUploadModal: false});
|
||||
}
|
||||
|
||||
generateDownloadTemplate() {
|
||||
const roleObj = {};
|
||||
const items = Setting.getRoleColumns();
|
||||
items.forEach((item) => {
|
||||
roleObj[item] = null;
|
||||
});
|
||||
const worksheet = XLSX.utils.json_to_sheet([roleObj]);
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
|
||||
XLSX.writeFile(workbook, "import-role.xlsx", {compression: true});
|
||||
}
|
||||
|
||||
renderRoleUpload() {
|
||||
const uploadThis = this;
|
||||
const props = {
|
||||
name: "file",
|
||||
accept: ".xlsx",
|
||||
method: "post",
|
||||
action: `${Setting.ServerUrl}/api/upload-roles`,
|
||||
withCredentials: true,
|
||||
onChange: (info) => {
|
||||
this.uploadRoleFile(info);
|
||||
showUploadList: false,
|
||||
beforeUpload: (file) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const binary = e.target.result;
|
||||
|
||||
try {
|
||||
const workbook = XLSX.read(binary, {type: "array"});
|
||||
if (!workbook.SheetNames || workbook.SheetNames.length === 0) {
|
||||
Setting.showMessage("error", i18next.t("general:No sheets found in file"));
|
||||
return;
|
||||
}
|
||||
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
const jsonData = XLSX.utils.sheet_to_json(worksheet);
|
||||
this.setState({uploadJsonData: jsonData, file: file});
|
||||
|
||||
const columns = Setting.getRoleColumns().map(el => {
|
||||
return {title: el.split("#")[0], dataIndex: el, key: el};
|
||||
});
|
||||
this.setState({uploadColumns: columns}, () => {this.setState({showUploadModal: true});});
|
||||
} catch (err) {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to upload")}: ${err.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
reader.onerror = (error) => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to upload")}: ${error?.message || error}`);
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Upload {...props}>
|
||||
<Button icon={<UploadOutlined />} size="small">
|
||||
{i18next.t("user:Upload (.xlsx)")}
|
||||
</Button>
|
||||
</Upload>
|
||||
<>
|
||||
<Upload {...props}>
|
||||
<Button icon={<UploadOutlined />} size="small">
|
||||
{i18next.t("general:Upload (.xlsx)")}
|
||||
</Button>
|
||||
</Upload>
|
||||
<Modal title={i18next.t("general:Upload (.xlsx)")}
|
||||
width={"100%"}
|
||||
closable={true}
|
||||
open={this.state.showUploadModal}
|
||||
okText={i18next.t("general:Click to Upload")}
|
||||
onOk = {() => {
|
||||
const formData = new FormData();
|
||||
formData.append("file", this.state.file);
|
||||
fetch(`${Setting.ServerUrl}/api/upload-roles`, {
|
||||
method: "post",
|
||||
body: formData,
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {uploadThis.uploadRoleFile(res);})
|
||||
.catch((error) => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to upload")}: ${error.message}`);
|
||||
});
|
||||
}}
|
||||
cancelText={i18next.t("general:Cancel")}
|
||||
onCancel={() => {this.setState({showUploadModal: false, uploadJsonData: [], uploadColumns: []});}}
|
||||
>
|
||||
<div style={{marginRight: "34px"}}>
|
||||
<Table scroll={{x: "max-content"}} dataSource={this.state.uploadJsonData} columns={this.state.uploadColumns} />
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
renderTable(roles) {
|
||||
@@ -253,6 +321,7 @@ class RoleListPage extends BaseListPage {
|
||||
<div>
|
||||
{i18next.t("general:Roles")}
|
||||
<Button style={{marginRight: "5px"}} type="primary" size="small" onClick={this.addRole.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
<Button style={{marginRight: "5px"}} type="primary" size="small" onClick={this.generateDownloadTemplate}>{i18next.t("general:Download template")} </Button>
|
||||
{
|
||||
this.renderRoleUpload()
|
||||
}
|
||||
|
||||
@@ -471,6 +471,16 @@ export const UserFields = ["owner", "name", "password", "display_name", "id", "t
|
||||
"created_time", "updated_time", "deleted_time",
|
||||
"ip_whitelist"];
|
||||
|
||||
export const GroupFields = ["owner", "name", "created_time", "updated_time", "display_name", "manager",
|
||||
"contact_email", "type", "parent_id", "is_top_group", "is_enabled"];
|
||||
|
||||
export const RoleFields = ["owner", "name", "created_time", "display_name", "description",
|
||||
"users", "groups", "roles", "domains", "is_enabled"];
|
||||
|
||||
export const PermissionFields = ["owner", "name", "created_time", "display_name", "description",
|
||||
"users", "groups", "roles", "domains", "model", "adapter", "resource_type",
|
||||
"resources", "actions", "effect", "is_enabled", "submitter", "approver", "approve_time", "state"];
|
||||
|
||||
export const GetTranslatedUserItems = () => {
|
||||
return [
|
||||
{name: "Organization", label: i18next.t("general:Organization")},
|
||||
@@ -565,6 +575,54 @@ export function getUserColumns() {
|
||||
});
|
||||
}
|
||||
|
||||
export function getGroupColumns() {
|
||||
return GroupFields.map(field => {
|
||||
let transField = field.toLowerCase().split("_").join(" ");
|
||||
transField = transField.charAt(0).toUpperCase() + transField.slice(1);
|
||||
transField = transField.replace("Id", "ID");
|
||||
if (transField === "Owner") {
|
||||
transField = "Organization";
|
||||
}
|
||||
const toTranslateList = ["general", "group"].map(ns => `${ns}:${transField}`);
|
||||
const transResult = toTranslateList.map(item => i18next.t(item) === transField ? null : i18next.t(item))
|
||||
.find(item => item !== null);
|
||||
transField = transResult ? transResult : transField;
|
||||
return `${transField}#${field}`;
|
||||
});
|
||||
}
|
||||
|
||||
export function getRoleColumns() {
|
||||
return RoleFields.map(field => {
|
||||
let transField = field.toLowerCase().split("_").join(" ");
|
||||
transField = transField.charAt(0).toUpperCase() + transField.slice(1);
|
||||
transField = transField.replace("Id", "ID");
|
||||
if (transField === "Owner") {
|
||||
transField = "Organization";
|
||||
}
|
||||
const toTranslateList = ["general", "role"].map(ns => `${ns}:${transField}`);
|
||||
const transResult = toTranslateList.map(item => i18next.t(item) === transField ? null : i18next.t(item))
|
||||
.find(item => item !== null);
|
||||
transField = transResult ? transResult : transField;
|
||||
return `${transField}#${field}`;
|
||||
});
|
||||
}
|
||||
|
||||
export function getPermissionColumns() {
|
||||
return PermissionFields.map(field => {
|
||||
let transField = field.toLowerCase().split("_").join(" ");
|
||||
transField = transField.charAt(0).toUpperCase() + transField.slice(1);
|
||||
transField = transField.replace("Id", "ID");
|
||||
if (transField === "Owner") {
|
||||
transField = "Organization";
|
||||
}
|
||||
const toTranslateList = ["general", "permission"].map(ns => `${ns}:${transField}`);
|
||||
const transResult = toTranslateList.map(item => i18next.t(item) === transField ? null : i18next.t(item))
|
||||
.find(item => item !== null);
|
||||
transField = transResult ? transResult : transField;
|
||||
return `${transField}#${field}`;
|
||||
});
|
||||
}
|
||||
|
||||
export function initCountries() {
|
||||
const countries = require("i18n-iso-countries");
|
||||
countries.registerLocale(require("i18n-iso-countries/langs/" + getLanguage() + ".json"));
|
||||
|
||||
@@ -230,10 +230,10 @@ class UserListPage extends BaseListPage {
|
||||
<>
|
||||
<Upload {...props}>
|
||||
<Button icon={<UploadOutlined />} id="upload-button" size="small">
|
||||
{i18next.t("user:Upload (.xlsx)")}
|
||||
{i18next.t("general:Upload (.xlsx)")}
|
||||
</Button>
|
||||
</Upload>
|
||||
<Modal title={i18next.t("user:Upload (.xlsx)")}
|
||||
<Modal title={i18next.t("general:Upload (.xlsx)")}
|
||||
width={"100%"}
|
||||
closable={true}
|
||||
open={this.state.showUploadModal}
|
||||
|
||||
@@ -510,6 +510,7 @@
|
||||
"Unknown authentication type": "Unknown authentication type",
|
||||
"Up": "Up",
|
||||
"Updated time": "Updated time",
|
||||
"Upload (.xlsx)": "Upload (.xlsx)",
|
||||
"User": "User",
|
||||
"User - Tooltip": "Make sure the username is correct",
|
||||
"User Management": "User Management",
|
||||
@@ -535,7 +536,6 @@
|
||||
"Parent group - Tooltip": "Parent group of this group",
|
||||
"Physical": "Physical",
|
||||
"Show all": "Show all",
|
||||
"Upload (.xlsx)": "Upload (.xlsx)",
|
||||
"Virtual": "Virtual",
|
||||
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page"
|
||||
},
|
||||
@@ -1381,7 +1381,6 @@
|
||||
"Title - Tooltip": "Job title or position held in the affiliation",
|
||||
"Two passwords you typed do not match.": "Two passwords you typed do not match.",
|
||||
"Unlink": "Unlink",
|
||||
"Upload (.xlsx)": "Upload (.xlsx)",
|
||||
"Upload ID card back picture": "Upload ID card back picture",
|
||||
"Upload ID card front picture": "Upload ID card front picture",
|
||||
"Upload ID card with person picture": "Upload ID card with person picture",
|
||||
|
||||
@@ -509,6 +509,7 @@
|
||||
"Unknown authentication type": "未知的认证类型",
|
||||
"Up": "上移",
|
||||
"Updated time": "更新时间",
|
||||
"Upload (.xlsx)": "上传 (.xlsx)",
|
||||
"User": "用户",
|
||||
"User - Tooltip": "请确保用户名正确",
|
||||
"User Management": "用户管理",
|
||||
@@ -534,7 +535,6 @@
|
||||
"Parent group - Tooltip": "该组的父组",
|
||||
"Physical": "实体组",
|
||||
"Show all": "显示全部",
|
||||
"Upload (.xlsx)": "上传(.xlsx)",
|
||||
"Virtual": "虚拟组",
|
||||
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "您需要先删除所有子组。您可以在 [组织] -> [群组] 页面左侧的群组树中查看子组"
|
||||
},
|
||||
@@ -1368,7 +1368,6 @@
|
||||
"Title - Tooltip": "在工作单位担任的职务",
|
||||
"Two passwords you typed do not match.": "两次输入的密码不匹配。",
|
||||
"Unlink": "解绑",
|
||||
"Upload (.xlsx)": "上传(.xlsx)",
|
||||
"Upload ID card back picture": "上传身份证反面照片",
|
||||
"Upload ID card front picture": "上传身份证正面照片",
|
||||
"Upload ID card with person picture": "上传手持身份证照片",
|
||||
|
||||
Reference in New Issue
Block a user