fix: the 404 page in the footer (#517)

* fix: 404 page in footer

* Update ReplyBox.js

* Update AdminFrontConf.js

* Update frontConf.go

* Update router.go

* Update Footer.js

* Update FooterRenderBox.js

Co-authored-by: Yang Luo <hsluoyz@qq.com>
This commit is contained in:
Nekotoxin 2022-05-22 17:28:36 +08:00 committed by GitHub
parent ccc6af382a
commit ab5466e0b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 559 additions and 108 deletions

View File

@ -18,66 +18,85 @@ import (
"encoding/json"
"github.com/casbin/casnode/object"
"github.com/casbin/casnode/service"
)
// @Title GetFrontConfById
// @Description Get front conf by id
// @Success 200 {object} object.FrontConf The Response object
// @router /get-front-conf-by-id [get]
// @Tag FrontConf API
func (c *ApiController) GetFrontConfById() {
id := c.Input().Get("id")
conf := object.GetFrontConfById(id)
c.ResponseOk(conf)
}
// @Title GetFrontConfsByField
// @Description Get front confs by field
// @Success 200 {array} object.FrontConf The Response object
// @router /get-front-conf-by-field [get]
// @router /get-front-confs-by-field [get]
// @Tag FrontConf API
func (c *ApiController) GetFrontConfByField() {
func (c *ApiController) GetFrontConfsByField() {
field := c.Input().Get("field")
c.Data["json"] = object.GetFrontConfByField(field)
c.ServeJSON()
confs := object.GetFrontConfsByField(field)
c.ResponseOk(confs)
}
// @Title GetFrontConfs
// @Description Get all front confs
// @Success 200 {array} array The Response object
// @router /get-front-confs [get]
// @router /update-front-conf-by-id [post]
// @Tag FrontConf API
func (c *ApiController) GetFrontConfs() {
confs := make(map[string]string)
conf := object.GetFrontConfs()
for _, frontconf := range conf {
confs[frontconf.Id] = frontconf.Value
}
c.Data["json"] = confs
c.ServeJSON()
}
// @router /update-front-conf [post]
// @Tag FrontConf API
// @Title UpdateFrontConf
func (c *ApiController) UpdateFrontConf() {
var confs []*object.FrontConf
// @Title UpdateFrontConfById
func (c *ApiController) UpdateFrontConfById() {
if c.RequireAdmin() {
return
}
id := c.Input().Get("id")
// get from body
var value string
err := json.Unmarshal(c.Ctx.Input.RequestBody, &value)
tags := service.Finalword(value)
affect, err := object.UpdateFrontConfById(id, value, tags)
if err != nil {
c.ResponseError(err.Error())
}
c.ResponseOk(affect)
}
// @router /update-front-confs-by-filed [post]
// @Tag FrontConf API
// @Title UpdateFrontConfsByField
func (c *ApiController) UpdateFrontConfsByField() {
if c.RequireAdmin() {
return
}
filed := c.Input().Get("field")
var confs []*object.FrontConf
err := json.Unmarshal(c.Ctx.Input.RequestBody, &confs)
if err != nil {
panic(err)
c.ResponseError(err.Error())
}
res := object.UpdateFrontConf(confs)
c.ResponseOk(res)
err = object.UpdateFrontConfsByField(confs, filed)
if err != nil {
c.ResponseError(err.Error())
}
c.ResponseOk(nil)
}
// @router /update-to-default-conf [post]
// @router /restore-front-confs [post]
// @Tag FrontConf API
// @Title UpdateFrontConfToDefault
func (c *ApiController) UpdateFrontConfToDefault() {
// @Title RestoreFrontConfs
func (c *ApiController) RestoreFrontConfs() {
if c.RequireAdmin() {
return
}
res := object.UpdateFrontConf(object.Confs)
filed := c.Input().Get("field")
res := object.UpdateFrontConfsByField(object.Confs, filed)
c.ResponseOk(res)
}

View File

@ -15,50 +15,47 @@
package object
type FrontConf struct {
Id string `xorm:"varchar(100) notnull pk"`
Value string `xorm:"mediumtext"`
Field string `xorm:"varchar(100)`
Id string `xorm:"varchar(100) notnull pk" json:"id"`
Value string `xorm:"mediumtext" json:"value"`
Field string `xorm:"varchar(100)" json:"field"`
Tags []string `xorm:"varchar(200)" json:"tags"`
}
var (
Confs = []*FrontConf{
{Id: "forumName", Value: "Casnode", Field: "visualConf"},
{Id: "logoImage", Value: "https://cdn.casbin.com/forum/static/img/logo.png", Field: "visualConf"},
{Id: "footerLogoImage", Value: "https://cdn.casbin.com/forum/static/img/logo-footer.png", Field: "visualConf"},
{Id: "footerLogoUrl", Value: "https://www.digitalocean.com/", Field: "visualConf"},
{Id: "signinBoxStrong", Value: "Casbin = way to authorization", Field: "visualConf"},
{Id: "signinBoxSpan", Value: "A place for Casbin developers and users", Field: "visualConf"},
{Id: "footerDeclaration", Value: "World is powered by code", Field: "visualConf"},
{Id: "footerAdvise", Value: "♥ Do have faith in what you're doing.", Field: "visualConf"},
{Id: "forumName", Value: "Casnode", Field: "visualConf", Tags: nil},
{Id: "logoImage", Value: "https://cdn.casbin.com/forum/static/img/logo.png", Field: "visualConf", Tags: nil},
{Id: "footerLogoImage", Value: "https://cdn.casbin.com/forum/static/img/logo-footer.png", Field: "visualConf", Tags: nil},
{Id: "footerLogoUrl", Value: "https://www.digitalocean.com/", Field: "visualConf", Tags: nil},
{Id: "signinBoxStrong", Value: "Casbin = way to authorization", Field: "visualConf", Tags: nil},
{Id: "signinBoxSpan", Value: "A place for Casbin developers and users", Field: "visualConf", Tags: nil},
{Id: "footerDeclaration", Value: "World is powered by code", Field: "visualConf", Tags: nil},
{Id: "footerAdvise", Value: "♥ Do have faith in what you're doing.", Field: "visualConf", Tags: nil},
{Id: "faq", Value: "Not yet", Field: "", Tags: nil},
{Id: "mission", Value: "Not yet", Field: "", Tags: nil},
{Id: "advertise", Value: "Not yet", Field: "", Tags: nil},
{Id: "thanks", Value: "Not yet", Field: "", Tags: nil},
}
)
func InitFrontConf() {
conf := GetFrontConfs()
if len(conf) > 0 {
var confs []*FrontConf
err := adapter.Engine.Find(&confs)
if err != nil {
panic(err)
}
if len(confs) > 0 {
return
}
conf = Confs
_, err := adapter.Engine.Insert(&conf)
confs = Confs
_, err = adapter.Engine.Insert(&confs)
if err != nil {
panic(err)
}
}
func GetFrontConfByField(field string) []*FrontConf {
confs := []*FrontConf{}
err := adapter.Engine.Where("field = ?", field).Find(&confs)
if err != nil {
panic(err)
}
return confs
}
func GetFrontConfById(id string) *FrontConf {
confs := []*FrontConf{}
var confs []*FrontConf
err := adapter.Engine.Where("id = ?", id).Find(&confs)
if err != nil {
panic(err)
@ -71,9 +68,9 @@ func GetFrontConfById(id string) *FrontConf {
}
}
func GetFrontConfs() []*FrontConf {
confs := []*FrontConf{}
err := adapter.Engine.Find(&confs)
func GetFrontConfsByField(field string) []*FrontConf {
var confs []*FrontConf
err := adapter.Engine.Where("field = ?", field).Find(&confs)
if err != nil {
panic(err)
}
@ -81,10 +78,10 @@ func GetFrontConfs() []*FrontConf {
return confs
}
func UpdateFrontConf(confs []*FrontConf) bool {
func UpdateFrontConfs(confs []*FrontConf) bool {
var err error
for _, v := range confs {
_, err = adapter.Engine.Id(v.Id).Cols("value").Update(v)
for _, conf := range confs {
_, err = adapter.Engine.Where("id = ?", conf.Id).Update(conf)
if err != nil {
panic(err)
}
@ -92,3 +89,19 @@ func UpdateFrontConf(confs []*FrontConf) bool {
return true
}
func UpdateFrontConfById(id string, value string, tags []string) (int64, error) {
return adapter.Engine.Id(id).Cols("value", "tags").Update(&FrontConf{Value: value, Tags: tags})
}
func UpdateFrontConfsByField(confs []*FrontConf, field string) error {
for _, conf := range confs {
if conf.Field == field {
_, err := adapter.Engine.Id(conf.Id).Cols("value").Update(conf)
if err != nil {
return err
}
}
}
return nil
}

View File

@ -157,8 +157,9 @@ func initAPI() {
beego.Router("/api/node-navigation", &controllers.ApiController{}, "GET:GetNodeNavigation")
beego.Router("/api/search", &controllers.ApiController{}, "GET:Search")
beego.Router("/api/get-front-conf-by-field", &controllers.ApiController{}, "GET:GetFrontConfByField")
beego.Router("/api/get-front-confs", &controllers.ApiController{}, "GET:GetFrontConfs")
beego.Router("/api/update-front-conf", &controllers.ApiController{}, "POST:UpdateFrontConf")
beego.Router("/api/update-to-default-conf", &controllers.ApiController{}, "POST:UpdateFrontConfToDefault")
beego.Router("/api/get-front-conf-by-id", &controllers.ApiController{}, "GET:GetFrontConfById")
beego.Router("/api/get-front-confs-by-field", &controllers.ApiController{}, "GET:GetFrontConfsByField")
beego.Router("/api/update-front-conf-by-id", &controllers.ApiController{}, "POST:UpdateFrontConfById")
beego.Router("/api/update-front-confs-by-field", &controllers.ApiController{}, "POST:UpdateFrontConfsByField")
beego.Router("/api/restore-front-confs", &controllers.ApiController{}, "POST:RestoreFrontConfs")
}

View File

@ -72,10 +72,12 @@ import AdminPlane from "./admin/AdminPlane";
import AdminTopic from "./admin/AdminTopic";
import AdminSensitive from "./admin/AdminSensitive";
import AdminFrontConf from "./admin/AdminFrontConf";
import AdminFooterEdit from "./admin/AdminFooterEdit";
import AboutForum from "./main/AboutForum";
import SearchResultPage from "./SearchResultPage";
import NoMatch from "./main/NoMatch";
import Embed from "./Embed";
import FooterRenderBox from "./main/FooterRenderBox";
class App extends Component {
constructor(props) {
@ -442,6 +444,57 @@ class App extends Component {
<AdminFrontConf account={this.state.account} />
</div>
</Route>
<Route exact path="/admin/faq">
<div id={pcBrowser ? "Main" : ""}>
{pcBrowser ? <div className="sep20" /> : null}
<AdminFooterEdit account={this.state.account} id={"faq"} />
</div>
</Route>
<Route exact path="/admin/mission">
<div id={pcBrowser ? "Main" : ""}>
{pcBrowser ? <div className="sep20" /> : null}
<AdminFooterEdit account={this.state.account} id={"mission"} />
</div>
</Route>
<Route exact path="/admin/advertise">
<div id={pcBrowser ? "Main" : ""}>
{pcBrowser ? <div className="sep20" /> : null}
<AdminFooterEdit account={this.state.account} id={"advertise"} />
</div>
</Route>
<Route exact path="/admin/thanks">
<div id={pcBrowser ? "Main" : ""}>
{pcBrowser ? <div className="sep20" /> : null}
<AdminFooterEdit account={this.state.account} id={"thanks"} />
</div>
</Route>
<Route exact path="/faq">
<div id={pcBrowser ? "Main" : ""}>
{pcBrowser ? <div className="sep20" /> : null}
<FooterRenderBox id={"faq"} key={"faq"} />
</div>
</Route>
<Route exact path="/mission">
<div id={pcBrowser ? "Main" : ""}>
{pcBrowser ? <div className="sep20" /> : null}
<FooterRenderBox id={"mission"} key={"mission"} />
</div>
</Route>
<Route exact path="/advertise">
<div id={pcBrowser ? "Main" : ""}>
{pcBrowser ? <div className="sep20" /> : null}
<FooterRenderBox id={"advertise"} key={"advertise"} />
</div>
</Route>
<Route exact path="/thanks">
<div id={pcBrowser ? "Main" : ""}>
{pcBrowser ? <div className="sep20" /> : null}
<FooterRenderBox id={"thanks"} key={"thanks"} />
</div>
</Route>
<Route exact path="/about">
<div id={pcBrowser ? "Main" : ""}>
{pcBrowser ? <div className="sep20" /> : null}

View File

@ -124,9 +124,9 @@ class Footer extends React.Component {
FAQ
</Link>{" "}
&nbsp; <span className="snow">·</span> &nbsp;{" "}
<Link to="/api" className="dark" target="_self">
<a href={`${Setting.ServerUrl}/swagger`} className="dark" target="_self">
API
</Link>{" "}
</a>{" "}
&nbsp; <span className="snow">·</span> &nbsp;{" "}
<Link to="/mission" className="dark" target="_self">
{i18next.t("footer:Mission")}
@ -136,7 +136,7 @@ class Footer extends React.Component {
{i18next.t("footer:Advertise")}
</Link>{" "}
&nbsp; <span className="snow">·</span> &nbsp;{" "}
<Link to="/advertise/2019.html" className="dark" target="_self">
<Link to="/thanks" className="dark" target="_self">
{i18next.t("footer:Thanks")}
</Link>{" "}
&nbsp; <span className="snow">·</span> &nbsp;{" "}

View File

@ -320,12 +320,12 @@ export function getFrontConf(field) {
}
}
ConfBackend.getFrontConfByField(field).then((res) => {
for (let key in res) {
if (res[key].Value !== "") {
FrontConfig[res[key].Id] = res[key].Value;
ConfBackend.getFrontConfsByField(field).then((res) => {
for (let key in res.data) {
if (res.data[key].Value !== "") {
FrontConfig[res.data[key].id] = res.data[key].value;
}
storage[res[key].Id] = FrontConfig[res[key].Id];
storage[res.data[key].id] = FrontConfig[res.data[key].id];
}
});
}

View File

@ -0,0 +1,208 @@
// Copyright 2022 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, withRouter } from "react-router-dom";
import * as Setting from "../Setting";
import i18next from "i18next";
import * as ConfBackend from "../backend/ConfBackend";
import * as Tools from "../main/Tools";
import { Controlled as CodeMirrorsEditor } from "react-codemirror2";
class AdminFrontConf extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
content: "",
publishClicked: false,
id: this.props.id,
message: "",
};
}
componentDidMount() {
this.i18nTag = `admin:${this.state?.id.charAt(0).toUpperCase() + this.state?.id.slice(1)}`;
this.getContent(this.state.id);
this.getFrontConf(this.state.id);
}
changeField(field) {
this.setState(
{
field: field,
},
() => {
this.getFrontConf(this.state.field);
}
);
}
getContent(id) {
ConfBackend.getFrontConfById(id).then((res) => {
console.log(res.data.value);
this.setState({
content: res.data.value,
});
});
}
renderManagementList(item) {
return (
<a href="javascript:void(0);" className={this.state.field === item.value ? "tab_current" : "tab"} onClick={() => this.changeField(item.value)}>
{item.label}
</a>
);
}
updateFormField(key, value) {
let form = this.state.form;
form[key] = value;
this.setState({
form: form,
isTypingStarted: true,
});
}
getFrontConf(id) {
ConfBackend.getFrontConfById(id).then((res) => {
console.log(res.data[0]);
});
}
clearMessage() {
this.setState({
message: "",
});
}
updateConf() {
this.setState({ publishClicked: true });
let confs = [];
for (const k in this.state.changeForm) {
confs.push({ id: k, value: this.state.changeForm[k], field: this.state.field });
}
ConfBackend.updateFrontConfById(this.state.id, this.state.content).then((res) => {
if (res.status === "ok") {
this.setState({
message: i18next.t("poster:Update frontconf information success"),
publishClicked: false,
});
} else {
this.setState({
message: res?.msg,
});
}
});
}
renderEditor() {
return (
<div
style={{
overflow: "hidden",
overflowWrap: "break-word",
resize: "none",
height: "auto",
}}
className={`mll`}
id="reply_content"
>
<div className={`cm-short-content`}>
<CodeMirrorsEditor
editorDidMount={(editor) => Tools.attachEditor(editor)}
onPaste={() => Tools.uploadMdFile()}
value={this.state.content}
onDrop={() => Tools.uploadMdFile()}
options={{
mode: "markdown",
lineNumbers: false,
lineWrapping: true,
extraKeys: { "Ctrl-Space": "autocomplete" },
hintOptions: {
alignWithWord: false,
closeOnUnfocus: false,
closeOnBlur: false,
className: "textcomplete-item",
},
}}
onBeforeChange={(editor, data, value) => {
this.setState({ content: value });
}}
onChange={(editor, data, value) => {}}
/>
</div>
</div>
);
}
render() {
let blurStyle = { color: "#ccc", pointerEvents: "none" };
return (
<div>
<div className="box">
<div className="header">
<Link to="/">{Setting.getForumName()}</Link> <span className="chevron">&nbsp;&nbsp;</span>
<Link to="/admin">{i18next.t("admin:Backstage management")}</Link>
<span className="chevron">&nbsp;&nbsp;</span> {i18next.t(this.i18nTag)}
</div>
</div>
<div className="box">
{this.state.message !== "" ? (
<div className="message" onClick={() => this.clearMessage()}>
<li className="fa fa-exclamation-triangle"></li>
&nbsp; {this.state.message}
</div>
) : null}
<div className="inner">
<div className={`cell ${this.props.nodeId}`}>
<div
style={{
overflow: "hidden",
}}
>
{this.renderEditor()}
</div>
<div className="sep10" />
<div
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<button type="button" onClick={this.updateConf.bind(this)} type="submit" className="super normal button">
<li className={this.state.publishClicked ? "fa fa-circle-o-notch fa-spin" : "fa fa-paper-plane"} />
&nbsp;{this.state.publishClicked ? i18next.t("new:Publishing...") : i18next.t("frontConf:Save")}
</button>
</div>
<div
style={{
overflow: "hidden",
}}
>
<div style={blurStyle} className="fr">
<div className="sep5" />
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default withRouter(AdminFrontConf);

View File

@ -13,7 +13,7 @@
// limitations under the License.
import React from "react";
import { withRouter, Link } from "react-router-dom";
import { Link, withRouter } from "react-router-dom";
import * as Setting from "../Setting";
import i18next from "i18next";
import * as ConfBackend from "../backend/ConfBackend";
@ -26,14 +26,14 @@ class AdminFrontConf extends React.Component {
Management_LIST: [{ label: "Visual Conf", value: "visualConf" }],
field: "visualConf",
conf: [
{ Id: "forumName", Value: "" },
{ Id: "logoImage", Value: "" },
{ Id: "signinBoxStrong", Value: "" },
{ Id: "signinBoxSpan", Value: "" },
{ Id: "footerAdvise", Value: "" },
{ Id: "footerDeclaration", Value: "" },
{ Id: "footerLogoImage", Value: "" },
{ Id: "footerLogoUrl", Value: "" },
{ id: "forumName", value: "" },
{ id: "logoImage", value: "" },
{ id: "signinBoxStrong", value: "" },
{ id: "signinBoxSpan", value: "" },
{ id: "footerAdvise", value: "" },
{ id: "footerDeclaration", value: "" },
{ id: "footerLogoImage", value: "" },
{ id: "footerLogoUrl", value: "" },
],
form: {},
changeForm: {},
@ -67,14 +67,14 @@ class AdminFrontConf extends React.Component {
}
getFrontConf(field) {
ConfBackend.getFrontConfByField(field).then((res) => {
ConfBackend.getFrontConfsByField(field).then((res) => {
let form = this.state.form;
let conf = this.state.conf;
for (var k in res) {
form[res[k].Id] = res[k].Value;
for (let k in res.data) {
form[res.data[k].id] = res.data[k].value;
}
for (var i in conf) {
conf[i].Value = form[conf[i].Id];
for (let i in conf) {
conf[i].value = form[conf[i].id];
}
this.setState({
conf: conf,
@ -103,10 +103,11 @@ class AdminFrontConf extends React.Component {
updateConf() {
let confs = [];
for (var k in this.state.changeForm) {
confs.push({ Id: k, Value: this.state.changeForm[k] });
for (const k in this.state.changeForm) {
confs.push({ id: k, value: this.state.changeForm[k], field: this.state.field });
}
ConfBackend.updateFrontConfs(confs).then((res) => {
ConfBackend.updateFrontConfsByField(this.state.field, confs).then((res) => {
if (res.status === "ok") {
this.setState({
message: i18next.t("poster:Update frontconf information success"),
@ -120,7 +121,7 @@ class AdminFrontConf extends React.Component {
}
updateConfToDefault() {
ConfBackend.updateFrontConfToDefault().then((res) => {
ConfBackend.restoreFrontConfs(this.state.field).then((res) => {
if (res.status === "ok") {
this.setState({
message: i18next.t("poster:Update frontconf information success"),
@ -194,10 +195,10 @@ class AdminFrontConf extends React.Component {
return (
<tr>
<td width={Setting.PcBrowser ? "120" : "90"} align="right">
{this.convert(item.Id)}
{this.convert(item.id)}
</td>
<td width="auto" align="left">
<textarea rows="1" style={{ width: "80%" }} value={this.state.form[item.Id]} onChange={(event) => this.inputChange(event, item.Id)} />
<textarea rows="1" style={{ width: "80%" }} value={this.state.form[item.id]} onChange={(event) => this.inputChange(event, item.id)} />
</td>
</tr>
);

View File

@ -70,6 +70,26 @@ class AdminHomepage extends React.Component {
value: "frontconf",
image: Setting.getStatic("/img/settings.png"),
},
{
label: i18next.t("admin:Faq"),
value: "faq",
image: Setting.getStatic("/img/settings.png"),
},
{
label: i18next.t("admin:Mission"),
value: "mission",
image: Setting.getStatic("/img/settings.png"),
},
{
label: i18next.t("admin:Advertise"),
value: "advertise",
image: Setting.getStatic("/img/settings.png"),
},
{
label: i18next.t("admin:Thanks"),
value: "thanks",
image: Setting.getStatic("/img/settings.png"),
},
],
message: "",
};

View File

@ -71,7 +71,6 @@ class AdminPoster extends React.Component {
}
);
}
console.log(poster);
});
}

View File

@ -14,23 +14,38 @@
import * as Setting from "../Setting";
export function getFrontConfByField(field) {
return fetch(`${Setting.ServerUrl}/api/get-front-conf-by-field?field=${field}`, {
export function getFrontConfById(id) {
return fetch(`${Setting.ServerUrl}/api/get-front-conf-by-id?id=${id}`, {
method: "GET",
credentials: "include",
}).then((res) => res.json());
}
export function updateFrontConfs(confs) {
return fetch(`${Setting.ServerUrl}/api/update-front-conf`, {
export function getFrontConfsByField(field) {
return fetch(`${Setting.ServerUrl}/api/get-front-confs-by-field?field=${field}`, {
method: "GET",
credentials: "include",
}).then((res) => res.json());
}
export function updateFrontConfById(id, value) {
return fetch(`${Setting.ServerUrl}/api/update-front-conf-by-id?id=${id}`, {
method: "POST",
credentials: "include",
body: JSON.stringify(value),
}).then((res) => res.json());
}
export function updateFrontConfsByField(field, confs) {
return fetch(`${Setting.ServerUrl}/api/update-front-confs-by-field?field=${field}`, {
method: "POST",
credentials: "include",
body: JSON.stringify(confs),
}).then((res) => res.json());
}
export function updateFrontConfToDefault() {
return fetch(`${Setting.ServerUrl}/api/update-to-default-conf`, {
export function restoreFrontConfs(field) {
return fetch(`${Setting.ServerUrl}/api/restore-front-confs?field=${field}`, {
method: "POST",
credentials: "include",
}).then((res) => res.json());

View File

@ -7,16 +7,19 @@
"Version": "Version"
},
"admin": {
"Advertise": "Advertise",
"Asc": "Aufsteigend",
"Backstage management": "Backstage Management",
"Condition search": "Zustandssuche",
"Desc": "Absteigend",
"Faq": "Faq",
"FrontConf management": "FrontConf Verwaltung",
"Get search result success": "Suchergebnis erfolgreich abrufen",
"Hidden": "Hidden",
"Ignore": "Ignorieren",
"Manage": "Verwalten",
"Member management": "Mitglieder verwalten",
"Mission": "Mission",
"No matching data": "Keine übereinstimmenden Daten",
"Node management": "Verwaltung von Knoten(punkten)",
"Plane management": "Ebenenmanagement",
@ -25,6 +28,7 @@
"Show": "Anzeigen",
"Sorter": "Sortierer",
"Tab management": "Tab-Einstellungen",
"Thanks": "Thanks",
"Topic management": "Themen-Verwaltung",
"Translation management": "Übersetzungsverwaltung"
},

View File

@ -7,16 +7,19 @@
"Version": "Version"
},
"admin": {
"Advertise": "Advertise",
"Asc": "Asc",
"Backstage management": "Backstage management",
"Condition search": "Condition search",
"Desc": "Desc",
"Faq": "Faq",
"FrontConf management": "FrontConf management",
"Get search result success": "Get search result success",
"Hidden": "Hidden",
"Ignore": "Ignore",
"Manage": "Manage",
"Member management": "Member management",
"Mission": "Mission",
"No matching data": "No matching data",
"Node management": "Node management",
"Plane management": "Plane management",
@ -25,6 +28,7 @@
"Show": "Show",
"Sorter": "Sorter",
"Tab management": "Tab management",
"Thanks": "Thanks",
"Topic management": "Topic management",
"Translation management": "Translation management"
},

View File

@ -7,16 +7,19 @@
"Version": "Version"
},
"admin": {
"Advertise": "Advertise",
"Asc": "Ascendant",
"Backstage management": "Gestion des coulisses",
"Condition search": "Recherche de conditions",
"Desc": "Desc",
"Faq": "Faq",
"FrontConf management": "Gestion FrontConf",
"Get search result success": "Réussite du résultat de la recherche",
"Hidden": "Hidden",
"Ignore": "Ignorer",
"Manage": "Gérer",
"Member management": "Gestion des membres",
"Mission": "Mission",
"No matching data": "Aucune donnée correspondante",
"Node management": "Gestion des nœuds",
"Plane management": "Gestion des plans",
@ -25,6 +28,7 @@
"Show": "Afficher",
"Sorter": "Tri",
"Tab management": "Gestion des onglets",
"Thanks": "Thanks",
"Topic management": "Gestion des sujets",
"Translation management": "Gestion des traductions"
},

View File

@ -7,16 +7,19 @@
"Version": "バージョン"
},
"admin": {
"Advertise": "Advertise",
"Asc": "Asc",
"Backstage management": "バックステージ管理",
"Condition search": "条件検索",
"Desc": "Desc",
"Faq": "Faq",
"FrontConf management": "FrontConf管理",
"Get search result success": "検索結果を取得しました",
"Hidden": "Hidden",
"Ignore": "無視",
"Manage": "管理",
"Member management": "メンバー管理",
"Mission": "Mission",
"No matching data": "一致するデータがありません",
"Node management": "ノード管理",
"Plane management": "平面管理",
@ -25,6 +28,7 @@
"Show": "表示",
"Sorter": "ソート",
"Tab management": "タブ管理",
"Thanks": "Thanks",
"Topic management": "トピック管理",
"Translation management": "翻訳管理"
},

View File

@ -7,16 +7,19 @@
"Version": "Версе"
},
"admin": {
"Advertise": "Advertise",
"Asc": "Көтерілу (Asc)",
"Backstage management": "Backstage'ді басқару",
"Condition search": "Шартты іздеу",
"Desc": "Desc",
"Faq": "Faq",
"FrontConf management": "FrontConf'ті басқару",
"Get search result success": "Іздеу нәтижесі сәтті алынды",
"Hidden": "Жасырын",
"Ignore": "Ескермеу",
"Manage": "Басқару",
"Member management": "Мүшелерді басқару",
"Mission": "Mission",
"No matching data": "Сәйкес деректер жоқ",
"Node management": "Түйіндерді басқару",
"Plane management": "Plane басқару",
@ -25,6 +28,7 @@
"Show": "Көрсету",
"Sorter": "Сұрыптау",
"Tab management": "Tab'ты басқару",
"Thanks": "Thanks",
"Topic management": "Топиктерді басқару",
"Translation management": "Аударманы басқару"
},

View File

@ -7,16 +7,19 @@
"Version": "Version"
},
"admin": {
"Advertise": "Advertise",
"Asc": "Asc",
"Backstage management": "Backstage management",
"Condition search": "Condition search",
"Desc": "Desc",
"Faq": "Faq",
"FrontConf management": "FrontConf management",
"Get search result success": "Get search result success",
"Hidden": "Hidden",
"Ignore": "Ignore",
"Manage": "Manage",
"Member management": "Member management",
"Mission": "Mission",
"No matching data": "No matching data",
"Node management": "Node management",
"Plane management": "Plane management",
@ -25,6 +28,7 @@
"Show": "Show",
"Sorter": "Sorter",
"Tab management": "Tab management",
"Thanks": "Thanks",
"Topic management": "Topic management",
"Translation management": "Translation management"
},

View File

@ -7,16 +7,19 @@
"Version": "Версия"
},
"admin": {
"Advertise": "Advertise",
"Asc": "Аск",
"Backstage management": "Управление бэкстадией",
"Condition search": "Поиск условий",
"Desc": "Desc",
"Faq": "Faq",
"FrontConf management": "Управление FrontConf",
"Get search result success": "Результат поиска выполнен",
"Hidden": "Hidden",
"Ignore": "Пропустить",
"Manage": "Управлять",
"Member management": "Управление участниками",
"Mission": "Mission",
"No matching data": "Нет подходящих данных",
"Node management": "Управление узлами",
"Plane management": "Управление плоскостью",
@ -25,6 +28,7 @@
"Show": "Показать",
"Sorter": "Сортировать",
"Tab management": "Управление вкладками",
"Thanks": "Thanks",
"Topic management": "Управление темами",
"Translation management": "Управление переводами"
},

View File

@ -7,16 +7,19 @@
"Version": "版本"
},
"admin": {
"Advertise": "廣告",
"Asc": "升序",
"Backstage management": "後臺管理",
"Condition search": "條件搜索",
"Desc": "降序",
"Faq": "常見問題",
"FrontConf management": "前端配置管理",
"Get search result success": "獲取搜索結果成功",
"Hidden": "隱藏",
"Ignore": "忽略",
"Manage": "管理",
"Member management": "用戶管理",
"Mission": "願景",
"No matching data": "沒有匹配信息",
"Node management": "節點管理",
"Plane management": "位面管理",
@ -25,6 +28,7 @@
"Show": "顯示",
"Sorter": "排序",
"Tab management": "類別管理",
"Thanks": "致謝",
"Topic management": "帖子管理",
"Translation management": "翻譯管理"
},

View File

@ -7,16 +7,19 @@
"Version": "版本"
},
"admin": {
"Advertise": "广告投放",
"Asc": "升序",
"Backstage management": "后台管理",
"Condition search": "条件搜索",
"Desc": "降序",
"Faq": "常见问题",
"FrontConf management": "前端配置管理",
"Get search result success": "获取搜索结果成功",
"Hidden": "隐藏",
"Ignore": "忽略",
"Manage": "管理",
"Member management": "用户管理",
"Mission": "愿景",
"No matching data": "没有匹配信息",
"Node management": "节点管理",
"Plane management": "位面管理",
@ -25,6 +28,7 @@
"Show": "显示",
"Sorter": "排序",
"Tab management": "类别管理",
"Thanks": "致谢",
"Topic management": "帖子管理",
"Translation management": "翻译管理"
},

View File

@ -0,0 +1,86 @@
// Copyright 2022 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, Link } from "react-router-dom";
import * as Setting from "../Setting";
import * as ConfBackend from "../backend/ConfBackend";
import "./NoMatch.css";
import i18next from "i18next";
import { Helmet } from "react-helmet";
import ReactMarkdown from "react-markdown";
class NoMatch extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
id: this.props.id,
conf: null,
};
}
componentDidMount() {
this.i18nTag = `admin:${this.state?.id.charAt(0).toUpperCase() + this.state?.id.slice(1)}`;
this.getContent(this.state.id);
}
getContent(id) {
ConfBackend.getFrontConfById(id).then((res) => {
this.setState({
conf: res.data,
});
});
}
renderBox() {
return (
<div>
<div className="box">
<Helmet>
<title>
{i18next.t(this.i18nTag)} - {Setting.getForumName()}
</title>
<meta name="keywords" content={this.state.conf?.tags} />
</Helmet>
<div className="header">
<Link to="/">{Setting.getForumName()}</Link>
<span className="chevron">&nbsp;&nbsp;</span> {i18next.t(this.i18nTag)}
</div>
</div>
<div className="box">
<div className="inner" id="topic_preview">
<div className="topic_content">
<div className="markdown_body">
<ReactMarkdown source={this.state.conf?.value} escapeHtml={false} />
</div>
</div>
</div>
</div>
</div>
);
}
render() {
return (
<div className="main">
{this.renderBox()}
</div>
)
}
}
export default withRouter(NoMatch);