Merge pull request #507 from trheyi/main

[add] sui editor render with mock request and global data support
This commit is contained in:
Max 2023-11-24 14:16:42 +08:00 committed by GitHub
commit 5ab5361e68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 360 additions and 79 deletions

View file

@ -655,16 +655,7 @@ func EditorRender(process *process.Process) interface{} {
exception.New(err.Error(), 500).Throw() exception.New(err.Error(), 500).Throw()
} }
// Request data res, err := page.EditorRender()
urlQuery := url.Values{}
if process.NumOfArgs() > 3 {
if v, ok := process.Args[3].(url.Values); ok {
urlQuery = v
}
}
req := &core.Request{Method: "GET", Query: urlQuery}
res, err := page.EditorRender(req)
if err != nil { if err != nil {
exception.New(err.Error(), 500).Throw() exception.New(err.Error(), 500).Throw()
} }

View file

@ -69,13 +69,20 @@ func (r *Request) Render() (string, int, error) {
dataSel.Remove() dataSel.Remove()
} }
globalDataText := ""
globalDataSel := doc.Find("script[name=global]")
if globalDataSel != nil && globalDataSel.Length() > 0 {
globalDataText = globalDataSel.Text()
globalDataSel.Remove()
}
html, err := doc.Html() html, err := doc.Html()
if err != nil { if err != nil {
return "", 500, fmt.Errorf("parse error, please re-complie the page %s", err.Error()) return "", 500, fmt.Errorf("parse error, please re-complie the page %s", err.Error())
} }
// Save to The Cache // Save to The Cache
c = core.SetCache(r.File, html, dataText) c = core.SetCache(r.File, html, dataText, globalDataText)
log.Trace("The page %s is cached", r.File) log.Trace("The page %s is cached", r.File)
} }
@ -88,6 +95,14 @@ func (r *Request) Render() (string, int, error) {
} }
} }
if c.Global != "" {
global, err := r.Request.ExecString(c.Global)
if err != nil {
return "", 500, fmt.Errorf("global data error, please re-complie the page %s", err.Error())
}
data["$global"] = global
}
parser := core.NewTemplateParser(data, nil) parser := core.NewTemplateParser(data, nil)
html, err := parser.Render(c.HTML) html, err := parser.Render(c.HTML)
if err != nil { if err != nil {

View file

@ -11,7 +11,7 @@ import (
func (page *Page) Build(option *BuildOption) (*goquery.Document, []string, error) { func (page *Page) Build(option *BuildOption) (*goquery.Document, []string, error) {
warnings := []string{} warnings := []string{}
html, err := page.BuildHTML(option.AssetRoot) html, err := page.BuildHTML(option)
if err != nil { if err != nil {
warnings = append(warnings, err.Error()) warnings = append(warnings, err.Error())
} }
@ -23,14 +23,14 @@ func (page *Page) Build(option *BuildOption) (*goquery.Document, []string, error
} }
// Add Style // Add Style
style, err := page.BuildStyle(option.AssetRoot) style, err := page.BuildStyle(option)
if err != nil { if err != nil {
warnings = append(warnings, err.Error()) warnings = append(warnings, err.Error())
} }
doc.Selection.Find("head").AppendHtml(style) doc.Selection.Find("head").AppendHtml(style)
// Add Script // Add Script
script, err := page.BuildScript(option.AssetRoot) script, err := page.BuildScript(option)
if err != nil { if err != nil {
warnings = append(warnings, err.Error()) warnings = append(warnings, err.Error())
} }
@ -39,15 +39,18 @@ func (page *Page) Build(option *BuildOption) (*goquery.Document, []string, error
} }
// BuildHTML build the html // BuildHTML build the html
func (page *Page) BuildHTML(assetRoot string) (string, error) { func (page *Page) BuildHTML(option *BuildOption) (string, error) {
html := string(page.Document) html := string(page.Document)
if page.Codes.HTML.Code != "" { if page.Codes.HTML.Code != "" {
html = strings.Replace(html, "{{ __page }}", page.Codes.HTML.Code, 1) html = strings.Replace(html, "{{ __page }}", page.Codes.HTML.Code, 1)
} }
code := strings.ReplaceAll(html, "@assets", assetRoot) if !option.IgnoreAssetRoot {
res, err := page.CompileHTML([]byte(code), false) html = strings.ReplaceAll(html, "@assets", option.AssetRoot)
}
res, err := page.CompileHTML([]byte(html), false)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -56,12 +59,16 @@ func (page *Page) BuildHTML(assetRoot string) (string, error) {
} }
// BuildStyle build the style // BuildStyle build the style
func (page *Page) BuildStyle(assetRoot string) (string, error) { func (page *Page) BuildStyle(option *BuildOption) (string, error) {
if page.Codes.CSS.Code == "" { if page.Codes.CSS.Code == "" {
return "", nil return "", nil
} }
code := strings.ReplaceAll(page.Codes.CSS.Code, "@assets", assetRoot) code := page.Codes.CSS.Code
if !option.IgnoreAssetRoot {
code = strings.ReplaceAll(page.Codes.CSS.Code, "@assets", option.AssetRoot)
}
res, err := page.CompileCSS([]byte(code), false) res, err := page.CompileCSS([]byte(code), false)
if err != nil { if err != nil {
return "", err return "", err
@ -71,7 +78,7 @@ func (page *Page) BuildStyle(assetRoot string) (string, error) {
} }
// BuildScript build the script // BuildScript build the script
func (page *Page) BuildScript(assetRoot string) (string, error) { func (page *Page) BuildScript(option *BuildOption) (string, error) {
if page.Codes.JS.Code == "" && page.Codes.TS.Code == "" { if page.Codes.JS.Code == "" && page.Codes.TS.Code == "" {
return "", nil return "", nil
@ -86,7 +93,11 @@ func (page *Page) BuildScript(assetRoot string) (string, error) {
return fmt.Sprintf("<script>\n%s\n</script>\n", res), nil return fmt.Sprintf("<script>\n%s\n</script>\n", res), nil
} }
code := strings.ReplaceAll(page.Codes.JS.Code, "@assets", assetRoot) code := page.Codes.JS.Code
if !option.IgnoreAssetRoot {
code = strings.ReplaceAll(page.Codes.JS.Code, "@assets", option.AssetRoot)
}
res, err := page.CompileJS([]byte(code), false) res, err := page.CompileJS([]byte(code), false)
if err != nil { if err != nil {
return "", err return "", err

View file

@ -22,6 +22,7 @@ func (page *Page) Compile(option *BuildOption) (string, error) {
} }
} }
// Page Data
if page.Codes.DATA.Code != "" { if page.Codes.DATA.Code != "" {
doc.Find("body").AppendHtml("\n\n" + `<script name="data" type="json">` + "\n" + doc.Find("body").AppendHtml("\n\n" + `<script name="data" type="json">` + "\n" +
page.Codes.DATA.Code + page.Codes.DATA.Code +
@ -29,13 +30,17 @@ func (page *Page) Compile(option *BuildOption) (string, error) {
) )
} }
// // add the route data // Page Global Data
// doc.Find("body").AppendHtml(`<script name="route" type="json">` + "\n" + if page.GlobalData != nil && len(page.GlobalData) > 0 {
// fmt.Sprintf( doc.Find("body").AppendHtml("\n\n" + `<script name="global" type="json">` + "\n" +
// `{"sui": "%s", "template": "%s", "route": "%s"}`, string(page.GlobalData) +
// page.SuiID, page.TemplateID, page.Route, "\n</script>\n\n",
// ) + )
// "\n</script>\n") }
// Replace the document
page.Config = page.GetConfig()
page.ReplaceDocument(doc)
html, err := doc.Html() html, err := doc.Html()
if err != nil { if err != nil {

View file

@ -9,6 +9,7 @@ import (
"github.com/antonmedv/expr/ast" "github.com/antonmedv/expr/ast"
"github.com/antonmedv/expr/vm" "github.com/antonmedv/expr/vm"
"github.com/yaoapp/gou/process" "github.com/yaoapp/gou/process"
"github.com/yaoapp/kun/log"
) )
var stmtRe = regexp.MustCompile(`\{\{([^}]+)\}\}`) var stmtRe = regexp.MustCompile(`\{\{([^}]+)\}\}`)
@ -56,6 +57,20 @@ func (data Data) ExecString(stmt string) (string, error) {
return fmt.Sprintf("%v", res), nil return fmt.Sprintf("%v", res), nil
} }
// Replace replace the statement
func (data Data) Replace(value string) (string, bool) {
hasStmt := false
res := stmtRe.ReplaceAllStringFunc(value, func(stmt string) string {
hasStmt = true
res, err := data.ExecString(stmt)
if err != nil {
log.Warn("Replace %s: %s", stmt, err)
}
return res
})
return res, hasStmt
}
func _process(args ...any) (interface{}, error) { func _process(args ...any) (interface{}, error) {
if len(args) < 1 { if len(args) < 1 {

View file

@ -5,7 +5,7 @@ import (
) )
// EditorRender render HTML for the editor // EditorRender render HTML for the editor
func (page *Page) EditorRender(request *Request) (*ResponseEditorRender, error) { func (page *Page) EditorRender() (*ResponseEditorRender, error) {
res := &ResponseEditorRender{ res := &ResponseEditorRender{
HTML: "", HTML: "",
@ -32,27 +32,16 @@ func (page *Page) EditorRender(request *Request) (*ResponseEditorRender, error)
} }
res.Styles = append(res.Styles, styles...) res.Styles = append(res.Styles, styles...)
// // Page Styles // Render the page
// if page.Codes.CSS.Code != "" { request := NewRequestMock(page.Config.Mock)
// res.Styles = append(res.Styles, filepath.Join("@pages", page.Route, page.Name+".css"))
// }
// // Render the HTML with the data
// // Page Scripts
// if page.Codes.JS.Code != "" {
// res.Scripts = append(res.Scripts, filepath.Join("@pages", page.Route, page.Name+".js"))
// }
// if page.Codes.TS.Code != "" {
// res.Scripts = append(res.Scripts, filepath.Join("@pages", page.Route, page.Name+".ts"))
// }
// Render tools // Render tools
// res.Scripts = append(res.Scripts, filepath.Join("@assets", "__render.js")) // res.Scripts = append(res.Scripts, filepath.Join("@assets", "__render.js"))
// res.Styles = append(res.Styles, filepath.Join("@assets", "__render.css")) // res.Styles = append(res.Styles, filepath.Join("@assets", "__render.css"))
doc, warnings, err := page.Build(&BuildOption{ doc, warnings, err := page.Build(&BuildOption{
SSR: true, SSR: true,
AssetRoot: request.AssetRoot, IgnoreAssetRoot: true,
}) })
if err != nil { if err != nil {
@ -77,6 +66,13 @@ func (page *Page) EditorRender(request *Request) (*ResponseEditorRender, error)
} }
res.Render(data) res.Render(data)
// Set the title
res.Config.Rendered = &PageConfigRendered{
Title: page.RenderTitle(data),
Link: page.Link(request),
}
return res, nil return res, nil
} }
@ -91,7 +87,8 @@ func (res *ResponseEditorRender) Render(data map[string]interface{}) error {
} }
var err error var err error
parser := NewTemplateParser(data, nil) parser := NewTemplateParser(data, &ParserOption{Editor: true})
res.HTML, err = parser.Render(res.HTML) res.HTML, err = parser.Render(res.HTML)
if err != nil { if err != nil {
return err return err
@ -102,6 +99,7 @@ func (res *ResponseEditorRender) Render(data map[string]interface{}) error {
res.Warnings = append(res.Warnings, err.Error()) res.Warnings = append(res.Warnings, err.Error())
} }
} }
return nil return nil
} }

View file

@ -27,6 +27,7 @@ type SUI interface {
WithSid(sid string) WithSid(sid string)
PublicRootMatcher() *Matcher PublicRootMatcher() *Matcher
GetPublic() *Public GetPublic() *Public
PublicRootWithSid(sid string) (string, error)
} }
// ITemplate is the interface for the ITemplate // ITemplate is the interface for the ITemplate
@ -70,7 +71,7 @@ type IPage interface {
SaveTemp(request *RequestSource) error SaveTemp(request *RequestSource) error
Remove() error Remove() error
EditorRender(request *Request) (*ResponseEditorRender, error) EditorRender() (*ResponseEditorRender, error)
EditorPageSource() SourceData EditorPageSource() SourceData
EditorScriptSource() SourceData EditorScriptSource() SourceData
EditorStyleSource() SourceData EditorStyleSource() SourceData

View file

@ -1,9 +1,12 @@
package core package core
import ( import (
"path/filepath"
"strings" "strings"
"github.com/PuerkitoBio/goquery"
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/kun/log"
) )
// Get get the base info // Get get the base info
@ -13,17 +16,29 @@ func (page *Page) Get() *Page {
// GetConfig get the config // GetConfig get the config
func (page *Page) GetConfig() *PageConfig { func (page *Page) GetConfig() *PageConfig {
if page.Config == nil && page.Codes.CONF.Code != "" {
if page.Codes.CONF.Code != "" {
var config PageConfig var config PageConfig
err := jsoniter.Unmarshal([]byte(page.Codes.CONF.Code), &config) err := jsoniter.Unmarshal([]byte(page.Codes.CONF.Code), &config)
if err == nil { if err == nil {
page.Config = &config page.Config = &config
} }
} }
if page.Config == nil {
page.Config = &PageConfig{
Mock: &PageMock{Method: "GET"},
}
}
if page.Config.Mock == nil {
page.Config.Mock = &PageMock{Method: "GET"}
}
return page.Config return page.Config
} }
// Data get the data // Data get the data deprecated
func (page *Page) Data(request *Request) (Data, map[string]interface{}, error) { func (page *Page) Data(request *Request) (Data, map[string]interface{}, error) {
setting := map[string]interface{}{ setting := map[string]interface{}{
@ -51,5 +66,140 @@ func (page *Page) Exec(request *Request) (Data, error) {
return nil, err return nil, err
} }
// Global data
if page.GlobalData != nil {
global, err := request.ExecString(string(page.GlobalData))
if err != nil {
return nil, err
}
data["$global"] = global
}
return data, nil return data, nil
} }
// RenderTitle render the title
func (page *Page) RenderTitle(data Data) string {
if page.Config == nil {
return "Untitled"
}
if page.Config.Title != "" {
title, _ := data.Replace(page.Config.Title)
return title
}
return "Untitled"
}
// Link get the link
func (page *Page) Link(r *Request) string {
sui, has := SUIs[page.SuiID]
if !has {
log.Error("[sui] get page link %s not found", page.SuiID)
return ""
}
root, err := sui.PublicRootWithSid(r.Sid)
if err != nil {
log.Error("[sui] get page link %s root error %s", page.SuiID, err.Error())
return ""
}
parts := strings.Split(page.Route, "/")
if len(parts) == 0 {
log.Error("[sui] get page link %s path not found", page.SuiID)
return ""
}
// Get the route
paths := []string{root, "/"}
for _, part := range parts {
if part == "" {
continue
}
if strings.HasPrefix(part, "[") && strings.HasSuffix(part, "]") {
name := strings.TrimSuffix(strings.TrimPrefix(part, "["), "]")
if name == "" {
continue
}
if r == nil {
continue
}
value, has := r.Params[name]
if !has {
paths = append(paths, name)
continue
}
paths = append(paths, value)
continue
}
paths = append(paths, part)
}
url := filepath.Join(paths...)
if r.Query != nil {
query := r.Query.Encode()
if query != "" {
url = url + "?" + query
}
}
return url
}
// ReplaceDocument replace the document
func (page *Page) ReplaceDocument(doc *goquery.Document) {
if page.Config == nil {
return
}
if doc == nil {
return
}
if page.Config.Title != "" {
if doc.Find("title") != nil {
doc.Find("title").SetText(page.Config.Title)
}
}
if page.Config.Description != "" {
if doc.Find("meta[name=description]") != nil {
doc.Find("meta[name=description]").SetAttr("content", page.Config.Description)
}
}
if page.Config.SEO != nil {
if page.Config.SEO.Title != "" {
if doc.Find("meta[property=og:title]") != nil {
doc.Find("meta[property=og:title]").SetAttr("content", page.Config.SEO.Title)
}
}
if page.Config.SEO.Description != "" {
if doc.Find("meta[name=description]") != nil {
doc.Find("meta[name=description]").SetAttr("content", page.Config.SEO.Description)
}
}
if page.Config.SEO.Image != "" {
if doc.Find("meta[property=og:image]") != nil {
doc.Find("meta[property=og:image]").SetAttr("content", page.Config.SEO.Image)
}
}
if page.Config.SEO.URL != "" {
if doc.Find("meta[property=og:url]") != nil {
doc.Find("meta[property=og:url]").SetAttr("content", page.Config.SEO.URL)
}
}
}
}

View file

@ -16,6 +16,7 @@ type TemplateParser struct {
sequence int // sequence for the rendering sequence int // sequence for the rendering
errors []error // errors errors []error // errors
replace map[*goquery.Selection][]*html.Node // replace nodes replace map[*goquery.Selection][]*html.Node // replace nodes
option *ParserOption // parser option
} }
// Mapping mapping for the template // Mapping mapping for the template
@ -26,16 +27,24 @@ type Mapping struct {
} }
// ParserOption parser option // ParserOption parser option
type ParserOption struct{} type ParserOption struct {
Editor bool `json:"editor,omitempty"`
Preview bool `json:"preview,omitempty"`
}
// NewTemplateParser create a new template parser // NewTemplateParser create a new template parser
func NewTemplateParser(data Data, option *ParserOption) *TemplateParser { func NewTemplateParser(data Data, option *ParserOption) *TemplateParser {
if option == nil {
option = &ParserOption{}
}
return &TemplateParser{ return &TemplateParser{
data: data, data: data,
mapping: map[string]Mapping{}, mapping: map[string]Mapping{},
sequence: 0, sequence: 0,
errors: []error{}, errors: []error{},
replace: map[*goquery.Selection][]*html.Node{}, replace: map[*goquery.Selection][]*html.Node{},
option: option,
} }
} }
@ -103,17 +112,7 @@ func (parser *TemplateParser) parseElementNode(sel *goquery.Selection) {
func (parser *TemplateParser) parseTextNode(node *html.Node) { func (parser *TemplateParser) parseTextNode(node *html.Node) {
parser.sequence = parser.sequence + 1 parser.sequence = parser.sequence + 1
hasStmt := false res, hasStmt := parser.data.Replace(node.Data)
res := stmtRe.ReplaceAllFunc([]byte(node.Data), func(stmt []byte) []byte {
hasStmt = true
res, err := parser.data.ExecString(string(stmt))
if err != nil {
parser.errors = append(parser.errors, err)
return []byte(``)
}
return []byte(res)
})
// Bind the variable to the parent node // Bind the variable to the parent node
if node.Parent != nil && hasStmt { if node.Parent != nil && hasStmt {
bindings := strings.TrimSpace(node.Data) bindings := strings.TrimSpace(node.Data)
@ -125,8 +124,7 @@ func (parser *TemplateParser) parseTextNode(node *html.Node) {
}...) }...)
} }
} }
node.Data = res
node.Data = string(res)
} }
func (parser *TemplateParser) forStatementNode(sel *goquery.Selection) { func (parser *TemplateParser) forStatementNode(sel *goquery.Selection) {
@ -153,6 +151,12 @@ func (parser *TemplateParser) forStatementNode(sel *goquery.Selection) {
indexVarName := sel.AttrOr("s:for-index", "index") indexVarName := sel.AttrOr("s:for-index", "index")
itemNodes := []*html.Node{} itemNodes := []*html.Node{}
// Keep the node if the editor is enabled
if parser.option.Editor {
clone := sel.Clone()
itemNodes = append(itemNodes, clone.Nodes...)
}
for idx, item := range items { for idx, item := range items {
// Create a new node // Create a new node
@ -168,6 +172,10 @@ func (parser *TemplateParser) forStatementNode(sel *goquery.Selection) {
parser.data[itemVarName] = item parser.data[itemVarName] = item
parser.data[indexVarName] = idx parser.data[indexVarName] = idx
if parser.option.Editor {
parser.setSuiAttr(new, "generate", "true")
}
// Process the new node // Process the new node
for i := range new.Nodes { for i := range new.Nodes {
parser.parseNode(new.Nodes[i]) parser.parseNode(new.Nodes[i])
@ -259,7 +267,23 @@ func (parser *TemplateParser) elseStatementNode(sel *goquery.Selection) ([]*goqu
return elifNodes, elseNode return elifNodes, elseNode
} }
func (parser *TemplateParser) setSuiAttr(sel *goquery.Selection, key, value string) *goquery.Selection {
key = fmt.Sprintf("data-sui-%s", key)
return sel.SetAttr(key, value)
}
func (parser *TemplateParser) removeSuiAttr(sel *goquery.Selection, key string) *goquery.Selection {
key = fmt.Sprintf("data-sui-%s", key)
return sel.RemoveAttr(key)
}
func (parser *TemplateParser) hide(sel *goquery.Selection) { func (parser *TemplateParser) hide(sel *goquery.Selection) {
if parser.option.Editor {
parser.setSuiAttr(sel, "hide", "true")
return
}
style := sel.AttrOr("style", "") style := sel.AttrOr("style", "")
if strings.Contains(style, "display: none") { if strings.Contains(style, "display: none") {
return return
@ -274,6 +298,12 @@ func (parser *TemplateParser) hide(sel *goquery.Selection) {
} }
func (parser *TemplateParser) show(sel *goquery.Selection) { func (parser *TemplateParser) show(sel *goquery.Selection) {
if parser.option.Editor {
parser.removeSuiAttr(sel, "hide")
return
}
style := sel.AttrOr("style", "") style := sel.AttrOr("style", "")
if !strings.Contains(style, "display: none") { if !strings.Contains(style, "display: none") {
return return

View file

@ -11,13 +11,30 @@ import (
// Cache the cache // Cache the cache
type Cache struct { type Cache struct {
Data string Data string
HTML string Global string
HTML string
} }
// Caches the caches // Caches the caches
var Caches = map[string]*Cache{} var Caches = map[string]*Cache{}
// NewRequestMock is the constructor for Request.
func NewRequestMock(mock *PageMock) *Request {
if mock == nil {
mock = &PageMock{Method: "GET"}
}
return &Request{
Method: mock.Method,
Query: mock.Query,
Body: mock.Body,
Payload: mock.Payload,
Referer: mock.Referer,
Headers: mock.Headers,
Params: mock.Params,
}
}
// ExecString get the data // ExecString get the data
func (r *Request) ExecString(data string) (Data, error) { func (r *Request) ExecString(data string) (Data, error) {
var res Data var res Data
@ -231,10 +248,11 @@ func (r *Request) parseArgs(args []interface{}) ([]interface{}, error) {
} }
// SetCache set the cache // SetCache set the cache
func SetCache(file string, html string, data string) *Cache { func SetCache(file string, html string, data string, global string) *Cache {
Caches[file] = &Cache{ Caches[file] = &Cache{
Data: data, Data: data,
HTML: html, HTML: html,
Global: global,
} }
return Caches[file] return Caches[file]
} }

View file

@ -45,6 +45,28 @@ func (sui *DSL) PublicRootMatcher() *Matcher {
return &Matcher{Exact: pub.Root} return &Matcher{Exact: pub.Root}
} }
// PublicRootWithSid returns the public root path with sid
func (sui *DSL) PublicRootWithSid(sid string) (string, error) {
ss := session.Global().ID(sid)
data, err := ss.Dump()
if err != nil {
return "", err
}
vars := map[string]interface{}{"$session": data}
var root = sui.Public.Root
dot := maps.Of(vars).Dot()
output := varRe.ReplaceAllStringFunc(root, func(matched string) string {
varName := strings.TrimSpace(matched[2 : len(matched)-2])
if value, ok := dot[varName]; ok {
return fmt.Sprint(value)
}
return "__undefined"
})
return output, nil
}
// PublicRoot returns the public root path // PublicRoot returns the public root path
func (sui *DSL) PublicRoot() (string, error) { func (sui *DSL) PublicRoot() (string, error) {
// Cache the public root // Cache the public root

View file

@ -33,6 +33,7 @@ type Page struct {
Path string `json:"-"` Path string `json:"-"`
Codes SourceCodes `json:"-"` Codes SourceCodes `json:"-"`
Document []byte `json:"-"` Document []byte `json:"-"`
GlobalData []byte `json:"-"`
} }
// PageTreeNode is the struct for the page tree node // PageTreeNode is the struct for the page tree node
@ -86,6 +87,7 @@ type Template struct {
Screenshots []string `json:"screenshots"` Screenshots []string `json:"screenshots"`
Themes []SelectOption `json:"themes"` Themes []SelectOption `json:"themes"`
Document []byte `json:"-"` Document []byte `json:"-"`
GlobalData []byte `json:"-"`
} }
// Theme is the struct for the theme // Theme is the struct for the theme
@ -133,10 +135,11 @@ type MediaSearchResult struct {
// BuildOption is the struct for the option option // BuildOption is the struct for the option option
type BuildOption struct { type BuildOption struct {
SSR bool `json:"ssr"` SSR bool `json:"ssr"`
CDN bool `json:"cdn"` CDN bool `json:"cdn"`
UpdateAll bool `json:"update_all"` UpdateAll bool `json:"update_all"`
AssetRoot string `json:"asset_root,omitempty"` AssetRoot string `json:"asset_root,omitempty"`
IgnoreAssetRoot bool `json:"ignore_asset_root,omitempty"`
} }
// Request is the struct for the request // Request is the struct for the request
@ -202,17 +205,21 @@ type BoardSourceData struct {
// PageMock is the struct for the request // PageMock is the struct for the request
type PageMock struct { type PageMock struct {
Method string `json:"method,omitempty"` Method string `json:"method,omitempty"`
Params map[string]string `json:"params,omitempty"` Referer string `json:"referer,omitempty"`
Query map[string][]string `json:"query,omitempty"` Payload map[string]interface{} `json:"payload,omitempty"`
Headers map[string][]string `json:"headers,omitempty"` Query url.Values `json:"query,omitempty"`
Body interface{} `json:"body,omitempty"` Params map[string]string `json:"params,omitempty"`
Headers url.Values `json:"headers,omitempty"`
Body interface{} `json:"body,omitempty"`
Sid string `json:"sid,omitempty"`
} }
// PageConfig is the struct for the page config // PageConfig is the struct for the page config
type PageConfig struct { type PageConfig struct {
PageSetting `json:",omitempty"` PageSetting `json:",omitempty"`
Mock *PageMock `json:"mock,omitempty"` Mock *PageMock `json:"mock,omitempty"`
Rendered *PageConfigRendered `json:"rendered,omitempty"`
} }
// PageSetting is the struct for the page setting // PageSetting is the struct for the page setting
@ -222,6 +229,12 @@ type PageSetting struct {
SEO *PageSEO `json:"seo,omitempty"` SEO *PageSEO `json:"seo,omitempty"`
} }
// PageConfigRendered is the struct for the page config rendered
type PageConfigRendered struct {
Title string `json:"title,omitempty"`
Link string `json:"link,omitempty"`
}
// PageSEO is the struct for the page seo // PageSEO is the struct for the page seo
type PageSEO struct { type PageSEO struct {
Title string `json:"title,omitempty"` Title string `json:"title,omitempty"`

View file

@ -146,6 +146,16 @@ func (local *Local) getTemplate(id string, path string) (*Template, error) {
tmpl.Document = documentBytes tmpl.Document = documentBytes
} }
// load the __data.json
dataFile := filepath.Join(path, "__data.json")
if local.fs.IsFile(dataFile) {
dataBytes, err := local.fs.ReadFile(dataFile)
if err != nil {
return nil, err
}
tmpl.GlobalData = dataBytes
}
return &tmpl, nil return &tmpl, nil
} }

View file

@ -374,6 +374,9 @@ func (page *Page) Load() error {
// Set the page document // Set the page document
page.Document = page.tmpl.Document page.Document = page.tmpl.Document
// Set the page global data
page.GlobalData = page.tmpl.GlobalData
return nil return nil
} }

View file

@ -23,8 +23,7 @@ func TestPageEditorRender(t *testing.T) {
t.Fatalf("Page error: %v", err) t.Fatalf("Page error: %v", err)
} }
r := &core.Request{Method: "GET"} res, err := page.EditorRender()
res, err := page.EditorRender(r)
if err != nil { if err != nil {
t.Fatalf("EditorRender error: %v", err) t.Fatalf("EditorRender error: %v", err)
} }