336 lines
7.9 KiB
Go
336 lines
7.9 KiB
Go
package aboutme
|
|
|
|
import (
|
|
"errors"
|
|
"html/template"
|
|
"net/http"
|
|
"path"
|
|
"personalwebsite/apps"
|
|
"personalwebsite/apps/appCtx"
|
|
"personalwebsite/errormessage"
|
|
"personalwebsite/libs"
|
|
"personalwebsite/wde"
|
|
"personalwebsite/webfilesystem"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
)
|
|
|
|
type AboutMeApp struct {
|
|
fs *webfilesystem.WebFileSystem
|
|
appID string
|
|
mLib libs.MarkdownLib
|
|
titleBarConfig wde.TitleBarConfig //TODO to app manifest?
|
|
path string
|
|
manifest apps.ApplicationManifest
|
|
}
|
|
|
|
func NewAboutMeApp(webFs *webfilesystem.WebFileSystem) *AboutMeApp {
|
|
manifest := apps.ApplicationManifest{}
|
|
_, err := webFs.Read(path.Join("/Applications/AboutMe.app", ".appmanifest"), &manifest)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
newApp := AboutMeApp{
|
|
fs: webFs,
|
|
appID: "AboutMe",
|
|
path: "/Applications/AboutMe.app",
|
|
titleBarConfig: wde.TitleBarConfig{
|
|
Lable: "About Me",
|
|
CloseButton: true,
|
|
HasIcon: true,
|
|
IconPath: "/Icons/GenericApp.icn",
|
|
IconSize: "16",
|
|
},
|
|
manifest: manifest,
|
|
}
|
|
return &newApp
|
|
}
|
|
func (p *AboutMeApp) GetPath() string {
|
|
return p.path
|
|
}
|
|
func (p *AboutMeApp) GetManifest() apps.ApplicationManifest {
|
|
return p.manifest
|
|
}
|
|
func (p *AboutMeApp) PublicRoutes(route *gin.RouterGroup) {
|
|
route.POST("render", func(ctx *gin.Context) {
|
|
filePath := ctx.Query("path")
|
|
if filePath == "" {
|
|
ctx.Status(http.StatusBadRequest)
|
|
return
|
|
}
|
|
appCtx := appCtx.AppContext{}
|
|
err := ctx.BindJSON(&appCtx)
|
|
if err != nil {
|
|
ctx.Status(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
ginH, err := p.Render(appCtx, filePath)
|
|
if err != nil {
|
|
ctx.JSON(http.StatusInternalServerError, "TODO") //TODO
|
|
return
|
|
}
|
|
if appCtx.IsMobile {
|
|
ctx.HTML(http.StatusOK, "personal-properties/mobile-app.tmpl", ginH)
|
|
} else {
|
|
ctx.HTML(http.StatusOK, "personal-properties/app.tmpl", ginH)
|
|
}
|
|
})
|
|
}
|
|
|
|
func (p *AboutMeApp) PrivateRoutes(router *gin.RouterGroup) {
|
|
p.PublicRoutes(router)
|
|
router.GET("writeMock", func(ctx *gin.Context) {
|
|
err := p.WriteMock()
|
|
if err != nil {
|
|
ctx.Status(http.StatusInternalServerError)
|
|
}
|
|
ctx.Status(http.StatusOK)
|
|
})
|
|
|
|
router.GET("getMock", func(ctx *gin.Context) {
|
|
ctx.JSON(http.StatusOK, PropertiesFileData{
|
|
Header: HeaderIsland{
|
|
Name: "",
|
|
IconPath: "",
|
|
Value1: "",
|
|
Value2: "",
|
|
},
|
|
Links: []HeaderLink{
|
|
{
|
|
Text: "",
|
|
Url: "",
|
|
Icon: "",
|
|
},
|
|
},
|
|
Islands: []Island{
|
|
{
|
|
Header: "qq",
|
|
Properties: []IslandProperty{
|
|
{
|
|
Key: "22",
|
|
KeyComments: []string{
|
|
"45",
|
|
"12",
|
|
},
|
|
Values: []string{
|
|
"aaaa",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
})
|
|
|
|
router.GET("read", func(ctx *gin.Context) {
|
|
filePath := ctx.Query("path")
|
|
if filePath == "" {
|
|
ctx.JSON(http.StatusBadRequest, errormessage.ErrorMessage{
|
|
Message: "File path is empty",
|
|
})
|
|
return
|
|
}
|
|
propData, err := p.Read(filePath)
|
|
if err != nil {
|
|
ctx.JSON(http.StatusInternalServerError, errormessage.ErrorMessage{
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, propData)
|
|
})
|
|
|
|
router.POST("edit", func(ctx *gin.Context) {
|
|
filePath := ctx.Query("path")
|
|
if filePath == "" {
|
|
ctx.JSON(http.StatusBadRequest, errormessage.ErrorMessage{
|
|
Message: "File path is empty",
|
|
})
|
|
return
|
|
}
|
|
|
|
propsData := PropertiesFileData{}
|
|
err := ctx.BindJSON(&propsData)
|
|
if err != nil {
|
|
ctx.JSON(http.StatusBadRequest, errormessage.ErrorMessage{
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
err = p.Edit(filePath, propsData)
|
|
if err != nil {
|
|
ctx.JSON(http.StatusInternalServerError, errormessage.ErrorMessage{
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
ctx.Status(http.StatusOK)
|
|
})
|
|
|
|
}
|
|
|
|
func (p *AboutMeApp) GetAppID() string {
|
|
return p.appID
|
|
}
|
|
|
|
func (p *AboutMeApp) WriteMock() error {
|
|
fileHeader := webfilesystem.FileHeader{
|
|
MongoId: primitive.NewObjectID(),
|
|
Name: "aboutme.props",
|
|
Type: "personal-properties",
|
|
Icon: "",
|
|
}
|
|
fileData := PropertiesFileData{
|
|
Header: HeaderIsland{
|
|
Name: "Test Name",
|
|
IconPath: "test icon path",
|
|
},
|
|
}
|
|
_, _, err := p.fs.Write("/Applications/AboutMe.app/aboutme.props", &fileHeader, fileData)
|
|
return err
|
|
}
|
|
|
|
func (p *AboutMeApp) Read(filePath string) (*PropertiesFileData, error) {
|
|
propData := PropertiesFileData{}
|
|
fileHeader, err := p.fs.Read(path.Join(filePath, "aboutme.props"), &propData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if fileHeader.Type != "personal-properties" {
|
|
return nil, errors.New("wrong file type")
|
|
}
|
|
return &propData, nil
|
|
}
|
|
|
|
func (p *AboutMeApp) Edit(filePath string, blogData PropertiesFileData) error {
|
|
propsPath := path.Join(filePath, "aboutme.props")
|
|
fileHeader, err := p.fs.Read(propsPath, nil)
|
|
if err != nil {
|
|
if err.Error() == "file not found" { //FIXME
|
|
p.WriteMock()
|
|
p.Edit(filePath, blogData)
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
if fileHeader.Type != "personal-properties" {
|
|
return errors.New("wrong file type")
|
|
}
|
|
|
|
err = p.fs.Remove(propsPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fileHeader.MongoId = primitive.NewObjectID()
|
|
_, _, err = p.fs.Write(propsPath, fileHeader, blogData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *AboutMeApp) Render(appCtx appCtx.AppContext, filePath string) (gin.H, error) {
|
|
//Read file from WebFS
|
|
propsData := &PropertiesFileData{}
|
|
filePath = p.fs.RelativeToAbsolute(appCtx, filePath)
|
|
_, err := p.fs.Read(filePath, &propsData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
//Render Markdown strings
|
|
renderedIslands := []RenderedIsland{}
|
|
for _, island := range propsData.Islands {
|
|
newRenderedIsland := RenderedIsland{
|
|
Header: island.Header,
|
|
Properties: []RenderedIslandProperty{},
|
|
}
|
|
for _, property := range island.Properties {
|
|
newRenderedIslandProperty := RenderedIslandProperty{
|
|
Key: property.Key,
|
|
KeyComments: property.KeyComments,
|
|
Values: []template.HTML{},
|
|
}
|
|
for _, value := range property.Values {
|
|
renderedValue := p.mLib.Render([]byte(value), true)
|
|
newRenderedIslandProperty.Values = append(newRenderedIslandProperty.Values, renderedValue)
|
|
}
|
|
newRenderedIsland.Properties = append(newRenderedIsland.Properties, newRenderedIslandProperty)
|
|
}
|
|
renderedIslands = append(renderedIslands, newRenderedIsland)
|
|
}
|
|
|
|
//Make icon path absolute
|
|
propsData.Header.IconPath = p.fs.RelativeToAbsolute(appCtx, propsData.Header.IconPath)
|
|
|
|
absoluteLinks := []HeaderLink{}
|
|
for _, link := range propsData.Links {
|
|
absoluteLinks = append(absoluteLinks, HeaderLink{
|
|
Text: link.Text,
|
|
Url: link.Url,
|
|
Icon: p.fs.RelativeToAbsolute(appCtx, link.Icon),
|
|
})
|
|
}
|
|
|
|
return gin.H{
|
|
"TitleBarConfig": p.titleBarConfig,
|
|
"HeaderProps": propsData.Header,
|
|
"Links": absoluteLinks,
|
|
"Islands": renderedIslands,
|
|
}, nil
|
|
}
|
|
|
|
type PropertiesFileData struct {
|
|
Header HeaderIsland `bson:"header" json:"header"`
|
|
Links []HeaderLink `bson:"links" json:"links"`
|
|
Islands []Island `bson:"islands" json:"islands"` //TODO rename
|
|
}
|
|
|
|
type HeaderIsland struct {
|
|
Name string `bson:"name" json:"name"`
|
|
IconPath string `bson:"iconpath" json:"iconpath"`
|
|
Value1 string `bson:"value1" json:"value1"` //TODO Rename to value
|
|
Value2 string `bson:"value2" json:"value2"`
|
|
}
|
|
|
|
type HeaderLink struct {
|
|
Text string `bson:"text" json:"text"`
|
|
Url string `bson:"url" json:"url"`
|
|
Icon string `bson:"icon" json:"icon"`
|
|
}
|
|
|
|
type Island struct {
|
|
Header string `bson:"header" json:"header"`
|
|
Properties []IslandProperty `bson:"properties" json:"properties"`
|
|
}
|
|
|
|
type IslandProperty struct {
|
|
Key string `bson:"key" json:"key"`
|
|
KeyComments []string `bson:"keycomment" json:"keycomment"`
|
|
Values []string `bson:"values" json:"values"`
|
|
}
|
|
|
|
type RenderedIsland struct {
|
|
Header string
|
|
Properties []RenderedIslandProperty
|
|
}
|
|
|
|
type RenderedIslandProperty struct {
|
|
Key string
|
|
KeyComments []string
|
|
Values []template.HTML
|
|
}
|
|
|
|
// type Value struct {
|
|
// Blocks []ValueBlock
|
|
// }
|
|
|
|
// type ValueBlock struct {
|
|
// Type string
|
|
// Data string
|
|
// }
|