mirror of https://github.com/casbin/casnode.git
Merge pull request #72 from kocoler/AddPlaneManagement
Add plane management
This commit is contained in:
commit
642d330e1f
|
@ -75,6 +75,12 @@ func (c *APIController) forbiddenAccountResp(memberId string) {
|
|||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *APIController) RequireAdmin(memberId string) {
|
||||
resp := Response{Status: "error", Msg: "Unauthorized.", Data: memberId}
|
||||
c.Data["json"] = resp
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *APIController) GetCommunityHealth() {
|
||||
var resp Response
|
||||
|
||||
|
@ -106,7 +112,7 @@ var GlobalSessions *session.Manager
|
|||
func InitBeegoSession() {
|
||||
sessionConfig := &session.ManagerConfig{
|
||||
ProviderConfig: "./tmp",
|
||||
Gclifetime: 3600 * 24 * 365,
|
||||
Gclifetime: 3600 * 24 * 365,
|
||||
}
|
||||
GlobalSessions, _ = session.NewManager("file", sessionConfig)
|
||||
go GlobalSessions.GC()
|
||||
|
|
|
@ -56,9 +56,7 @@ func (c *APIController) UpdateNode() {
|
|||
var node object.Node
|
||||
|
||||
if !object.CheckModIdentity(c.GetSessionUser()) {
|
||||
resp = Response{Status: "fail", Msg: "Unauthorized."}
|
||||
c.Data["json"] = resp
|
||||
c.ServeJSON()
|
||||
c.RequireAdmin(c.GetSessionUser())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -77,6 +75,11 @@ func (c *APIController) AddNode() {
|
|||
var node object.Node
|
||||
var resp Response
|
||||
|
||||
if !object.CheckModIdentity(c.GetSessionUser()) {
|
||||
c.RequireAdmin(c.GetSessionUser())
|
||||
return
|
||||
}
|
||||
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &node)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -107,6 +110,11 @@ func (c *APIController) AddNode() {
|
|||
func (c *APIController) DeleteNode() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
if !object.CheckModIdentity(c.GetSessionUser()) {
|
||||
c.RequireAdmin(c.GetSessionUser())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = object.DeleteNode(id)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"encoding/json"
|
||||
|
||||
"github.com/casbin/casbin-forum/object"
|
||||
"github.com/casbin/casbin-forum/util"
|
||||
)
|
||||
|
||||
func (c *APIController) GetPlanes() {
|
||||
|
@ -37,6 +38,13 @@ func (c *APIController) GetPlane() {
|
|||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *APIController) GetPlaneAdmin() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
c.Data["json"] = object.GetPlaneAdmin(id)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *APIController) GetPlaneList() {
|
||||
var resp Response
|
||||
|
||||
|
@ -47,21 +55,90 @@ func (c *APIController) GetPlaneList() {
|
|||
}
|
||||
|
||||
func (c *APIController) AddPlane() {
|
||||
var plane object.Plane
|
||||
var plane object.AdminPlaneInfo
|
||||
var resp Response
|
||||
|
||||
if !object.CheckModIdentity(c.GetSessionUser()) {
|
||||
c.RequireAdmin(c.GetSessionUser())
|
||||
return
|
||||
}
|
||||
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &plane)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = object.AddPlane(&plane)
|
||||
if plane.Id == "" || plane.Name == "" {
|
||||
resp = Response{Status: "fail", Msg: "Some information is missing"}
|
||||
c.Data["json"] = resp
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
if object.HasPlane(plane.Id) {
|
||||
resp = Response{Status: "fail", Msg: "Plane ID existed"}
|
||||
c.Data["json"] = resp
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
newPlane := object.Plane{
|
||||
Id: plane.Id,
|
||||
Name: plane.Name,
|
||||
Sorter: plane.Sorter,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Image: plane.Image,
|
||||
BackgroundColor: plane.BackgroundColor,
|
||||
Color: plane.Color,
|
||||
Visible: plane.Visible,
|
||||
}
|
||||
res := object.AddPlane(&newPlane)
|
||||
resp = Response{Status: "ok", Msg: "success", Data: res}
|
||||
|
||||
c.Data["json"] = resp
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *APIController) UpdatePlaneInfo() {
|
||||
var info updatePlaneInfo
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &info)
|
||||
func (c *APIController) UpdatePlane() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var resp Response
|
||||
var plane object.AdminPlaneInfo
|
||||
|
||||
if !object.CheckModIdentity(c.GetSessionUser()) {
|
||||
c.RequireAdmin(c.GetSessionUser())
|
||||
return
|
||||
}
|
||||
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &plane)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
newPlane := object.Plane{
|
||||
Id: plane.Id,
|
||||
Name: plane.Name,
|
||||
Sorter: plane.Sorter,
|
||||
CreatedTime: plane.CreatedTime,
|
||||
Image: plane.Image,
|
||||
BackgroundColor: plane.BackgroundColor,
|
||||
Color: plane.Color,
|
||||
Visible: plane.Visible,
|
||||
}
|
||||
res := object.UpdatePlane(id, &newPlane)
|
||||
resp = Response{Status: "ok", Msg: "success", Data: res}
|
||||
|
||||
c.Data["json"] = object.UpdatePlaneInfo(info.Id, info.Field, info.Value)
|
||||
c.Data["json"] = resp
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *APIController) DeletePlane() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
if !object.CheckModIdentity(c.GetSessionUser()) {
|
||||
c.RequireAdmin(c.GetSessionUser())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = Response{Status: "ok", Msg: "success", Data: object.DeletePlane(id)}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
|
|
@ -55,15 +55,17 @@ func (c *APIController) GetTopic() {
|
|||
id := util.ParseInt(idStr)
|
||||
|
||||
topic := object.GetTopicWithAvatar(id, memberId)
|
||||
if topic == nil || topic.Deleted {
|
||||
c.Data["json"] = nil
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
if memberId != "" {
|
||||
topic.NodeModerator = object.CheckNodeModerator(memberId, topic.NodeId)
|
||||
}
|
||||
if topic.Deleted {
|
||||
c.Data["json"] = nil
|
||||
} else {
|
||||
c.Data["json"] = topic
|
||||
}
|
||||
|
||||
c.Data["json"] = topic
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
|
|
|
@ -138,6 +138,12 @@ func HasTab(id string) bool {
|
|||
return tab != nil
|
||||
}
|
||||
|
||||
func HasPlane(id string) bool {
|
||||
plane := GetPlane(id)
|
||||
|
||||
return plane != nil
|
||||
}
|
||||
|
||||
// IsMuted check member whether is muted.
|
||||
func IsMuted(id string) bool {
|
||||
status := GetMemberStatus(id)
|
||||
|
|
|
@ -18,6 +18,7 @@ type Plane struct {
|
|||
Id string `xorm:"varchar(50) notnull pk" json:"id"`
|
||||
Name string `xorm:"varchar(50)" json:"name"`
|
||||
Sorter int `xorm:"int" json:"-"`
|
||||
CreatedTime string `xorm:"varchar(40)" json:"createdTime"`
|
||||
Image string `xorm:"varchar(200)" json:"image"`
|
||||
BackgroundColor string `xorm:"varchar(20)" json:"backgroundColor"`
|
||||
Color string `xorm:"varchar(20)" json:"color"`
|
||||
|
@ -34,14 +35,24 @@ func GetPlanes() []*Plane {
|
|||
return planes
|
||||
}
|
||||
|
||||
func GetAllPlanes() []*Plane {
|
||||
func GetAllPlanes() []*AdminPlaneInfo {
|
||||
planes := []*Plane{}
|
||||
err := adapter.engine.Asc("sorter").Find(&planes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return planes
|
||||
var res []*AdminPlaneInfo
|
||||
for _, v := range planes {
|
||||
temp := AdminPlaneInfo{
|
||||
Plane: *v,
|
||||
Sorter: v.Sorter,
|
||||
Visible: v.Visible,
|
||||
NodesNum: GetPlaneNodesNum(v.Id),
|
||||
}
|
||||
res = append(res, &temp)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func GetPlane(id string) *Plane {
|
||||
|
@ -58,6 +69,28 @@ func GetPlane(id string) *Plane {
|
|||
}
|
||||
}
|
||||
|
||||
func GetPlaneAdmin(id string) *AdminPlaneInfo {
|
||||
plane := Plane{Id: id}
|
||||
existed, err := adapter.engine.Get(&plane)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
res := AdminPlaneInfo{
|
||||
Plane: plane,
|
||||
Sorter: plane.Sorter,
|
||||
Visible: plane.Visible,
|
||||
NodesNum: GetPlaneNodesNum(plane.Id),
|
||||
Nodes: GetNodeFromPlane(plane.Id),
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &res
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func AddPlane(plane *Plane) bool {
|
||||
affected, err := adapter.engine.Insert(plane)
|
||||
if err != nil {
|
||||
|
@ -67,8 +100,12 @@ func AddPlane(plane *Plane) bool {
|
|||
return affected != 0
|
||||
}
|
||||
|
||||
func UpdatePlaneInfo(id, field, value string) bool {
|
||||
affected, err := adapter.engine.Table(new(Plane)).ID(id).Update(map[string]interface{}{field: value})
|
||||
func UpdatePlane(id string, plane *Plane) bool {
|
||||
if GetPlane(id) == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
affected, err := adapter.engine.Id(id).AllCols().Update(plane)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -90,3 +127,22 @@ func GetPlaneList() []*PlaneWithNodes {
|
|||
|
||||
return res
|
||||
}
|
||||
|
||||
func DeletePlane(id string) bool {
|
||||
affected, err := adapter.engine.Id(id).Delete(&Plane{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func GetPlaneNodesNum(id string) int {
|
||||
node := new(Node)
|
||||
total, err := adapter.engine.Where("plane_id = ?", id).Count(node)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return int(total)
|
||||
}
|
||||
|
|
|
@ -88,6 +88,10 @@ func GetTopicWithAvatar(id int, memberId string) *TopicWithAvatar {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
if !existed {
|
||||
return nil
|
||||
}
|
||||
|
||||
res := TopicWithAvatar{
|
||||
Topic: topic,
|
||||
Avatar: GetMemberAvatar(topic.Author),
|
||||
|
@ -95,11 +99,7 @@ func GetTopicWithAvatar(id int, memberId string) *TopicWithAvatar {
|
|||
Editable: GetTopicEditableStatus(memberId, topic.Author, topic.NodeId, topic.CreatedTime),
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &res
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return &res
|
||||
}
|
||||
|
||||
func GetTopic(id int) *Topic {
|
||||
|
|
|
@ -115,3 +115,11 @@ type AdminMemberInfo struct {
|
|||
ReplyNum int `json:"replyNum"`
|
||||
LatestLogin string `json:"latestLogin"`
|
||||
}
|
||||
|
||||
type AdminPlaneInfo struct {
|
||||
Plane
|
||||
Sorter int `json:"sorter"`
|
||||
Visible bool `json:"visible"`
|
||||
NodesNum int `json:"nodesNum"`
|
||||
Nodes []*Node `json:"nodes"`
|
||||
}
|
||||
|
|
|
@ -76,9 +76,9 @@ func initAPI() {
|
|||
|
||||
beego.Router("/api/get-nodes", &controllers.APIController{}, "GET:GetNodes")
|
||||
beego.Router("/api/get-node", &controllers.APIController{}, "GET:GetNode")
|
||||
beego.Router("/api/update-node", &controllers.APIController{}, "POST:UpdateNode")
|
||||
beego.Router("/api/add-node", &controllers.APIController{}, "POST:AddNode")
|
||||
beego.Router("/api/delete-node", &controllers.APIController{}, "POST:DeleteNode")
|
||||
beego.Router("/api/update-node", &controllers.APIController{}, "POST:UpdateNode") // Update node api just for admin.
|
||||
beego.Router("/api/add-node", &controllers.APIController{}, "POST:AddNode") // Add node api just for admin.
|
||||
beego.Router("/api/delete-node", &controllers.APIController{}, "POST:DeleteNode") // Delete node api just for admin.
|
||||
beego.Router("/api/get-node-info", &controllers.APIController{}, "GET:GetNodeInfo")
|
||||
beego.Router("/api/get-node-relation", &controllers.APIController{}, "GET:GetNodeRelation")
|
||||
beego.Router("/api/get-nodes-num", &controllers.APIController{}, "GET:GetNodesNum")
|
||||
|
@ -109,9 +109,9 @@ func initAPI() {
|
|||
beego.Router("/api/get-tab-with-nodes", &controllers.APIController{}, "GET:GetTabWithNodes")
|
||||
beego.Router("/api/get-tabs-admin", &controllers.APIController{}, "GET:GetAllTabsAdmin")
|
||||
beego.Router("/api/get-tab-admin", &controllers.APIController{}, "GET:GetTabAdmin")
|
||||
beego.Router("/api/add-tab", &controllers.APIController{}, "POST:AddTab")
|
||||
beego.Router("/api/update-tab", &controllers.APIController{}, "POST:UpdateTab")
|
||||
beego.Router("/api/delete-tab", &controllers.APIController{}, "POST:DeleteTab")
|
||||
beego.Router("/api/add-tab", &controllers.APIController{}, "POST:AddTab") // Add tab api just for admin.
|
||||
beego.Router("/api/update-tab", &controllers.APIController{}, "POST:UpdateTab") // Update tab api just for admin.
|
||||
beego.Router("/api/delete-tab", &controllers.APIController{}, "POST:DeleteTab") // Delete tab api just for admin.
|
||||
|
||||
beego.Router("/api/get-notifications", &controllers.APIController{}, "GET:GetNotifications")
|
||||
beego.Router("/api/delete-notifications", &controllers.APIController{}, "POST:DeleteNotification")
|
||||
|
@ -119,11 +119,13 @@ func initAPI() {
|
|||
beego.Router("/api/update-read-status", &controllers.APIController{}, "POST:UpdateReadStatus")
|
||||
|
||||
beego.Router("/api/get-plane", &controllers.APIController{}, "GET:GetPlane")
|
||||
beego.Router("/api/get-planes", &controllers.APIController{}, "GET:GetPlanes")
|
||||
beego.Router("/api/add-plane", &controllers.APIController{}, "POST:AddPlane")
|
||||
beego.Router("/api/get-plane-admin", &controllers.APIController{}, "GET:GetPlaneAdmin")
|
||||
//beego.Router("/api/get-planes", &controllers.APIController{}, "GET:GetPlanes")
|
||||
beego.Router("/api/add-plane", &controllers.APIController{}, "POST:AddPlane") // Add plane api just for admin.
|
||||
beego.Router("/api/get-plane-list", &controllers.APIController{}, "GET:GetPlaneList")
|
||||
beego.Router("/api/update-plane-info", &controllers.APIController{}, "POST:UpdatePlaneInfo")
|
||||
beego.Router("/api/get-planes-admin", &controllers.APIController{}, "GET:GetPlanes")
|
||||
beego.Router("/api/update-plane", &controllers.APIController{}, "POST:UpdatePlane") // Update plane api just for admin.
|
||||
beego.Router("/api/get-planes-admin", &controllers.APIController{}, "GET:GetPlanesAdmin")
|
||||
beego.Router("/api/delete-plane", &controllers.APIController{}, "POST:DeletePlane") // Delete plane api just for admin.
|
||||
|
||||
beego.Router("/api/get-checkin-bonus-status", &controllers.APIController{}, "GET:GetCheckinBonusStatus")
|
||||
beego.Router("/api/get-checkin-bonus", &controllers.APIController{}, "GET:GetCheckinBonus")
|
||||
|
|
|
@ -65,6 +65,7 @@ import AdminNode from "./admin/AdminNode";
|
|||
import AdminTab from "./admin/AdminTab";
|
||||
import AdminMember from "./admin/AdminMember";
|
||||
import i18next from "i18next";
|
||||
import AdminPlane from "./admin/AdminPlane";
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
|
@ -374,6 +375,24 @@ class App extends Component {
|
|||
<AdminMember account={this.state.account} />
|
||||
</div>
|
||||
</Route>
|
||||
<Route exact path="/admin/plane">
|
||||
<div id={pcBrowser ? "Main" : ""}>
|
||||
{pcBrowser ? <div className="sep20" /> : null}
|
||||
<AdminPlane account={this.state.account} />
|
||||
</div>
|
||||
</Route>
|
||||
<Route exact path="/admin/plane/new">
|
||||
<div id={pcBrowser ? "Main" : ""}>
|
||||
{pcBrowser ? <div className="sep20" /> : null}
|
||||
<AdminPlane account={this.state.account} event={"new"} />
|
||||
</div>
|
||||
</Route>
|
||||
<Route exact path="/admin/plane/edit/:planeId">
|
||||
<div id={pcBrowser ? "Main" : ""}>
|
||||
{pcBrowser ? <div className="sep20" /> : null}
|
||||
<AdminPlane account={this.state.account} />
|
||||
</div>
|
||||
</Route>
|
||||
</Switch>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ class AdminHomepage extends React.Component {
|
|||
manageItems: [
|
||||
{label: i18next.t("admin:Tab management" ), value: "tab", image: Setting.getStatic("/static/img/settings.png")},
|
||||
{label: i18next.t("admin:Node management"), value: "node", image: Setting.getStatic("/static/img/settings.png")},
|
||||
{label: i18next.t("admin:Plane management" ), value: "plane", image: Setting.getStatic("/static/img/settings.png")},
|
||||
{label: i18next.t("admin:Topic management"), value: "topic", image: Setting.getStatic("/static/img/settings.png")},
|
||||
{label: i18next.t("admin:Member management" ), value: "member", image: Setting.getStatic("/static/img/settings.png")},
|
||||
],
|
||||
|
|
|
@ -305,10 +305,12 @@ class AdminMember extends React.Component {
|
|||
return (
|
||||
<div className="box">
|
||||
<div className="header"><a href="/">{Setting.getForumName()}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<a href={`/admin`}>{i18next.t("admin:Backstage management")}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<a href={`/admin/member`}>{i18next.t("member:Member management")}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<span className="gray">
|
||||
<span>
|
||||
{this.state.memberId}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -724,6 +726,8 @@ class AdminMember extends React.Component {
|
|||
<div className="box">
|
||||
<div className="header">
|
||||
<a href="/">{Setting.getForumName()}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<a href={`/admin`}>{i18next.t("admin:Backstage management")}</a>
|
||||
<span className="chevron"> › </span>{" "}{i18next.t("member:Member management")}
|
||||
<div className="fr f12">
|
||||
<span className="snow">{i18next.t("member:Total members")}{" "} </span>
|
||||
|
|
|
@ -326,12 +326,14 @@ class AdminNode extends React.Component {
|
|||
return (
|
||||
<div className="box">
|
||||
<div className="header"><a href="/">{Setting.getForumName()}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<a href={`/admin`}>{i18next.t("admin:Backstage management")}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<a href={`/admin/node`}>{i18next.t("node:Node management")}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
{
|
||||
this.props.event === "new" ?
|
||||
<span className="gray">
|
||||
<span>
|
||||
{i18next.t("node:New node")}
|
||||
</span> :
|
||||
<a href={`/go/${this.state.nodeId}`}>{this.state.nodeInfo?.name}</a>
|
||||
|
@ -853,6 +855,8 @@ class AdminNode extends React.Component {
|
|||
<div className="box">
|
||||
<div className="header">
|
||||
<a href="/">{Setting.getForumName()}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<a href={`/admin`}>{i18next.t("admin:Backstage management")}</a>
|
||||
<span className="chevron"> › </span>{" "}{i18next.t("node:Node management")}
|
||||
<div className="fr f12">
|
||||
<span className="snow">{i18next.t("node:Total nodes")}{" "} </span>
|
||||
|
|
|
@ -0,0 +1,717 @@
|
|||
// Copyright 2020 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 {withRouter} from "react-router-dom";
|
||||
import * as PlaneBackend from "../backend/PlaneBackend.js";
|
||||
import * as Setting from "../Setting";
|
||||
import Zmage from "react-zmage";
|
||||
import {SketchPicker} from "react-color";
|
||||
import i18next from "i18next";
|
||||
|
||||
class AdminPlane extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
nodes: [],
|
||||
planes: [],
|
||||
message: "",
|
||||
errorMessage: "",
|
||||
form: {},
|
||||
plane: [],
|
||||
//event: props.match.params.event,
|
||||
planeId: props.match.params.planeId,
|
||||
width: "",
|
||||
event: "basic",
|
||||
Management_LIST: [
|
||||
{label: "Basic Info", value: "basic"},
|
||||
{label: "Display", value: "display"},
|
||||
],
|
||||
color: "",
|
||||
backgroundColor: "",
|
||||
displayColorPicker: false,
|
||||
displayBackgroundColorPicker: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getPlane();
|
||||
this.getPlanes();
|
||||
}
|
||||
|
||||
getPlanes() {
|
||||
PlaneBackend.getPlanesAdmin()
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
planes: res,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getPlane() {
|
||||
if (this.state.planeId === undefined && this.props.event !== "new") {
|
||||
return;
|
||||
}
|
||||
|
||||
PlaneBackend.getPlaneAdmin(this.state.planeId)
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
plane: res,
|
||||
color: res?.color,
|
||||
backgroundColor: res?.backgroundColor,
|
||||
}, () => {
|
||||
this.initForm();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
initForm() {
|
||||
let form = this.state.form;
|
||||
if (this.props.event === "new") {
|
||||
form["sorter"] = 1;
|
||||
form["visible"] = true;
|
||||
} else {
|
||||
form["id"] = this.state.plane?.id;
|
||||
form["name"] = this.state.plane?.name;
|
||||
form["createdTime"] = this.state.plane?.createdTime;
|
||||
form["sorter"] = this.state.plane?.sorter;
|
||||
form["image"] = this.state.plane?.image;
|
||||
form["backgroundColor"] = this.state.plane?.backgroundColor;
|
||||
form["color"] = this.state.plane?.color;
|
||||
form["visible"] = this.state.plane?.visible;
|
||||
}
|
||||
this.setState({
|
||||
form: form,
|
||||
});
|
||||
}
|
||||
|
||||
updateFormField(key, value) {
|
||||
let form = this.state.form;
|
||||
form[key] = value;
|
||||
this.setState({
|
||||
form: form,
|
||||
});
|
||||
}
|
||||
|
||||
deletePlane(plane, planeId, nodesNum) {
|
||||
if (window.confirm(`${i18next.t(`plane:Are you sure to delete plane`)} ${plane} ?`)) {
|
||||
if (nodesNum !== 0) {
|
||||
alert(`
|
||||
${i18next.t("plane:Please delete all the nodes or move to another plane before deleting the plane")}
|
||||
${i18next.t("plane:Currently the number of nodes under the plane is")} ${nodesNum}
|
||||
`);
|
||||
} else {
|
||||
PlaneBackend.deletePlane(planeId)
|
||||
.then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
this.setState({
|
||||
message: `${i18next.t("plane:Delete plane")} ${plane} ${i18next.t("plane:success")}`,
|
||||
});
|
||||
this.getPlanes();
|
||||
} else {
|
||||
this.setState({
|
||||
errorMessage: res?.msg,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updatePlaneInfo() {
|
||||
PlaneBackend.updatePlane(this.state.planeId, this.state.form)
|
||||
.then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
this.getPlane();
|
||||
this.setState({
|
||||
message: i18next.t("plane:Update plane information success"),
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
message: res?.msg,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
postNewPlane() {
|
||||
if (this.state.form.id === undefined || this.state.form.id === "") {
|
||||
this.setState({
|
||||
errorMessage: "Please input plane ID",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.state.form.name === "" || this.state.form.name === undefined) {
|
||||
this.setState({
|
||||
errorMessage: "Please input plane name",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
PlaneBackend.addPlane(this.state.form)
|
||||
.then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
this.getPlane();
|
||||
this.setState({
|
||||
errorMessage: "",
|
||||
message: i18next.t("plane:Creat plane success"),
|
||||
}, () => {
|
||||
setTimeout(() => Setting.goToLink(`/admin/plane/edit/${this.state.form.id}`), 1600);
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
errorMessage: res?.msg,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
clearMessage() {
|
||||
this.setState({
|
||||
message: "",
|
||||
});
|
||||
}
|
||||
|
||||
clearErrorMessage() {
|
||||
this.setState({
|
||||
errorMessage: "",
|
||||
});
|
||||
}
|
||||
|
||||
handleColorChange = (color, event) => {
|
||||
if (event === "color") {
|
||||
this.updateFormField("color", color.hex);
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateFormField("backgroundColor", color.hex);
|
||||
};
|
||||
|
||||
handleColorClick = (event) => {
|
||||
if (event === "color") {
|
||||
this.setState({
|
||||
displayColorPicker: !this.state.displayColorPicker,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
displayBackgroundColorPicker: !this.state.displayBackgroundColorPicker,
|
||||
});
|
||||
};
|
||||
|
||||
handleColorClose = (event) => {
|
||||
if (event === "color") {
|
||||
this.setState({
|
||||
displayColorPicker: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
displayBackgroundColorPicker: false,
|
||||
});
|
||||
};
|
||||
|
||||
changeEvent(event) {
|
||||
this.setState({
|
||||
event: event,
|
||||
message: "",
|
||||
});
|
||||
if (this.props.event !== "new") {
|
||||
this.initForm();
|
||||
}
|
||||
}
|
||||
|
||||
resetColor(event) {
|
||||
if (event === "color") {
|
||||
this.updateFormField("color", this.state.color);
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateFormField("backgroundColor", this.state.backgroundColor);
|
||||
}
|
||||
|
||||
renderColorPicker(event) {
|
||||
return (
|
||||
<div style={{
|
||||
padding: '5px',
|
||||
background: '#fff',
|
||||
borderRadius: '1px',
|
||||
boxShadow: '0 0 0 1px rgba(0,0,0,.1)',
|
||||
display: 'inline-block',
|
||||
cursor: 'pointer'
|
||||
}} onClick={() => this.handleColorClick(event)} >
|
||||
<div style={{
|
||||
width: '36px',
|
||||
height: '14px',
|
||||
borderRadius: '2px',
|
||||
background: event === "color" ? `${this.state.form.color}` : `${this.state.form.backgroundColor}`
|
||||
}} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderNode(node) {
|
||||
return (
|
||||
<span>
|
||||
<a href={`/go/${node.id}`}>
|
||||
{node.name}
|
||||
</a>{" "} {" "}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
renderProblem() {
|
||||
let problems = [];
|
||||
|
||||
if (this.state.errorMessage !== "") {
|
||||
problems.push(i18next.t(`error:${this.state.errorMessage}`));
|
||||
}
|
||||
|
||||
if (problems.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="problem" onClick={() => this.clearErrorMessage()}>
|
||||
{i18next.t("error:Please resolve the following issues before submitting")}
|
||||
<ul>
|
||||
{
|
||||
problems.map((problem, i) => {
|
||||
return <li>{problem}</li>;
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderManagementList(item){
|
||||
return (
|
||||
<a href="javascript:void(0);" className={this.state.event === item.value ? "tab_current" : "tab"} onClick={() => this.changeEvent(item.value)}>{i18next.t(`plane:${item.label}`)}</a>
|
||||
);
|
||||
}
|
||||
|
||||
renderHeader() {
|
||||
return (
|
||||
<div className="box">
|
||||
<div className="header"><a href="/">{Setting.getForumName()}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<a href={`/admin`}>{i18next.t("admin:Backstage management")}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<a href={`/admin/plane`}>{i18next.t("plane:Plane management")}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<span>
|
||||
{
|
||||
this.props.event === "new" ?
|
||||
i18next.t("plane:New plane") : this.state.planeId
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<div className="cell">
|
||||
{
|
||||
this.state.Management_LIST.map((item) => {
|
||||
return this.renderManagementList(item);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderPlanes(plane) {
|
||||
const pcBrowser = Setting.PcBrowser;
|
||||
|
||||
return (
|
||||
<div className="cell">
|
||||
<table cellPadding="0" cellSpacing="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width={pcBrowser ? "200" : "auto"} align="left">
|
||||
<span className="gray">
|
||||
{plane?.name}
|
||||
</span>
|
||||
</td>
|
||||
<td width={pcBrowser ? "100" : "auto"} align="center">
|
||||
<a href={`/admin/plane/edit/${plane?.id}`}>
|
||||
{i18next.t("plane:Manage")}
|
||||
</a>
|
||||
</td>
|
||||
<td width="10"></td>
|
||||
<td width={pcBrowser ? "120" : "80"} valign="middle" style={{textAlign: "center"}}>
|
||||
<span style={{fontSize: "13px"}}>
|
||||
{plane?.nodesNum}{" "}{i18next.t("plane:nodes")}
|
||||
</span>
|
||||
</td>
|
||||
<td width={pcBrowser ? "120" : "80"} align="left" style={{textAlign: "center"}}>
|
||||
{
|
||||
plane?.visible ?
|
||||
<span className="positive">
|
||||
{i18next.t("plane:Visible")}
|
||||
</span> :
|
||||
<span className="gray">
|
||||
{i18next.t("plane:Invisible")}
|
||||
</span>
|
||||
}
|
||||
</td>
|
||||
<td width="50" align="left" style={{textAlign: "right"}}>
|
||||
<a href="#" onClick={() => this.deletePlane(plane?.name, plane?.id, plane?.nodesNum)}>
|
||||
{i18next.t("plane:Delete")}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const newPlane = (this.props.event === "new");
|
||||
|
||||
if (this.state.planeId !== undefined || newPlane) {
|
||||
if (this.state.planeId !== undefined) {
|
||||
if (this.state.plane !== null && this.state.plane.length === 0) {
|
||||
return (
|
||||
<div className="box">
|
||||
<div className="header"><a href="/">{Setting.getForumName()}</a><span className="chevron"> › </span>{" "}{i18next.t("loading:Page is loading")}</div>
|
||||
<div className="cell"><span className="gray bigger">{i18next.t("loading:Please wait patiently...")}</span></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.plane === null) {
|
||||
return (
|
||||
<div class="box">
|
||||
<div class="header">
|
||||
<a href="/">{Setting.getForumName()}</a>
|
||||
<span className="chevron"> › </span>{" "}{i18next.t("error:Plane not found")}</div>
|
||||
<div class="cell">
|
||||
{i18next.t("error:The plane you are trying to view does not exist")}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const plane = this.state.plane;
|
||||
|
||||
if (this.state.event === "basic") {
|
||||
return (
|
||||
<div>
|
||||
{this.renderHeader()}
|
||||
<div className="box">
|
||||
{this.renderProblem()}
|
||||
{
|
||||
this.state.message !== "" ?
|
||||
<div className="message" onClick={() => this.clearMessage()}>
|
||||
<li className="fa fa-exclamation-triangle"></li>
|
||||
{" "}
|
||||
{this.state.message}
|
||||
</div> : null
|
||||
}
|
||||
<div className="inner">
|
||||
<table cellPadding="5" cellSpacing="0" border="0" width="100%">
|
||||
<tbody>
|
||||
{
|
||||
newPlane ?
|
||||
<tr>
|
||||
<td width="120" align="right">{i18next.t("plane:Plane ID")}</td>
|
||||
<td width="auto" align="left">
|
||||
<input type="text" className="sl" name="id" id="plane_id" value={this.state.form?.id} onChange={event => this.updateFormField("id", event.target.value)} autoComplete="off" />
|
||||
</td>
|
||||
</tr> : null
|
||||
}
|
||||
<tr>
|
||||
<td width="120" align="right">{i18next.t("plane:Plane name")}</td>
|
||||
<td width="auto" align="left">
|
||||
<input type="text" className="sl" name="name" id="plane_name" value={this.state.form?.name===undefined ? "" : this.state.form?.name} onChange={event => this.updateFormField("name", event.target.value)} autoComplete="off" />
|
||||
</td>
|
||||
</tr>
|
||||
{
|
||||
!newPlane ?
|
||||
<tr>
|
||||
<td width="120" align="right">{i18next.t("plane:Created time")}</td>
|
||||
<td width="auto" align="left">
|
||||
<span className="gray">
|
||||
{plane?.createdTime}
|
||||
</span>
|
||||
</td>
|
||||
</tr> : null
|
||||
}
|
||||
{
|
||||
!newPlane ?
|
||||
<tr>
|
||||
<td width="120" align="right">{i18next.t("plane:Total nodes")}</td>
|
||||
<td width="auto" align="left">
|
||||
<span className="gray">
|
||||
{plane?.nodesNum}
|
||||
</span>
|
||||
</td>
|
||||
</tr> : null
|
||||
}
|
||||
{
|
||||
!newPlane ?
|
||||
<tr>
|
||||
<td width="120" align="right">{i18next.t("plane:Nodes")}</td>
|
||||
<td width="auto" align="left">
|
||||
<span className="gray">
|
||||
{
|
||||
this.state.plane?.nodes.length !== 0 ?
|
||||
this.state.plane?.nodes.map(node => this.renderNode(node)) :
|
||||
i18next.t("plane:No node yet")
|
||||
}
|
||||
</span>
|
||||
</td>
|
||||
</tr> : null
|
||||
}
|
||||
<tr>
|
||||
<td width="120" align="right">{i18next.t("plane:Sorter")}</td>
|
||||
<td width="auto" align="left">
|
||||
<input type="range" min="1" max="1000" step="1" value={this.state.form?.sorter === undefined ? 1 : this.state.form?.sorter} onChange={event => this.updateFormField("sorter", parseInt(event.target.value))}/>
|
||||
{" "} {" "}
|
||||
<input type="number" name="sorter" min="1" max="1000" step="1" value={this.state.form?.sorter} style={{width: "50px"}} onChange={event => this.updateFormField("sorter", parseInt(event.target.value))} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" align="right"></td>
|
||||
<td width="auto" align="left">
|
||||
<span className="gray">
|
||||
{i18next.t("plane:Decide the order of node navigation")}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" align="right">{i18next.t("plane:Visible")}</td>
|
||||
<td width="auto" align="left">
|
||||
<input type="radio" onClick={() => this.updateFormField("visible", true)} checked={this.state.form?.visible} name="visible" />{i18next.t("plane:Visible")}{" "}
|
||||
<input type="radio" onClick={() => this.updateFormField("visible", false)} checked={!this.state.form?.visible} name="visible" />{i18next.t("plane:Invisible")}
|
||||
</td>
|
||||
</tr>
|
||||
{
|
||||
!this.state.form.visible ?
|
||||
<tr>
|
||||
<td width="120" align="right"></td>
|
||||
<td width="auto" align="left">
|
||||
<span className="gray">
|
||||
{i18next.t("plane:This plane will not appear on the node navigation")}
|
||||
</span>
|
||||
</td>
|
||||
</tr> : null
|
||||
}
|
||||
<tr>
|
||||
<td width="120" align="right"></td>
|
||||
<td width="auto" align="left">
|
||||
{
|
||||
!newPlane ?
|
||||
<input type="submit" className="super normal button" value={i18next.t("plane:Save")} onClick={() => this.updatePlaneInfo()}/> : null
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
{
|
||||
newPlane ?
|
||||
<tr>
|
||||
<td width="120" align="right"></td>
|
||||
<td width="auto" align="left">
|
||||
<span className="gray">
|
||||
{i18next.t("plane:Please go to the display page to continue to improve the information and submit")}
|
||||
</span>
|
||||
</td>
|
||||
</tr> : null
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// display
|
||||
return (
|
||||
<div>
|
||||
{this.renderHeader()}
|
||||
<div className="box">
|
||||
{this.renderProblem()}
|
||||
{
|
||||
this.state.message !== "" ?
|
||||
<div className="message" onClick={() => this.clearMessage()}>
|
||||
<li className="fa fa-exclamation-triangle"></li>
|
||||
{" "}
|
||||
{this.state.message}
|
||||
</div> : null
|
||||
}
|
||||
<div className="inner">
|
||||
<table cellPadding="5" cellSpacing="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="120" align="right">{i18next.t("plane:Image")}</td>
|
||||
<td width="auto" align="left">
|
||||
{
|
||||
this.state.form?.image === undefined || this.state.form?.image === "" ?
|
||||
<span className="gray">
|
||||
{i18next.t("plane:Not set")}
|
||||
</span> :
|
||||
<Zmage
|
||||
src={this.state.form?.image} alt={this.state.form?.id} style={{maxWidth: "48px", maxHeight: "48px"}}
|
||||
/>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" align="right">
|
||||
{
|
||||
newPlane ?
|
||||
i18next.t("plane:Set image") :
|
||||
i18next.t("plane:Change image")
|
||||
}
|
||||
</td>
|
||||
<td width="auto" align="left">
|
||||
<input type="text" className="sl" name="image" id="change_image" value={this.state.form?.image===undefined ? "" : this.state.form?.image} onChange={event => this.updateFormField("image", event.target.value)} autoComplete="off" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" align="right">{i18next.t("plane:Font color")}</td>
|
||||
<td width="auto" align="left">
|
||||
{this.renderColorPicker("color")}
|
||||
{
|
||||
this.state.displayColorPicker ?
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
zIndex: '2'}}
|
||||
>
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
top: '0px',
|
||||
right: '0px',
|
||||
bottom: '0px',
|
||||
left: '0px'
|
||||
}} onClick={() => this.handleColorClose("color")} />
|
||||
<SketchPicker color={this.state.form.color} onChange={(color) => this.handleColorChange(color, "color")} />
|
||||
</div> : null
|
||||
}
|
||||
{" "} {" "}
|
||||
{
|
||||
!newPlane ?
|
||||
<a href="#" onClick={() => this.resetColor("color")}>
|
||||
{i18next.t("plane:Restore")}
|
||||
</a> : null
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" align="right">{i18next.t("plane:Background color")}</td>
|
||||
<td width="auto" align="left">
|
||||
{this.renderColorPicker("backgroundColor")}
|
||||
{
|
||||
this.state.displayBackgroundColorPicker ?
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
zIndex: '2'}}
|
||||
>
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
top: '0px',
|
||||
right: '0px',
|
||||
bottom: '0px',
|
||||
left: '0px'
|
||||
}} onClick={() => this.handleColorClose("backgroundColor")} />
|
||||
<SketchPicker color={this.state.form.backgroundColor} onChange={(color) => this.handleColorChange(color, "backgroundColor")} />
|
||||
</div> : null
|
||||
}
|
||||
{" "} {" "}
|
||||
{
|
||||
!newPlane ?
|
||||
<a href="#" onClick={() => this.resetColor("backgroundColor")}>
|
||||
{i18next.t("plane:Restore")}
|
||||
</a> : null
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" align="right">{i18next.t("plane:Preview")}</td>
|
||||
<td width="auto" align="left">
|
||||
<div className="header" style={{backgroundColor: this.state.form?.backgroundColor, color: this.state.form?.color, fontSize: Setting.PcBrowser ? "" : "13px"}}>
|
||||
<img src={this.state.form?.image} border="0" align="absmiddle" width="24"/>
|
||||
{" "} {" "}{this.state.form?.name}
|
||||
<span className="fr" style={{color: this.state.form?.color, lineHeight: "20px"}}>
|
||||
{this.state.form?.id}{" "}•{" "}
|
||||
{
|
||||
!newPlane ?
|
||||
<span className="small">
|
||||
{plane?.nodesNum}{" "}nodes
|
||||
</span> : null
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" align="right"></td>
|
||||
<td width="auto" align="left">
|
||||
{
|
||||
newPlane ?
|
||||
<input type="submit" className="super normal button" value={i18next.t("plane:Create")} onClick={() => this.postNewPlane()}/> :
|
||||
<input type="submit" className="super normal button" value={i18next.t("plane:Save")} onClick={() => this.updatePlaneInfo()}/>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="box">
|
||||
<div className="header">
|
||||
<a href="/">{Setting.getForumName()}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<a href={`/admin`}>{i18next.t("admin:Backstage management")}</a>
|
||||
<span className="chevron"> › </span>{" "}{i18next.t("plane:Plane management")}
|
||||
<div className="fr f12">
|
||||
<span className="snow">{i18next.t("plane:Total planes")}{" "} </span>
|
||||
<strong className="gray">{this.state.planes.length}</strong>
|
||||
</div>
|
||||
<div className="fr f12">
|
||||
<strong className="gray">
|
||||
<a href="plane/new">{i18next.t("plane:Add new plane")}</a>
|
||||
{" "}
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
this.state.message !== "" ?
|
||||
<div className="message" onClick={() => this.clearMessage()}>
|
||||
<li className="fa fa-exclamation-triangle"></li>
|
||||
{" "}
|
||||
{this.state.message}
|
||||
</div> : null
|
||||
}
|
||||
<div id="all-planes">
|
||||
{
|
||||
this.state.planes.length !== 0 ?
|
||||
this.state.planes.map(plane => this.renderPlanes(plane)) : null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(AdminPlane);
|
|
@ -251,10 +251,12 @@ class AdminTab extends React.Component {
|
|||
return (
|
||||
<div className="box">
|
||||
<div className="header"><a href="/">{Setting.getForumName()}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<a href={`/admin`}>{i18next.t("admin:Backstage management")}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<a href={`/admin/tab`}>{i18next.t("tab:Tab management")}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<span className="gray">
|
||||
<span>
|
||||
{
|
||||
this.props.event === "new" ?
|
||||
i18next.t("tab:New tab") : this.state.tabId
|
||||
|
@ -499,6 +501,8 @@ class AdminTab extends React.Component {
|
|||
<div className="box">
|
||||
<div className="header">
|
||||
<a href="/">{Setting.getForumName()}</a>
|
||||
{" "}<span className="chevron"> › </span>
|
||||
<a href={`/admin`}>{i18next.t("admin:Backstage management")}</a>
|
||||
<span className="chevron"> › </span>{" "}{i18next.t("tab:Tab management")}
|
||||
<div className="fr f12">
|
||||
<span className="snow">{i18next.t("tab:Total tabs")}{" "} </span>
|
||||
|
|
|
@ -21,9 +21,39 @@ export function getPlaneList() {
|
|||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getPlaneAdmin(id) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-plane-admin?id=${id}`, {
|
||||
method: 'GET',
|
||||
credentials: 'include'
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getPlanesAdmin() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-planes-admin`, {
|
||||
method: 'GET',
|
||||
credentials: 'include'
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function deletePlane(id) {
|
||||
return fetch(`${Setting.ServerUrl}/api/delete-plane?id=${id}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function updatePlane(id, plane) {
|
||||
return fetch(`${Setting.ServerUrl}/api/update-plane?id=${id}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(plane),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function addPlane(plane) {
|
||||
return fetch(`${Setting.ServerUrl}/api/add-plane`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(plane),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
|
|
@ -339,7 +339,45 @@
|
|||
},
|
||||
"plane":
|
||||
{
|
||||
"Plane list": "位面列表"
|
||||
"Plane list": "Plane list",
|
||||
"Are you sure to delete plane": "Are you sure to delete plane",
|
||||
"Please delete all the nodes or move to another plane before deleting the plane": "Please delete all the nodes or move to another plane before deleting the plane",
|
||||
"Currently the number of nodes under the plane is": "Currently the number of nodes under the plane is",
|
||||
"Delete plane": "Delete plane",
|
||||
"success": "success",
|
||||
"Update plane information success": "Update plane information success",
|
||||
"Creat plane success": "Creat plane success",
|
||||
"Plane management": "Plane management",
|
||||
"New plane": "New plane",
|
||||
"Manage": "Manage",
|
||||
"nodes": "nodes",
|
||||
"Visible": "Visible",
|
||||
"Invisible": "Invisible",
|
||||
"Delete": "Delete",
|
||||
"Plane ID": "Plane ID",
|
||||
"Plane name": "Plane name",
|
||||
"Created time": "Created time",
|
||||
"Total nodes": "Total nodes",
|
||||
"Nodes": "Nodes",
|
||||
"No node yet": "No node yet",
|
||||
"Sorter": "Sorter",
|
||||
"Decide the order of node navigation": "Decide the order of node navigation",
|
||||
"This plane will not appear on the node navigation": "This plane will not appear on the node navigation",
|
||||
"Save": "Save",
|
||||
"Please go to the display page to continue to improve the information and submit": "Please go to the display page to continue to improve the information and submit",
|
||||
"Image": "Image",
|
||||
"Not set": "Not set",
|
||||
"Set image": "Set image",
|
||||
"Change image": "Change image",
|
||||
"Font color": "Font color",
|
||||
"Restore": "Restore",
|
||||
"Background color": "Background color",
|
||||
"Preview": "Preview",
|
||||
"Create": "Create",
|
||||
"Total planes": "Total planes",
|
||||
"Add new plane": "Add new plane",
|
||||
"Basic Info": "Basic Info",
|
||||
"Display": "Display"
|
||||
},
|
||||
"newNodeTopic":
|
||||
{
|
||||
|
@ -565,7 +603,8 @@
|
|||
"Node management": "Node management",
|
||||
"Topic management": "Topic management",
|
||||
"Member management": "Member management",
|
||||
"Tab management": "Tab management"
|
||||
"Tab management": "Tab management",
|
||||
"Plane management": "Plane management"
|
||||
},
|
||||
"error":
|
||||
{
|
||||
|
@ -631,6 +670,11 @@
|
|||
"The tab you are trying to view does not exist": "The tab you are trying to view does not exist",
|
||||
"Tab ID existed": "Tab ID existed",
|
||||
"Your account has been muted": "Your account has been muted",
|
||||
"Your account has been forbidden to log in": "Your account has been forbidden to log in"
|
||||
"Your account has been forbidden to log in": "Your account has been forbidden to log in",
|
||||
"Plane not found": "Plane not found",
|
||||
"The plane you are trying to view does not exist": "The plane you are trying to view does not exist",
|
||||
"Plane ID existed": "Plane ID existed",
|
||||
"Please input plane ID": "Please input plane ID",
|
||||
"Please input plane name": "Please input plane name"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -339,7 +339,45 @@
|
|||
},
|
||||
"plane":
|
||||
{
|
||||
"Plane list": "位面列表"
|
||||
"Plane list": "位面列表",
|
||||
"Are you sure to delete plane": "你确定删除位面",
|
||||
"Please delete all the nodes or move to another plane before deleting the plane": "在删除位面之前请删除位面下所有节点或移动至其他位面",
|
||||
"Currently the number of nodes under the plane is": "目前此位面下的节点数为",
|
||||
"Delete plane": "删除位面",
|
||||
"success": "成功",
|
||||
"Update plane information success": "更新位面信息成功",
|
||||
"Creat plane success": "创建位面成功",
|
||||
"Plane management": "位面管理",
|
||||
"New plane": "新建位面",
|
||||
"Manage": "管理",
|
||||
"nodes": "节点",
|
||||
"Visible": "可见",
|
||||
"Invisible": "隐藏",
|
||||
"Delete": "删除",
|
||||
"Plane ID": "位面ID",
|
||||
"Plane name": "位面名称",
|
||||
"Created time": "创建时间",
|
||||
"Total nodes": "节点总数",
|
||||
"Nodes": "节点",
|
||||
"No node yet": "暂无节点",
|
||||
"Sorter": "排序",
|
||||
"Decide the order of node navigation": "决定在节点导航中的顺序",
|
||||
"This plane will not appear on the node navigation": "此位面将不会出现在节点导航中",
|
||||
"Save": "保存",
|
||||
"Please go to the display page to continue to improve the information and submit": "请前往显示页面继续完善信息然后提交",
|
||||
"Image": "图标",
|
||||
"Not set": "未设定",
|
||||
"Set image": "设置图标",
|
||||
"Change image": "更换图标",
|
||||
"Font color": "文字颜色",
|
||||
"Restore": "恢复",
|
||||
"Background color": "背景颜色",
|
||||
"Preview": "预览",
|
||||
"Create": "创建",
|
||||
"Total planes": "总位面",
|
||||
"Add new plane": "新增位面",
|
||||
"Basic Info": "基本信息",
|
||||
"Display": "显示"
|
||||
},
|
||||
"newNodeTopic":
|
||||
{
|
||||
|
@ -565,7 +603,8 @@
|
|||
"Node management": "节点管理",
|
||||
"Topic management": "帖子管理",
|
||||
"Member management": "用户管理",
|
||||
"Tab management": "类别管理"
|
||||
"Tab management": "类别管理",
|
||||
"Plane management": "位面管理"
|
||||
},
|
||||
"error":
|
||||
{
|
||||
|
@ -631,6 +670,11 @@
|
|||
"The tab you are trying to view does not exist": "你即将访问的类别不存在",
|
||||
"Tab ID existed": "类别ID已存在",
|
||||
"Your account has been muted": "你的账号已被禁言",
|
||||
"Your account has been forbidden to log in": "你的账号已被禁止登录"
|
||||
"Your account has been forbidden to log in": "你的账号已被禁止登录",
|
||||
"Plane not found": "位面未找到",
|
||||
"The plane you are trying to view does not exist": "你即将访问的位面不存在",
|
||||
"Plane ID existed": "位面ID已存在",
|
||||
"Please input plane ID": "请填写位面ID",
|
||||
"Please input plane name": "请填写位面名称"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ class TopicBox extends React.Component {
|
|||
<div className="outdated">
|
||||
{i18next.t("topic:This is a topic created")} {diffDays} {i18next.t("topic:days ago, the information in it may have changed.")}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
addFavorite() {
|
||||
|
@ -223,7 +223,7 @@ class TopicBox extends React.Component {
|
|||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderImage = ({alt, src}) => {
|
||||
|
@ -285,7 +285,7 @@ class TopicBox extends React.Component {
|
|||
</div>
|
||||
{" "} {" "}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderDesktopButtons() {
|
||||
|
@ -339,19 +339,19 @@ class TopicBox extends React.Component {
|
|||
: null
|
||||
}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const pcBrowser = Setting.PcBrowser
|
||||
const pcBrowser = Setting.PcBrowser;
|
||||
|
||||
if (this.props.account === undefined || (this.state.topic !== null && this.state.topic.length === 0)) {
|
||||
if ((this.props.account === undefined) || (this.state.topic !== null && this.state.topic.length === 0)) {
|
||||
return (
|
||||
<div class="box">
|
||||
<div class="header"><a href="/">{Setting.getForumName()}</a> <span class="chevron"> › </span>{" "}{i18next.t("loading:Topic is loading")}</div>
|
||||
<div class="cell"><span class="gray bigger">{i18next.t("loading:Please wait patiently...")}</span></div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.topic === null) {
|
||||
|
@ -361,12 +361,12 @@ class TopicBox extends React.Component {
|
|||
<div class="cell"><span class="gray bigger">404 Topic Not Found</span></div>
|
||||
<div class="inner">← <a href="/">{i18next.t("error:Back to Home Page")}</a></div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.event === "review") {
|
||||
if (this.props.account === null || this.props.account?.id !== this.state.topic?.author) {
|
||||
goToLink(`/t/${this.state.topic?.id}`)
|
||||
goToLink(`/t/${this.state.topic?.id}`);
|
||||
}
|
||||
return (
|
||||
<div class="box">
|
||||
|
@ -444,7 +444,7 @@ class TopicBox extends React.Component {
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
Loading…
Reference in New Issue