From b10fb97c92da88df0be23e44fcb2cb360d6aa993 Mon Sep 17 00:00:00 2001 From: Yaodong Yu <2814461814@qq.com> Date: Fri, 25 Nov 2022 16:02:20 +0800 Subject: [PATCH] feat: finish policy list management (#1317) --- controllers/casbin_adapter.go | 66 ++++++- object/casbin_adapter.go | 122 ++++++++---- routers/router.go | 3 + util/struct.go | 29 +++ web/src/AdapterEditPage.js | 113 +---------- web/src/AdapterListPage.js | 2 +- web/src/Setting.js | 8 +- web/src/backend/AdapterBackend.js | 35 ++++ web/src/common/PoliciyTable.js | 308 ++++++++++++++++++++++++++++++ web/src/locales/de/data.json | 7 +- web/src/locales/en/data.json | 7 +- web/src/locales/fr/data.json | 7 +- web/src/locales/ja/data.json | 7 +- web/src/locales/ko/data.json | 7 +- web/src/locales/ru/data.json | 7 +- web/src/locales/zh/data.json | 7 +- 16 files changed, 582 insertions(+), 153 deletions(-) create mode 100644 util/struct.go create mode 100644 web/src/common/PoliciyTable.js diff --git a/controllers/casbin_adapter.go b/controllers/casbin_adapter.go index 4d394b9a..12fdc1a8 100644 --- a/controllers/casbin_adapter.go +++ b/controllers/casbin_adapter.go @@ -18,6 +18,7 @@ import ( "encoding/json" "github.com/beego/beego/utils/pagination" + xormadapter "github.com/casbin/xorm-adapter/v3" "github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/util" ) @@ -89,6 +90,69 @@ func (c *ApiController) SyncPolicies() { id := c.Input().Get("id") adapter := object.GetCasbinAdapter(id) - c.Data["json"] = object.SyncPolicies(adapter) + policies, err := object.SyncPolicies(adapter) + if err != nil { + c.ResponseError(err.Error()) + return + } + + c.Data["json"] = policies + c.ServeJSON() +} + +func (c *ApiController) UpdatePolicy() { + id := c.Input().Get("id") + adapter := object.GetCasbinAdapter(id) + var policies []xormadapter.CasbinRule + err := json.Unmarshal(c.Ctx.Input.RequestBody, &policies) + if err != nil { + c.ResponseError(err.Error()) + return + } + + affected, err := object.UpdatePolicy(util.CasbinToSlice(policies[0]), util.CasbinToSlice(policies[1]), adapter) + if err != nil { + c.ResponseError(err.Error()) + return + } + c.Data["json"] = wrapActionResponse(affected) + c.ServeJSON() +} + +func (c *ApiController) AddPolicy() { + id := c.Input().Get("id") + adapter := object.GetCasbinAdapter(id) + var policy xormadapter.CasbinRule + err := json.Unmarshal(c.Ctx.Input.RequestBody, &policy) + if err != nil { + c.ResponseError(err.Error()) + return + } + + affected, err := object.AddPolicy(util.CasbinToSlice(policy), adapter) + if err != nil { + c.ResponseError(err.Error()) + return + } + c.Data["json"] = wrapActionResponse(affected) + c.ServeJSON() +} + +func (c *ApiController) RemovePolicy() { + id := c.Input().Get("id") + adapter := object.GetCasbinAdapter(id) + var policy xormadapter.CasbinRule + err := json.Unmarshal(c.Ctx.Input.RequestBody, &policy) + if err != nil { + c.ResponseError(err.Error()) + return + } + + affected, err := object.RemovePolicy(util.CasbinToSlice(policy), adapter) + if err != nil { + c.ResponseError(err.Error()) + return + } + c.Data["json"] = wrapActionResponse(affected) c.ServeJSON() } diff --git a/object/casbin_adapter.go b/object/casbin_adapter.go index 79f77892..c660a49f 100644 --- a/object/casbin_adapter.go +++ b/object/casbin_adapter.go @@ -148,34 +148,7 @@ func (casbinAdapter *CasbinAdapter) getTable() string { } } -func safeReturn(policy []string, i int) string { - if len(policy) > i { - return policy[i] - } else { - return "" - } -} - -func matrixToCasbinRules(pType string, policies [][]string) []*xormadapter.CasbinRule { - res := []*xormadapter.CasbinRule{} - - for _, policy := range policies { - line := xormadapter.CasbinRule{ - Ptype: pType, - V0: safeReturn(policy, 0), - V1: safeReturn(policy, 1), - V2: safeReturn(policy, 2), - V3: safeReturn(policy, 3), - V4: safeReturn(policy, 4), - V5: safeReturn(policy, 5), - } - res = append(res, &line) - } - - return res -} - -func SyncPolicies(casbinAdapter *CasbinAdapter) []*xormadapter.CasbinRule { +func initEnforcer(modelObj *Model, casbinAdapter *CasbinAdapter) (*casbin.Enforcer, error) { // init Adapter if casbinAdapter.Adapter == nil { var dataSourceName string @@ -191,20 +164,60 @@ func SyncPolicies(casbinAdapter *CasbinAdapter) []*xormadapter.CasbinRule { dataSourceName = strings.ReplaceAll(dataSourceName, "dbi.", "db.") } - casbinAdapter.Adapter, _ = xormadapter.NewAdapterByEngineWithTableName(NewAdapter(casbinAdapter.DatabaseType, dataSourceName, casbinAdapter.Database).Engine, casbinAdapter.getTable(), "") + var err error + casbinAdapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(NewAdapter(casbinAdapter.DatabaseType, dataSourceName, casbinAdapter.Database).Engine, casbinAdapter.getTable(), "") + if err != nil { + return nil, err + } } // init Model - modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model) m, err := model.NewModelFromString(modelObj.ModelText) if err != nil { - panic(err) + return nil, err } // init Enforcer enforcer, err := casbin.NewEnforcer(m, casbinAdapter.Adapter) if err != nil { - panic(err) + return nil, err + } + + return enforcer, nil +} + +func safeReturn(policy []string, i int) string { + if len(policy) > i { + return policy[i] + } else { + return "" + } +} + +func matrixToCasbinRules(Ptype string, policies [][]string) []*xormadapter.CasbinRule { + res := []*xormadapter.CasbinRule{} + + for _, policy := range policies { + line := xormadapter.CasbinRule{ + Ptype: Ptype, + V0: safeReturn(policy, 0), + V1: safeReturn(policy, 1), + V2: safeReturn(policy, 2), + V3: safeReturn(policy, 3), + V4: safeReturn(policy, 4), + V5: safeReturn(policy, 5), + } + res = append(res, &line) + } + + return res +} + +func SyncPolicies(casbinAdapter *CasbinAdapter) ([]*xormadapter.CasbinRule, error) { + modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model) + enforcer, err := initEnforcer(modelObj, casbinAdapter) + if err != nil { + return nil, err } policies := matrixToCasbinRules("p", enforcer.GetPolicy()) @@ -212,5 +225,48 @@ func SyncPolicies(casbinAdapter *CasbinAdapter) []*xormadapter.CasbinRule { policies = append(policies, matrixToCasbinRules("g", enforcer.GetGroupingPolicy())...) } - return policies + return policies, nil +} + +func UpdatePolicy(oldPolicy, newPolicy []string, casbinAdapter *CasbinAdapter) (bool, error) { + modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model) + enforcer, err := initEnforcer(modelObj, casbinAdapter) + if err != nil { + return false, err + } + + affected, err := enforcer.UpdatePolicy(oldPolicy, newPolicy) + if err != nil { + return affected, err + } + return affected, nil +} + +func AddPolicy(policy []string, casbinAdapter *CasbinAdapter) (bool, error) { + modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model) + enforcer, err := initEnforcer(modelObj, casbinAdapter) + if err != nil { + return false, err + } + + affected, err := enforcer.AddPolicy(policy) + if err != nil { + return affected, err + } + return affected, nil +} + +func RemovePolicy(policy []string, casbinAdapter *CasbinAdapter) (bool, error) { + modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model) + enforcer, err := initEnforcer(modelObj, casbinAdapter) + if err != nil { + return false, err + } + + affected, err := enforcer.RemovePolicy(policy) + if err != nil { + return affected, err + } + + return affected, nil } diff --git a/routers/router.go b/routers/router.go index 7d8e4c7c..4b03c706 100644 --- a/routers/router.go +++ b/routers/router.go @@ -105,6 +105,9 @@ func initAPI() { beego.Router("/api/add-adapter", &controllers.ApiController{}, "POST:AddCasbinAdapter") beego.Router("/api/delete-adapter", &controllers.ApiController{}, "POST:DeleteCasbinAdapter") beego.Router("/api/sync-policies", &controllers.ApiController{}, "GET:SyncPolicies") + beego.Router("/api/update-policy", &controllers.ApiController{}, "POST:UpdatePolicy") + beego.Router("/api/add-policy", &controllers.ApiController{}, "POST:AddPolicy") + beego.Router("/api/remove-policy", &controllers.ApiController{}, "POST:RemovePolicy") beego.Router("/api/set-password", &controllers.ApiController{}, "POST:SetPassword") beego.Router("/api/check-user-password", &controllers.ApiController{}, "POST:CheckUserPassword") diff --git a/util/struct.go b/util/struct.go new file mode 100644 index 00000000..1dbedfc8 --- /dev/null +++ b/util/struct.go @@ -0,0 +1,29 @@ +// Copyright 2022 The Casdoor Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import xormadapter "github.com/casbin/xorm-adapter/v3" + +func CasbinToSlice(casbinRule xormadapter.CasbinRule) []string { + s := []string{ + casbinRule.V0, + casbinRule.V1, + casbinRule.V2, + casbinRule.V3, + casbinRule.V4, + casbinRule.V5, + } + return s +} diff --git a/web/src/AdapterEditPage.js b/web/src/AdapterEditPage.js index 4d0be6c6..825516ed 100644 --- a/web/src/AdapterEditPage.js +++ b/web/src/AdapterEditPage.js @@ -13,7 +13,7 @@ // limitations under the License. import React from "react"; -import {Button, Card, Col, Input, InputNumber, Row, Select, Switch, Table, Tooltip} from "antd"; +import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd"; import * as AdapterBackend from "./backend/AdapterBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as Setting from "./Setting"; @@ -21,7 +21,7 @@ import i18next from "i18next"; import "codemirror/lib/codemirror.css"; import * as ModelBackend from "./backend/ModelBackend"; -import {EditOutlined, MinusOutlined} from "@ant-design/icons"; +import PolicyTable from "./common/PoliciyTable"; require("codemirror/theme/material-darker.css"); require("codemirror/mode/javascript/javascript"); @@ -32,12 +32,11 @@ class AdapterEditPage extends React.Component { super(props); this.state = { classes: props, - organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName, + owner: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName, adapterName: props.match.params.adapterName, adapter: null, organizations: [], models: [], - policyLists: [], mode: props.location.mode !== undefined ? props.location.mode : "edit", }; } @@ -48,7 +47,7 @@ class AdapterEditPage extends React.Component { } getAdapter() { - AdapterBackend.getAdapter(this.state.organizationName, this.state.adapterName) + AdapterBackend.getAdapter(this.state.owner, this.state.adapterName) .then((adapter) => { this.setState({ adapter: adapter, @@ -93,93 +92,6 @@ class AdapterEditPage extends React.Component { }); } - synPolicies() { - this.setState({loading: true}); - AdapterBackend.syncPolicies(this.state.adapter.owner, this.state.adapter.name) - .then((res) => { - this.setState({loading: false, policyLists: res}); - }) - .catch(error => { - this.setState({loading: false}); - Setting.showMessage("error", `Adapter failed to get policies: ${error}`); - }); - } - - renderTable(table) { - const columns = [ - { - title: "Rule Type", - dataIndex: "PType", - key: "PType", - width: "100px", - }, - { - title: "V0", - dataIndex: "V0", - key: "V0", - width: "100px", - }, - { - title: "V1", - dataIndex: "V1", - key: "V1", - width: "100px", - }, - { - title: "V2", - dataIndex: "V2", - key: "V2", - width: "100px", - }, - { - title: "V3", - dataIndex: "V3", - key: "V3", - width: "100px", - }, - { - title: "V4", - dataIndex: "V4", - key: "V4", - width: "100px", - }, - { - title: "V5", - dataIndex: "V5", - key: "V5", - width: "100px", - }, - { - title: "Option", - key: "option", - width: "100px", - render: (text, record, index) => { - return ( -
- -
- ); - }, - }]; - - return ( -
- - - ); - } - renderAdapter() { return ( {Setting.getLabel(i18next.t("adapter:Policies"), i18next.t("adapter:Policies - Tooltip"))} : - - - - - - - - - { - this.renderTable(this.state.policyLists) - } + + @@ -371,7 +272,7 @@ class AdapterEditPage extends React.Component { if (willExist) { this.props.history.push("/adapters"); } else { - this.props.history.push(`/adapters/${this.state.adapter.name}`); + this.props.history.push(`/adapters/${this.state.owner}/${this.state.adapter.name}`); } } else { Setting.showMessage("error", res.msg); diff --git a/web/src/AdapterListPage.js b/web/src/AdapterListPage.js index 09f653b7..c86a99b5 100644 --- a/web/src/AdapterListPage.js +++ b/web/src/AdapterListPage.js @@ -45,7 +45,7 @@ class AdapterListPage extends BaseListPage { const newAdapter = this.newAdapter(); AdapterBackend.addAdapter(newAdapter) .then((res) => { - this.props.history.push({pathname: `/adapters/${newAdapter.owner}/${newAdapter.name}`, mode: "add"}); + this.props.history.push({pathname: `/adapters/${newAdapter.organization}/${newAdapter.name}`, mode: "add"}); } ) .catch(error => { diff --git a/web/src/Setting.js b/web/src/Setting.js index 016305e2..c1700b6d 100644 --- a/web/src/Setting.js +++ b/web/src/Setting.js @@ -416,9 +416,7 @@ export function goToLinkSoft(ths, link) { } export function showMessage(type, text) { - if (type === "") { - return; - } else if (type === "success") { + if (type === "success") { message.success(text); } else if (type === "error") { message.error(text); @@ -445,8 +443,8 @@ export function deepCopy(obj) { return Object.assign({}, obj); } -export function addRow(array, row) { - return [...array, row]; +export function addRow(array, row, position = "end") { + return position === "end" ? [...array, row] : [row, ...array]; } export function prependRow(array, row) { diff --git a/web/src/backend/AdapterBackend.js b/web/src/backend/AdapterBackend.js index ed706694..c393bb22 100644 --- a/web/src/backend/AdapterBackend.js +++ b/web/src/backend/AdapterBackend.js @@ -70,6 +70,41 @@ export function deleteAdapter(Adapter) { }).then(res => res.json()); } +export function UpdatePolicy(owner, name, policy) { + // eslint-disable-next-line no-console + console.log(policy); + return fetch(`${Setting.ServerUrl}/api/update-policy?id=${owner}/${encodeURIComponent(name)}`, { + method: "POST", + credentials: "include", + body: JSON.stringify(policy), + headers: { + "Accept-Language": Setting.getAcceptLanguage(), + }, + }).then(res => res.json()); +} + +export function AddPolicy(owner, name, policy) { + return fetch(`${Setting.ServerUrl}/api/add-policy?id=${owner}/${encodeURIComponent(name)}`, { + method: "POST", + credentials: "include", + body: JSON.stringify(policy), + headers: { + "Accept-Language": Setting.getAcceptLanguage(), + }, + }).then(res => res.json()); +} + +export function RemovePolicy(owner, name, policy) { + return fetch(`${Setting.ServerUrl}/api/remove-policy?id=${owner}/${encodeURIComponent(name)}`, { + method: "POST", + credentials: "include", + body: JSON.stringify(policy), + headers: { + "Accept-Language": Setting.getAcceptLanguage(), + }, + }).then(res => res.json()); +} + export function syncPolicies(owner, name) { return fetch(`${Setting.ServerUrl}/api/sync-policies?id=${owner}/${encodeURIComponent(name)}`, { method: "GET", diff --git a/web/src/common/PoliciyTable.js b/web/src/common/PoliciyTable.js new file mode 100644 index 00000000..7b69974c --- /dev/null +++ b/web/src/common/PoliciyTable.js @@ -0,0 +1,308 @@ +// Copyright 2022 The Casdoor Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from "react"; +import {DeleteOutlined, EditOutlined} from "@ant-design/icons"; +import {Button, Input, Popconfirm, Table, Tooltip} from "antd"; +import * as Setting from "../Setting"; +import * as AdapterBackend from "../backend/AdapterBackend"; +import i18next from "i18next"; + +class PolicyTable extends React.Component { + constructor(props) { + super(props); + this.state = { + policyLists: [], + loading: false, + editingIndex: "", + oldPolicy: "", + add: false, + }; + } + + UNSAFE_componentWillMount() { + if (this.props.mode === "edit") { + this.synPolicies(); + } + } + + isEditing = (index) => { + return index === this.state.editingIndex; + }; + + edit = (record, index) => { + this.setState({editingIndex: index, oldPolicy: Setting.deepCopy(record)}); + }; + + cancel = (table, index) => { + Object.keys(table[index]).forEach((key) => { + table[index][key] = this.state.oldPolicy[key]; + }); + this.updateTable(table); + this.setState({editingIndex: "", oldPolicy: ""}); + if (this.state.add) { + this.deleteRow(this.state.policyLists, index); + this.setState({add: false}); + } + }; + + updateTable(table) { + this.setState({policyLists: table}); + } + + updateField(table, index, key, value) { + table[index][key] = value; + this.updateTable(table); + } + + addRow(table) { + const row = {Ptype: "p"}; + if (table === undefined) { + table = []; + } + table = Setting.addRow(table, row, "top"); + this.updateTable(table); + this.edit(row, 0); + this.setState({add: true}); + } + + deleteRow(table, i) { + table = Setting.deleteRow(table, i); + this.updateTable(table); + } + + save(table, i) { + this.state.add ? this.addPolicy(table, i) : this.updatePolicy(table, i); + } + + synPolicies() { + this.setState({loading: true}); + AdapterBackend.syncPolicies(this.props.owner, this.props.name) + .then((res) => { + if (res.status !== "error") { + this.setState({loading: false, policyLists: res}); + } else { + this.setState({loading: false}); + Setting.showMessage("error", `Adapter failed to get policies, ${res.msg}`); + } + }) + .catch(error => { + this.setState({loading: false}); + Setting.showMessage("error", `Adapter failed to get policies, ${error}`); + }); + } + + updatePolicy(table, i) { + AdapterBackend.UpdatePolicy(this.props.owner, this.props.name, [this.state.oldPolicy, table[i]]).then(res => { + if (res.status === "ok") { + this.setState({editingIndex: "", oldPolicy: ""}); + Setting.showMessage("success", i18next.t("adapter:Update policy successfully")); + } else { + Setting.showMessage("error", i18next.t(`adapter:Update policy failed, ${res.msg}`)); + } + }); + } + + addPolicy(table, i) { + AdapterBackend.AddPolicy(this.props.owner, this.props.name, table[i]).then(res => { + if (res.status === "ok") { + this.setState({editingIndex: "", oldPolicy: "", add: false}); + if (res.data !== "Affected") { + Setting.showMessage("info", i18next.t("adapter:Repeated policy")); + } else { + Setting.showMessage("success", i18next.t("adapter:Add policy successfully")); + } + } else { + Setting.showMessage("error", i18next.t(`adapter:Add policy failed, ${res.msg}`)); + } + }); + } + + deletePolicy(table, i) { + AdapterBackend.RemovePolicy(this.props.owner, this.props.name, table[i]).then(res => { + if (res.status === "ok") { + table = Setting.deleteRow(table, i); + this.updateTable(table); + Setting.showMessage("success", i18next.t("adapter:Delete policy successfully")); + } else { + Setting.showMessage("error", i18next.t(`adapter:Delete policy failed, ${res.msg}`)); + } + }); + } + + renderTable(table) { + const columns = [ + { + title: "Rule Type", + dataIndex: "Ptype", + width: "100px", + // render: (text, record, index) => { + // const editing = this.isEditing(index); + // return ( + // editing ? + // { + // this.updateField(table, index, "Ptype", e.target.value); + // }} /> + // : text + // ); + // }, + }, + { + title: "V0", + dataIndex: "V0", + width: "100px", + render: (text, record, index) => { + const editing = this.isEditing(index); + return ( + editing ? + { + this.updateField(table, index, "V0", e.target.value); + }} /> + : text + ); + }, + }, + { + title: "V1", + dataIndex: "V1", + width: "100px", + render: (text, record, index) => { + const editing = this.isEditing(index); + return ( + editing ? + { + this.updateField(table, index, "V1", e.target.value); + }} /> + : text + ); + }, + }, + { + title: "V2", + dataIndex: "V2", + width: "100px", + render: (text, record, index) => { + const editing = this.isEditing(index); + return ( + editing ? + { + this.updateField(table, index, "V2", e.target.value); + }} /> + : text + ); + }, + }, + { + title: "V3", + dataIndex: "V3", + width: "100px", + render: (text, record, index) => { + const editing = this.isEditing(index); + return ( + editing ? + { + this.updateField(table, index, "V3", e.target.value); + }} /> + : text + ); + }, + }, + { + title: "V4", + dataIndex: "V4", + width: "100px", + render: (text, record, index) => { + const editing = this.isEditing(index); + return ( + editing ? + { + this.updateField(table, index, "V4", e.target.value); + }} /> + : text + ); + }, + }, + { + title: "V5", + dataIndex: "V5", + width: "100px", + render: (text, record, index) => { + const editing = this.isEditing(index); + return ( + editing ? + { + this.updateField(table, index, "V5", e.target.value); + }} /> + : text + ); + }, + }, + { + title: "Option", + key: "option", + width: "100px", + render: (text, record, index) => { + const editable = this.isEditing(index); + return editable ? ( + + + this.cancel(table, index)}> + Cancel + + + ) : ( +
+ +
+ ); + }, + }]; + + return ( +
( +
+ +
+ )} + /> + ); + } + + render() { + return (<> + + { + this.renderTable(this.state.policyLists) + } + + ); + } +} + +export default PolicyTable; diff --git a/web/src/locales/de/data.json b/web/src/locales/de/data.json index 07dbb2d0..b92b0437 100644 --- a/web/src/locales/de/data.json +++ b/web/src/locales/de/data.json @@ -6,11 +6,15 @@ "Sign Up": "Registrieren" }, "adapter": { + "Add policy successfully": "Add policy successfully", + "Delete policy successfully": "Delete policy successfully", "Edit Adapter": "Edit Adapter", "New Adapter": "New Adapter", "Policies": "Policies", "Policies - Tooltip": "Policies - Tooltip", - "Sync": "Sync" + "Repeated policy": "Repeated policy", + "Sync": "Sync", + "Update policy successfully": "Update policy successfully" }, "application": { "Always": "Always", @@ -555,6 +559,7 @@ "UserInfo URL": "UserInfo URL", "UserInfo URL - Tooltip": "UserInfo URL - Tooltip", "Visible": "Visible", + "admin (share)": "admin (share)", "alertType": "alarmtyp" }, "record": { diff --git a/web/src/locales/en/data.json b/web/src/locales/en/data.json index 83e0d770..b3572762 100644 --- a/web/src/locales/en/data.json +++ b/web/src/locales/en/data.json @@ -6,11 +6,15 @@ "Sign Up": "Sign Up" }, "adapter": { + "Add policy successfully": "Add policy successfully", + "Delete policy successfully": "Delete policy successfully", "Edit Adapter": "Edit Adapter", "New Adapter": "New Adapter", "Policies": "Policies", "Policies - Tooltip": "Policies - Tooltip", - "Sync": "Sync" + "Repeated policy": "Repeated policy", + "Sync": "Sync", + "Update policy successfully": "Update policy successfully" }, "application": { "Always": "Always", @@ -555,6 +559,7 @@ "UserInfo URL": "UserInfo URL", "UserInfo URL - Tooltip": "UserInfo URL - Tooltip", "Visible": "Visible", + "admin (share)": "admin (share)", "alertType": "alertType" }, "record": { diff --git a/web/src/locales/fr/data.json b/web/src/locales/fr/data.json index 43099446..b6cc6cb4 100644 --- a/web/src/locales/fr/data.json +++ b/web/src/locales/fr/data.json @@ -6,11 +6,15 @@ "Sign Up": "S'inscrire" }, "adapter": { + "Add policy successfully": "Add policy successfully", + "Delete policy successfully": "Delete policy successfully", "Edit Adapter": "Edit Adapter", "New Adapter": "New Adapter", "Policies": "Policies", "Policies - Tooltip": "Policies - Tooltip", - "Sync": "Sync" + "Repeated policy": "Repeated policy", + "Sync": "Sync", + "Update policy successfully": "Update policy successfully" }, "application": { "Always": "Always", @@ -555,6 +559,7 @@ "UserInfo URL": "UserInfo URL", "UserInfo URL - Tooltip": "UserInfo URL - Tooltip", "Visible": "Visible", + "admin (share)": "admin (share)", "alertType": "Type d'alerte" }, "record": { diff --git a/web/src/locales/ja/data.json b/web/src/locales/ja/data.json index 7147003f..4ef6eade 100644 --- a/web/src/locales/ja/data.json +++ b/web/src/locales/ja/data.json @@ -6,11 +6,15 @@ "Sign Up": "新規登録" }, "adapter": { + "Add policy successfully": "Add policy successfully", + "Delete policy successfully": "Delete policy successfully", "Edit Adapter": "Edit Adapter", "New Adapter": "New Adapter", "Policies": "Policies", "Policies - Tooltip": "Policies - Tooltip", - "Sync": "Sync" + "Repeated policy": "Repeated policy", + "Sync": "Sync", + "Update policy successfully": "Update policy successfully" }, "application": { "Always": "Always", @@ -555,6 +559,7 @@ "UserInfo URL": "UserInfo URL", "UserInfo URL - Tooltip": "UserInfo URL - Tooltip", "Visible": "Visible", + "admin (share)": "admin (share)", "alertType": "alertType" }, "record": { diff --git a/web/src/locales/ko/data.json b/web/src/locales/ko/data.json index a6804535..8f7f0752 100644 --- a/web/src/locales/ko/data.json +++ b/web/src/locales/ko/data.json @@ -6,11 +6,15 @@ "Sign Up": "Sign Up" }, "adapter": { + "Add policy successfully": "Add policy successfully", + "Delete policy successfully": "Delete policy successfully", "Edit Adapter": "Edit Adapter", "New Adapter": "New Adapter", "Policies": "Policies", "Policies - Tooltip": "Policies - Tooltip", - "Sync": "Sync" + "Repeated policy": "Repeated policy", + "Sync": "Sync", + "Update policy successfully": "Update policy successfully" }, "application": { "Always": "Always", @@ -555,6 +559,7 @@ "UserInfo URL": "UserInfo URL", "UserInfo URL - Tooltip": "UserInfo URL - Tooltip", "Visible": "Visible", + "admin (share)": "admin (share)", "alertType": "alertType" }, "record": { diff --git a/web/src/locales/ru/data.json b/web/src/locales/ru/data.json index fae7c09f..d57afed9 100644 --- a/web/src/locales/ru/data.json +++ b/web/src/locales/ru/data.json @@ -6,11 +6,15 @@ "Sign Up": "Регистрация" }, "adapter": { + "Add policy successfully": "Add policy successfully", + "Delete policy successfully": "Delete policy successfully", "Edit Adapter": "Edit Adapter", "New Adapter": "New Adapter", "Policies": "Policies", "Policies - Tooltip": "Policies - Tooltip", - "Sync": "Sync" + "Repeated policy": "Repeated policy", + "Sync": "Sync", + "Update policy successfully": "Update policy successfully" }, "application": { "Always": "Always", @@ -555,6 +559,7 @@ "UserInfo URL": "UserInfo URL", "UserInfo URL - Tooltip": "UserInfo URL - Tooltip", "Visible": "Visible", + "admin (share)": "admin (share)", "alertType": "тип оповещения" }, "record": { diff --git a/web/src/locales/zh/data.json b/web/src/locales/zh/data.json index db93a6cf..a57c6967 100644 --- a/web/src/locales/zh/data.json +++ b/web/src/locales/zh/data.json @@ -6,11 +6,15 @@ "Sign Up": "注册" }, "adapter": { + "Add policy successfully": "添加策略成功", + "Delete policy successfully": "删除策略成功", "Edit Adapter": "编辑适配器", "New Adapter": "添加适配器", "Policies": "策略", "Policies - Tooltip": "策略", - "Sync": "同步" + "Repeated policy": "策略重复", + "Sync": "同步", + "Update policy successfully": "更新策略成功" }, "application": { "Always": "始终开启", @@ -555,6 +559,7 @@ "UserInfo URL": "UserInfo URL", "UserInfo URL - Tooltip": "UserInfo URL - 工具提示", "Visible": "是否可见", + "admin (share)": "admin (share)", "alertType": "警报类型" }, "record": {