@@ -0,0 +1,108 @@ | |||
// Copyright 2023 The casbin 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 controllers | |||
import ( | |||
"encoding/json" | |||
"github.com/casbin/casibase/object" | |||
) | |||
func (c *ApiController) GetGlobalMessages() { | |||
messages, err := object.GetGlobalMessages() | |||
if err != nil { | |||
c.ResponseError(err.Error()) | |||
return | |||
} | |||
c.ResponseOk(messages) | |||
} | |||
func (c *ApiController) GetMessages() { | |||
owner := c.Input().Get("owner") | |||
messages, err := object.GetMessages(owner) | |||
if err != nil { | |||
c.ResponseError(err.Error()) | |||
return | |||
} | |||
c.ResponseOk(messages) | |||
} | |||
func (c *ApiController) GetMessage() { | |||
id := c.Input().Get("id") | |||
message, err := object.GetMessage(id) | |||
if err != nil { | |||
c.ResponseError(err.Error()) | |||
return | |||
} | |||
c.ResponseOk(message) | |||
} | |||
func (c *ApiController) UpdateMessage() { | |||
id := c.Input().Get("id") | |||
var message object.Message | |||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &message) | |||
if err != nil { | |||
c.ResponseError(err.Error()) | |||
return | |||
} | |||
success, err := object.UpdateMessage(id, &message) | |||
if err != nil { | |||
c.ResponseError(err.Error()) | |||
return | |||
} | |||
c.ResponseOk(success) | |||
} | |||
func (c *ApiController) AddMessage() { | |||
var message object.Message | |||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &message) | |||
if err != nil { | |||
c.ResponseError(err.Error()) | |||
return | |||
} | |||
success, err := object.AddMessage(&message) | |||
if err != nil { | |||
c.ResponseError(err.Error()) | |||
return | |||
} | |||
c.ResponseOk(success) | |||
} | |||
func (c *ApiController) DeleteMessage() { | |||
var message object.Message | |||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &message) | |||
if err != nil { | |||
c.ResponseError(err.Error()) | |||
return | |||
} | |||
success, err := object.DeleteMessage(&message) | |||
if err != nil { | |||
c.ResponseError(err.Error()) | |||
return | |||
} | |||
c.ResponseOk(success) | |||
} |
@@ -132,4 +132,9 @@ func (a *Adapter) createTable() { | |||
if err != nil { | |||
panic(err) | |||
} | |||
err = a.engine.Sync2(new(Message)) | |||
if err != nil { | |||
panic(err) | |||
} | |||
} |
@@ -0,0 +1,113 @@ | |||
// Copyright 2023 The casbin 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 object | |||
import ( | |||
"fmt" | |||
"github.com/casbin/casibase/util" | |||
"xorm.io/core" | |||
) | |||
type Message struct { | |||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"` | |||
Name string `xorm:"varchar(100) notnull pk" json:"name"` | |||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"` | |||
//Organization string `xorm:"varchar(100)" json:"organization"` | |||
Chat string `xorm:"varchar(100) index" json:"chat"` | |||
ReplyTo string `xorm:"varchar(100) index" json:"replyTo"` | |||
Author string `xorm:"varchar(100)" json:"author"` | |||
Text string `xorm:"mediumtext" json:"text"` | |||
} | |||
func GetGlobalMessages() ([]*Message, error) { | |||
messages := []*Message{} | |||
err := adapter.engine.Asc("owner").Desc("created_time").Find(&messages) | |||
if err != nil { | |||
return messages, err | |||
} | |||
return messages, nil | |||
} | |||
func GetMessages(owner string) ([]*Message, error) { | |||
messages := []*Message{} | |||
err := adapter.engine.Desc("created_time").Find(&messages, &Message{Owner: owner}) | |||
if err != nil { | |||
return messages, err | |||
} | |||
return messages, nil | |||
} | |||
func getMessage(owner, name string) (*Message, error) { | |||
message := Message{Owner: owner, Name: name} | |||
existed, err := adapter.engine.Get(&message) | |||
if err != nil { | |||
return &message, err | |||
} | |||
if existed { | |||
return &message, nil | |||
} else { | |||
return nil, nil | |||
} | |||
} | |||
func GetMessage(id string) (*Message, error) { | |||
owner, name := util.GetOwnerAndNameFromId(id) | |||
return getMessage(owner, name) | |||
} | |||
func UpdateMessage(id string, message *Message) (bool, error) { | |||
owner, name := util.GetOwnerAndNameFromId(id) | |||
_, err := getMessage(owner, name) | |||
if err != nil { | |||
return false, err | |||
} | |||
if message == nil { | |||
return false, nil | |||
} | |||
_, err = adapter.engine.ID(core.PK{owner, name}).AllCols().Update(message) | |||
if err != nil { | |||
return false, err | |||
} | |||
return true, nil | |||
} | |||
func AddMessage(message *Message) (bool, error) { | |||
affected, err := adapter.engine.Insert(message) | |||
if err != nil { | |||
return false, err | |||
} | |||
return affected != 0, nil | |||
} | |||
func DeleteMessage(message *Message) (bool, error) { | |||
affected, err := adapter.engine.ID(core.PK{message.Owner, message.Name}).Delete(&Message{}) | |||
if err != nil { | |||
return false, err | |||
} | |||
return affected != 0, nil | |||
} | |||
func (message *Message) GetId() string { | |||
return fmt.Sprintf("%s/%s", message.Owner, message.Name) | |||
} |
@@ -81,6 +81,13 @@ func initAPI() { | |||
beego.Router("/api/add-chat", &controllers.ApiController{}, "POST:AddChat") | |||
beego.Router("/api/delete-chat", &controllers.ApiController{}, "POST:DeleteChat") | |||
beego.Router("/api/get-global-messages", &controllers.ApiController{}, "GET:GetGlobalMessages") | |||
beego.Router("/api/get-messages", &controllers.ApiController{}, "GET:GetMessages") | |||
beego.Router("/api/get-message", &controllers.ApiController{}, "GET:GetMessage") | |||
beego.Router("/api/update-message", &controllers.ApiController{}, "POST:UpdateMessage") | |||
beego.Router("/api/add-message", &controllers.ApiController{}, "POST:AddMessage") | |||
beego.Router("/api/delete-message", &controllers.ApiController{}, "POST:DeleteMessage") | |||
beego.Router("/api/update-file", &controllers.ApiController{}, "POST:UpdateFile") | |||
beego.Router("/api/add-file", &controllers.ApiController{}, "POST:AddFile") | |||
beego.Router("/api/delete-file", &controllers.ApiController{}, "POST:DeleteFile") | |||
@@ -40,6 +40,8 @@ import i18next from "i18next"; | |||
import LanguageSelect from "./LanguageSelect"; | |||
import ChatEditPage from "./ChatEditPage"; | |||
import ChatListPage from "./ChatListPage"; | |||
import MessageListPage from "./MessageListPage"; | |||
import MessageEditPage from "./MessageEditPage"; | |||
const {Header, Footer} = Layout; | |||
@@ -92,6 +94,8 @@ class App extends Component { | |||
this.setState({selectedMenuKey: "/providers"}); | |||
} else if (uri.includes("/chats")) { | |||
this.setState({selectedMenuKey: "/chats"}); | |||
} else if (uri.includes("/messages")) { | |||
this.setState({selectedMenuKey: "/messages"}); | |||
} else { | |||
this.setState({selectedMenuKey: "null"}); | |||
} | |||
@@ -331,6 +335,13 @@ class App extends Component { | |||
</Link> | |||
</Menu.Item> | |||
); | |||
res.push( | |||
<Menu.Item key="/messages"> | |||
<Link to="/messages"> | |||
{i18next.t("general:Messages")} | |||
</Link> | |||
</Menu.Item> | |||
); | |||
res.push( | |||
<Menu.Item key="/permissions"> | |||
<a target="_blank" rel="noreferrer" href={Setting.getMyProfileUrl(this.state.account).replace("/account", "/permissions")}> | |||
@@ -418,6 +429,8 @@ class App extends Component { | |||
<Route exact path="/providers/:providerName" render={(props) => this.renderSigninIfNotSignedIn(<ProviderEditPage account={this.state.account} {...props} />)} /> | |||
<Route exact path="/chats" render={(props) => this.renderSigninIfNotSignedIn(<ChatListPage account={this.state.account} {...props} />)} /> | |||
<Route exact path="/chats/:chatName" render={(props) => this.renderSigninIfNotSignedIn(<ChatEditPage account={this.state.account} {...props} />)} /> | |||
<Route exact path="/messages" render={(props) => this.renderSigninIfNotSignedIn(<MessageListPage account={this.state.account} {...props} />)} /> | |||
<Route exact path="/messages/:messageName" render={(props) => this.renderSigninIfNotSignedIn(<MessageEditPage account={this.state.account} {...props} />)} /> | |||
</Switch> | |||
</div> | |||
); | |||
@@ -27,11 +27,13 @@ class ChatEditPage extends React.Component { | |||
classes: props, | |||
chatName: props.match.params.chatName, | |||
chat: null, | |||
// users: [], | |||
}; | |||
} | |||
UNSAFE_componentWillMount() { | |||
this.getChat(); | |||
// this.getUser(); | |||
} | |||
getChat() { | |||
@@ -0,0 +1,277 @@ | |||
// Copyright 2023 The casbin 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 {Button, Card, Col, Input, Row, Select} from "antd"; | |||
import * as Setting from "./Setting"; | |||
import i18next from "i18next"; | |||
import * as MessageBackend from "./backend/MessageBackend"; | |||
import TextArea from "antd/es/input/TextArea"; | |||
import * as ChatBackend from "./backend/ChatBackend"; | |||
class MessageEditPage extends React.Component { | |||
constructor(props) { | |||
super(props); | |||
this.state = { | |||
classes: props, | |||
messageName: props.match.params.messageName, | |||
messages: [], | |||
message: null, | |||
chats: [], | |||
// users: [], | |||
chat: null, | |||
}; | |||
} | |||
UNSAFE_componentWillMount() { | |||
this.getMessage(); | |||
this.getMessages(); | |||
this.getChats(); | |||
} | |||
getChats() { | |||
ChatBackend.getChats(this.props.account.name) | |||
.then((chats) => { | |||
if (chats.status === "ok") { | |||
this.setState({ | |||
chats: chats.data, | |||
}); | |||
} else { | |||
Setting.showMessage("error", `Failed to get chat: ${chats.msg}`); | |||
} | |||
}); | |||
} | |||
getChat(chatName) { | |||
ChatBackend.getChat(this.props.account.name, chatName) | |||
.then((chat) => { | |||
if (chat.status === "ok") { | |||
this.setState({ | |||
chat: chat.data, | |||
}); | |||
} else { | |||
Setting.showMessage("error", `Failed to get chat: ${chat.msg}`); | |||
} | |||
}); | |||
} | |||
getMessage() { | |||
MessageBackend.getMessage(this.props.account.name, this.state.messageName) | |||
.then((message) => { | |||
if (message.status === "ok") { | |||
this.setState({ | |||
message: message.data, | |||
}); | |||
} else { | |||
Setting.showMessage("error", `Failed to get message: ${message.msg}`); | |||
} | |||
}); | |||
} | |||
getMessages() { | |||
MessageBackend.getMessages(this.props.account.name) | |||
.then((messages) => { | |||
if (messages.status === "ok") { | |||
this.setState({ | |||
messages: messages.data, | |||
}); | |||
} else { | |||
Setting.showMessage("error", `Failed to get messages: ${messages.msg}`); | |||
} | |||
}); | |||
} | |||
parseMessageField(key, value) { | |||
if ([""].includes(key)) { | |||
value = Setting.myParseInt(value); | |||
} | |||
return value; | |||
} | |||
updateMessageField(key, value) { | |||
value = this.parseMessageField(key, value); | |||
const message = this.state.message; | |||
message[key] = value; | |||
this.setState({ | |||
message: message, | |||
}); | |||
} | |||
renderMessage() { | |||
return ( | |||
<Card size="small" title={ | |||
<div> | |||
{i18next.t("message:Edit Chat")} | |||
<Button type="primary" onClick={this.submitMessageEdit.bind(this)}>{i18next.t("general:Save")}</Button> | |||
</div> | |||
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner"> | |||
{/* <Row style={{marginTop: "10px"}} >*/} | |||
{/* <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>*/} | |||
{/* {Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :*/} | |||
{/* </Col>*/} | |||
{/* <Col span={22} >*/} | |||
{/* <Select virtual={false} disabled={!Setting.isAdminUser(this.props.account)} style={{width: "100%"}} value={this.state.chat.organization} onChange={(value => {this.updateChatField("organization", value);})}*/} | |||
{/* options={this.state.organizations.map((organization) => Setting.getOption(organization.name, organization.name))*/} | |||
{/* } />*/} | |||
{/* </Col>*/} | |||
{/* </Row>*/} | |||
<Row style={{marginTop: "10px"}}> | |||
<Col style={{marginTop: "5px"}} span={2}> | |||
{i18next.t("general:Name")}: | |||
</Col> | |||
<Col span={22}> | |||
<Input | |||
value={this.state.message.name} | |||
onChange={(e) => { | |||
this.updateMessageField("name", e.target.value); | |||
}} | |||
/> | |||
</Col> | |||
</Row> | |||
<Row style={{marginTop: "20px"}}> | |||
<Col style={{marginTop: "5px"}} span={2}> | |||
{i18next.t("message:Chat")}: | |||
</Col> | |||
<Col span={22}> | |||
<Select | |||
virtual={false} | |||
style={{width: "100%"}} | |||
value={this.state.message.chat} | |||
onChange={(value) => { | |||
this.updateMessageField("chat", value); | |||
this.getChat(value); | |||
}} | |||
options={this.state.chats.map((chat) => | |||
Setting.getOption(chat.name, chat.name) | |||
)} | |||
/> | |||
</Col> | |||
</Row> | |||
<Row style={{marginTop: "20px"}}> | |||
<Col style={{marginTop: "5px"}} span={2}> | |||
{i18next.t("message:Author")}: | |||
</Col> | |||
<Col span={22}> | |||
<Select | |||
virtual={false} | |||
style={{width: "100%"}} | |||
value={this.state.message.author} | |||
onChange={(value) => { | |||
this.updateMessageField("author", value); | |||
}} | |||
options={ | |||
this.state.chat !== null | |||
? this.state.chat.users.map((user) => | |||
Setting.getOption(`${user}`, `${user}`) | |||
) | |||
: [] | |||
} | |||
/> | |||
</Col> | |||
</Row> | |||
<Row style={{marginTop: "20px"}}> | |||
<Col style={{marginTop: "5px"}} span={2}> | |||
{i18next.t("message:replyTo")}: | |||
</Col> | |||
<Col span={22}> | |||
<Select | |||
virtual={false} | |||
style={{width: "100%"}} | |||
value={this.state.message.replyTo} | |||
onChange={(value) => { | |||
this.updateMessageField("replyTo", value); | |||
}} | |||
options={ | |||
this.state.messages !== null | |||
? this.state.messages.map((message) => | |||
Setting.getOption(`${message.name}`, `${message.name}`) | |||
) | |||
: [] | |||
} | |||
/> | |||
</Col> | |||
</Row> | |||
<Row style={{marginTop: "20px"}}> | |||
<Col style={{marginTop: "5px"}} span={2}> | |||
{i18next.t("message:Text")}: | |||
</Col> | |||
<Col span={22}> | |||
<TextArea | |||
rows={10} | |||
value={this.state.message.text} | |||
onChange={(e) => { | |||
this.updateMessageField("text", e.target.value); | |||
}} | |||
/> | |||
</Col> | |||
</Row> | |||
</Card> | |||
); | |||
} | |||
submitMessageEdit(exitAfterSave) { | |||
const message = Setting.deepCopy(this.state.message); | |||
MessageBackend.updateMessage(this.state.message.owner, this.state.messageName, message) | |||
.then((res) => { | |||
if (res.status === "ok") { | |||
if (res.data) { | |||
Setting.showMessage("success", "Successfully saved"); | |||
this.setState({ | |||
messageName: this.state.message.name, | |||
}); | |||
if (exitAfterSave) { | |||
this.props.history.push(`/messages/${this.state.message.name}`); | |||
} | |||
} else { | |||
Setting.showMessage("error", "failed to save: server side failure"); | |||
this.updateMessageField("name", this.state.messageName); | |||
} | |||
} else { | |||
Setting.showMessage("error", `failed to save: ${res.msg}`); | |||
} | |||
}) | |||
.catch((error) => { | |||
Setting.showMessage("error", `failed to save: ${error}`); | |||
}); | |||
} | |||
render() { | |||
return ( | |||
<div> | |||
<Row style={{width: "100%"}}> | |||
<Col span={1}></Col> | |||
<Col span={22}> | |||
{this.state.message !== null ? this.renderMessage() : null} | |||
</Col> | |||
<Col span={1}></Col> | |||
</Row> | |||
<Row style={{margin: 10}}> | |||
<Col span={2}></Col> | |||
<Col span={18}> | |||
<Button | |||
type="primary" | |||
size="large" | |||
onClick={this.submitMessageEdit.bind(this)} | |||
> | |||
{i18next.t("general:Save")} | |||
</Button> | |||
</Col> | |||
</Row> | |||
</div> | |||
); | |||
} | |||
} | |||
export default MessageEditPage; |
@@ -0,0 +1,246 @@ | |||
// Copyright 2023 The casbin 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 {Link} from "react-router-dom"; | |||
import {Button, Col, Popconfirm, Row, Table} from "antd"; | |||
import * as Setting from "./Setting"; | |||
import * as MessageBackend from "./backend/MessageBackend"; | |||
import moment from "moment"; | |||
import i18next from "i18next"; | |||
class MessageListPage extends React.Component { | |||
constructor(props) { | |||
super(props); | |||
this.state = { | |||
classes: props, | |||
messages: null, | |||
}; | |||
} | |||
UNSAFE_componentWillMount() { | |||
this.getMessages(); | |||
} | |||
getMessages() { | |||
MessageBackend.getMessages(this.props.account.name) | |||
.then((res) => { | |||
if (res.status === "ok") { | |||
this.setState({ | |||
messages: res.data, | |||
}); | |||
} else { | |||
Setting.showMessage("error", `Failed to get messages: ${res.msg}`); | |||
} | |||
}); | |||
} | |||
newMessage() { | |||
const randomName = Setting.getRandomName(); | |||
return { | |||
owner: this.props.account.name, | |||
name: `message_${randomName}`, | |||
createdTime: moment().format(), | |||
// organization: "Message Organization - 1", | |||
chat: "", | |||
replyTo: "", | |||
author: `${this.props.account.owner}/${this.props.account.name}`, | |||
text: "", | |||
}; | |||
} | |||
addMessage() { | |||
const newMessage = this.newMessage(); | |||
MessageBackend.addMessage(newMessage) | |||
.then((res) => { | |||
if (res.status === "ok") { | |||
Setting.showMessage("success", "Message added successfully"); | |||
this.setState({ | |||
messages: Setting.prependRow(this.state.messages, newMessage), | |||
}); | |||
} else { | |||
Setting.showMessage("error", `Failed to add Message: ${res.msg}`); | |||
} | |||
}) | |||
.catch(error => { | |||
Setting.showMessage("error", `Message failed to add: ${error}`); | |||
}); | |||
} | |||
deleteMessage(i) { | |||
MessageBackend.deleteMessage(this.state.messages[i]) | |||
.then((res) => { | |||
if (res.status === "ok") { | |||
Setting.showMessage("success", "Message deleted successfully"); | |||
this.setState({ | |||
messages: Setting.deleteRow(this.state.messages, i), | |||
}); | |||
} else { | |||
Setting.showMessage("error", `Failed to delete Message: ${res.msg}`); | |||
} | |||
}) | |||
.catch(error => { | |||
Setting.showMessage("error", `Message failed to delete: ${error}`); | |||
}); | |||
} | |||
renderTable(messages) { | |||
const columns = [ | |||
{ | |||
title: i18next.t("general:Name"), | |||
dataIndex: "name", | |||
key: "name", | |||
width: "140px", | |||
sorter: (a, b) => a.name.localeCompare(b.name), | |||
render: (text, record, index) => { | |||
return ( | |||
<Link to={`/messages/${text}`}> | |||
{text} | |||
</Link> | |||
); | |||
}, | |||
}, | |||
{ | |||
title: i18next.t("general:Created time"), | |||
dataIndex: "createdTime", | |||
key: "createdTime", | |||
width: "150px", | |||
sorter: true, | |||
render: (text, record, index) => { | |||
return Setting.getFormattedDate(text); | |||
}, | |||
}, | |||
{ | |||
title: i18next.t("message:Chat"), | |||
dataIndex: "chat", | |||
key: "chat", | |||
width: "150px", | |||
sorter: (a, b) => a.chat.localeCompare(b.chat), | |||
render: (text, record, index) => { | |||
return ( | |||
<Link to={`/chat/${text}`}> | |||
{text} | |||
</Link> | |||
); | |||
}, | |||
}, | |||
{ | |||
title: i18next.t("message:Reply to"), | |||
dataIndex: "replyTo", | |||
key: "replyTo", | |||
width: "150px", | |||
sorter: (a, b) => a.replyTo.localeCompare(b.replyTo), | |||
render: (text, record, index) => { | |||
return ( | |||
<Link to={`/message/${text}`}> | |||
{text} | |||
</Link> | |||
); | |||
}, | |||
}, | |||
{ | |||
title: i18next.t("message:Author"), | |||
dataIndex: "author", | |||
key: "author", | |||
width: "150px", | |||
sorter: (a, b) => a.author.localeCompare(b.author), | |||
render: (text, record, index) => { | |||
return ( | |||
<Link to={`/member/${text}`}> | |||
{text} | |||
</Link> | |||
); | |||
}, | |||
}, | |||
{ | |||
title: i18next.t("message:Text"), | |||
dataIndex: "text", | |||
key: "text", | |||
width: "200px", | |||
sorter: (a, b) => a.text.localeCompare(b.text), | |||
}, | |||
{ | |||
title: i18next.t("general:Action"), | |||
dataIndex: "action", | |||
key: "action", | |||
width: "130px", | |||
render: (text, record, index) => { | |||
return ( | |||
<div> | |||
<Button | |||
style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} | |||
type="primary" | |||
onClick={() => this.props.history.push(`/messages/${record.name}`)} | |||
> | |||
{i18next.t("general:Edit")} | |||
</Button> | |||
<Popconfirm | |||
title={`Sure to delete message: ${record.name} ?`} | |||
onConfirm={() => this.deleteMessage(index)} | |||
okText={i18next.t("general:OK")} | |||
cancelText={i18next.t("general:Cancel")} | |||
> | |||
<Button style={{marginBottom: "10px"}} type="danger"> | |||
{i18next.t("general:Delete")} | |||
</Button> | |||
</Popconfirm> | |||
</div> | |||
); | |||
}, | |||
}, | |||
]; | |||
return ( | |||
<div> | |||
<Table | |||
columns={columns} | |||
dataSource={messages} | |||
rowKey="name" | |||
size="middle" | |||
bordered | |||
pagination={{pageSize: 100}} | |||
title={() => ( | |||
<div> | |||
{i18next.t("message:Messages")} | |||
<Button type="primary" size="small" onClick={this.addMessage.bind(this)}> | |||
{i18next.t("general:Add")} | |||
</Button> | |||
</div> | |||
)} | |||
loading={messages === null} | |||
/> | |||
</div> | |||
); | |||
} | |||
render() { | |||
return ( | |||
<div> | |||
<Row style={{width: "100%"}}> | |||
<Col span={1}> | |||
</Col> | |||
<Col span={22}> | |||
{ | |||
this.renderTable(this.state.messages) | |||
} | |||
</Col> | |||
<Col span={1}> | |||
</Col> | |||
</Row> | |||
</div> | |||
); | |||
} | |||
} | |||
export default MessageListPage; |
@@ -0,0 +1,63 @@ | |||
// Copyright 2023 The casbin 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 * as Setting from "../Setting"; | |||
export function getGlobalMessages() { | |||
return fetch(`${Setting.ServerUrl}/api/get-global-messages`, { | |||
method: "GET", | |||
credentials: "include", | |||
}).then(res => res.json()); | |||
} | |||
export function getMessages(owner) { | |||
return fetch(`${Setting.ServerUrl}/api/get-messages?owner=${owner}`, { | |||
method: "GET", | |||
credentials: "include", | |||
}).then(res => res.json()); | |||
} | |||
export function getMessage(owner, name) { | |||
return fetch(`${Setting.ServerUrl}/api/get-message?id=${owner}/${encodeURIComponent(name)}`, { | |||
method: "GET", | |||
credentials: "include", | |||
}).then(res => res.json()); | |||
} | |||
export function updateMessage(owner, name, message) { | |||
const newMessage = Setting.deepCopy(message); | |||
return fetch(`${Setting.ServerUrl}/api/update-message?id=${owner}/${encodeURIComponent(name)}`, { | |||
method: "POST", | |||
credentials: "include", | |||
body: JSON.stringify(newMessage), | |||
}).then(res => res.json()); | |||
} | |||
export function addMessage(message) { | |||
const newMessage = Setting.deepCopy(message); | |||
return fetch(`${Setting.ServerUrl}/api/add-message`, { | |||
method: "POST", | |||
credentials: "include", | |||
body: JSON.stringify(newMessage), | |||
}).then(res => res.json()); | |||
} | |||
export function deleteMessage(message) { | |||
const newMessage = Setting.deepCopy(message); | |||
return fetch(`${Setting.ServerUrl}/api/delete-message`, { | |||
method: "POST", | |||
credentials: "include", | |||
body: JSON.stringify(newMessage), | |||
}).then(res => res.json()); | |||
} |