From b0d9631559d204252380d38d6c2a517472dfcc8c Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 24 Nov 2023 11:30:18 +0800 Subject: [PATCH 1/4] [add] sui editor render with mock request --- sui/api/process.go | 11 +------ sui/core/build.go | 31 +++++++++++++------ sui/core/editor.go | 24 +++++--------- sui/core/interfaces.go | 2 +- sui/core/page.go | 14 ++++++++- sui/core/parser.go | 43 +++++++++++++++++++++++++- sui/core/request.go | 16 ++++++++++ sui/core/types.go | 22 +++++++------ sui/storages/local/page_render_test.go | 3 +- 9 files changed, 115 insertions(+), 51 deletions(-) diff --git a/sui/api/process.go b/sui/api/process.go index a751c85c05..0f0a0dee90 100644 --- a/sui/api/process.go +++ b/sui/api/process.go @@ -655,16 +655,7 @@ func EditorRender(process *process.Process) interface{} { exception.New(err.Error(), 500).Throw() } - // Request data - 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) + res, err := page.EditorRender() if err != nil { exception.New(err.Error(), 500).Throw() } diff --git a/sui/core/build.go b/sui/core/build.go index f1dd058250..f9dbfdf265 100644 --- a/sui/core/build.go +++ b/sui/core/build.go @@ -11,7 +11,7 @@ import ( func (page *Page) Build(option *BuildOption) (*goquery.Document, []string, error) { warnings := []string{} - html, err := page.BuildHTML(option.AssetRoot) + html, err := page.BuildHTML(option) if err != nil { warnings = append(warnings, err.Error()) } @@ -23,14 +23,14 @@ func (page *Page) Build(option *BuildOption) (*goquery.Document, []string, error } // Add Style - style, err := page.BuildStyle(option.AssetRoot) + style, err := page.BuildStyle(option) if err != nil { warnings = append(warnings, err.Error()) } doc.Selection.Find("head").AppendHtml(style) // Add Script - script, err := page.BuildScript(option.AssetRoot) + script, err := page.BuildScript(option) if err != nil { warnings = append(warnings, err.Error()) } @@ -39,15 +39,18 @@ func (page *Page) Build(option *BuildOption) (*goquery.Document, []string, error } // BuildHTML build the html -func (page *Page) BuildHTML(assetRoot string) (string, error) { +func (page *Page) BuildHTML(option *BuildOption) (string, error) { html := string(page.Document) if page.Codes.HTML.Code != "" { html = strings.Replace(html, "{{ __page }}", page.Codes.HTML.Code, 1) } - code := strings.ReplaceAll(html, "@assets", assetRoot) - res, err := page.CompileHTML([]byte(code), false) + if !option.IgnoreAssetRoot { + html = strings.ReplaceAll(html, "@assets", option.AssetRoot) + } + + res, err := page.CompileHTML([]byte(html), false) if err != nil { return "", err } @@ -56,12 +59,16 @@ func (page *Page) BuildHTML(assetRoot string) (string, error) { } // 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 == "" { 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) if err != nil { return "", err @@ -71,7 +78,7 @@ func (page *Page) BuildStyle(assetRoot string) (string, error) { } // 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 == "" { return "", nil @@ -86,7 +93,11 @@ func (page *Page) BuildScript(assetRoot string) (string, error) { return fmt.Sprintf("\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) if err != nil { return "", err diff --git a/sui/core/editor.go b/sui/core/editor.go index b905642bf6..0821fa031d 100644 --- a/sui/core/editor.go +++ b/sui/core/editor.go @@ -5,7 +5,7 @@ import ( ) // EditorRender render HTML for the editor -func (page *Page) EditorRender(request *Request) (*ResponseEditorRender, error) { +func (page *Page) EditorRender() (*ResponseEditorRender, error) { res := &ResponseEditorRender{ HTML: "", @@ -32,27 +32,16 @@ func (page *Page) EditorRender(request *Request) (*ResponseEditorRender, error) } res.Styles = append(res.Styles, styles...) - // // Page Styles - // if page.Codes.CSS.Code != "" { - // 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 the page + request := NewRequestMock(page.Config.Mock) // Render tools // res.Scripts = append(res.Scripts, filepath.Join("@assets", "__render.js")) // res.Styles = append(res.Styles, filepath.Join("@assets", "__render.css")) doc, warnings, err := page.Build(&BuildOption{ - SSR: true, - AssetRoot: request.AssetRoot, + SSR: true, + IgnoreAssetRoot: true, }) if err != nil { @@ -91,7 +80,8 @@ func (res *ResponseEditorRender) Render(data map[string]interface{}) error { } var err error - parser := NewTemplateParser(data, nil) + parser := NewTemplateParser(data, &ParserOption{Editor: true}) + res.HTML, err = parser.Render(res.HTML) if err != nil { return err diff --git a/sui/core/interfaces.go b/sui/core/interfaces.go index 2a03f8f60b..9f090ab1fe 100644 --- a/sui/core/interfaces.go +++ b/sui/core/interfaces.go @@ -70,7 +70,7 @@ type IPage interface { SaveTemp(request *RequestSource) error Remove() error - EditorRender(request *Request) (*ResponseEditorRender, error) + EditorRender() (*ResponseEditorRender, error) EditorPageSource() SourceData EditorScriptSource() SourceData EditorStyleSource() SourceData diff --git a/sui/core/page.go b/sui/core/page.go index 04c9b76fc4..c3b7dbb6cb 100644 --- a/sui/core/page.go +++ b/sui/core/page.go @@ -13,13 +13,25 @@ func (page *Page) Get() *Page { // GetConfig get the config func (page *Page) GetConfig() *PageConfig { - if page.Config == nil && page.Codes.CONF.Code != "" { + + if page.Config == nil { + page.Config = &PageConfig{ + Mock: &PageMock{Method: "GET"}, + } + } + + if page.Codes.CONF.Code != "" { var config PageConfig err := jsoniter.Unmarshal([]byte(page.Codes.CONF.Code), &config) if err == nil { page.Config = &config } } + + if page.Config.Mock == nil { + page.Config.Mock = &PageMock{Method: "GET"} + } + return page.Config } diff --git a/sui/core/parser.go b/sui/core/parser.go index be38d2a0f1..22484aab61 100644 --- a/sui/core/parser.go +++ b/sui/core/parser.go @@ -16,6 +16,7 @@ type TemplateParser struct { sequence int // sequence for the rendering errors []error // errors replace map[*goquery.Selection][]*html.Node // replace nodes + option *ParserOption // parser option } // Mapping mapping for the template @@ -26,16 +27,24 @@ type Mapping struct { } // 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 func NewTemplateParser(data Data, option *ParserOption) *TemplateParser { + if option == nil { + option = &ParserOption{} + } + return &TemplateParser{ data: data, mapping: map[string]Mapping{}, sequence: 0, errors: []error{}, replace: map[*goquery.Selection][]*html.Node{}, + option: option, } } @@ -153,6 +162,12 @@ func (parser *TemplateParser) forStatementNode(sel *goquery.Selection) { indexVarName := sel.AttrOr("s:for-index", "index") 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 { // Create a new node @@ -168,6 +183,10 @@ func (parser *TemplateParser) forStatementNode(sel *goquery.Selection) { parser.data[itemVarName] = item parser.data[indexVarName] = idx + if parser.option.Editor { + parser.setSuiAttr(new, "generate", "true") + } + // Process the new node for i := range new.Nodes { parser.parseNode(new.Nodes[i]) @@ -259,7 +278,23 @@ func (parser *TemplateParser) elseStatementNode(sel *goquery.Selection) ([]*goqu 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) { + + if parser.option.Editor { + parser.setSuiAttr(sel, "hide", "true") + return + } + style := sel.AttrOr("style", "") if strings.Contains(style, "display: none") { return @@ -274,6 +309,12 @@ func (parser *TemplateParser) hide(sel *goquery.Selection) { } func (parser *TemplateParser) show(sel *goquery.Selection) { + + if parser.option.Editor { + parser.removeSuiAttr(sel, "hide") + return + } + style := sel.AttrOr("style", "") if !strings.Contains(style, "display: none") { return diff --git a/sui/core/request.go b/sui/core/request.go index 1f18416624..6ed2f53855 100644 --- a/sui/core/request.go +++ b/sui/core/request.go @@ -18,6 +18,22 @@ type Cache struct { // Caches the caches 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 func (r *Request) ExecString(data string) (Data, error) { var res Data diff --git a/sui/core/types.go b/sui/core/types.go index 04a8db22d8..2c138cfd05 100644 --- a/sui/core/types.go +++ b/sui/core/types.go @@ -133,10 +133,11 @@ type MediaSearchResult struct { // BuildOption is the struct for the option option type BuildOption struct { - SSR bool `json:"ssr"` - CDN bool `json:"cdn"` - UpdateAll bool `json:"update_all"` - AssetRoot string `json:"asset_root,omitempty"` + SSR bool `json:"ssr"` + CDN bool `json:"cdn"` + UpdateAll bool `json:"update_all"` + AssetRoot string `json:"asset_root,omitempty"` + IgnoreAssetRoot bool `json:"ignore_asset_root,omitempty"` } // Request is the struct for the request @@ -202,11 +203,14 @@ type BoardSourceData struct { // PageMock is the struct for the request type PageMock struct { - Method string `json:"method,omitempty"` - Params map[string]string `json:"params,omitempty"` - Query map[string][]string `json:"query,omitempty"` - Headers map[string][]string `json:"headers,omitempty"` - Body interface{} `json:"body,omitempty"` + Method string `json:"method,omitempty"` + Referer string `json:"referer,omitempty"` + Payload map[string]interface{} `json:"payload,omitempty"` + Query url.Values `json:"query,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 diff --git a/sui/storages/local/page_render_test.go b/sui/storages/local/page_render_test.go index 8ba566af74..8e4092eb0d 100644 --- a/sui/storages/local/page_render_test.go +++ b/sui/storages/local/page_render_test.go @@ -23,8 +23,7 @@ func TestPageEditorRender(t *testing.T) { t.Fatalf("Page error: %v", err) } - r := &core.Request{Method: "GET"} - res, err := page.EditorRender(r) + res, err := page.EditorRender() if err != nil { t.Fatalf("EditorRender error: %v", err) } From 5bce0f6081415476385a34dc372f58fe722d169b Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 24 Nov 2023 13:55:22 +0800 Subject: [PATCH 2/4] [add] sui template global data support --- sui/api/request.go | 17 ++++- sui/core/compile.go | 19 +++-- sui/core/data.go | 15 ++++ sui/core/editor.go | 8 ++ sui/core/interfaces.go | 1 + sui/core/page.go | 142 ++++++++++++++++++++++++++++++++++-- sui/core/parser.go | 15 +--- sui/core/request.go | 12 +-- sui/core/sui.go | 22 ++++++ sui/core/types.go | 11 ++- sui/storages/local/local.go | 10 +++ sui/storages/local/page.go | 3 + 12 files changed, 241 insertions(+), 34 deletions(-) diff --git a/sui/api/request.go b/sui/api/request.go index bc38f90537..69f16b29f6 100644 --- a/sui/api/request.go +++ b/sui/api/request.go @@ -69,13 +69,20 @@ func (r *Request) Render() (string, int, error) { dataSel.Remove() } + globalDataText := "" + globalDataSel := doc.Find("script[name=global]") + if globalDataSel != nil && globalDataSel.Length() > 0 { + globalDataText = globalDataSel.Text() + globalDataSel.Remove() + } + html, err := doc.Html() if err != nil { return "", 500, fmt.Errorf("parse error, please re-complie the page %s", err.Error()) } // 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) } @@ -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) html, err := parser.Render(c.HTML) if err != nil { diff --git a/sui/core/compile.go b/sui/core/compile.go index c3fc0955f9..d5dd7718e0 100644 --- a/sui/core/compile.go +++ b/sui/core/compile.go @@ -22,6 +22,7 @@ func (page *Page) Compile(option *BuildOption) (string, error) { } } + // Page Data if page.Codes.DATA.Code != "" { doc.Find("body").AppendHtml("\n\n" + `\n") + // Page Global Data + if page.GlobalData != nil && len(page.GlobalData) > 0 { + doc.Find("body").AppendHtml("\n\n" + `\n\n", + ) + } + + // Replace the document + page.Config = page.GetConfig() + page.ReplaceDocument(doc) html, err := doc.Html() if err != nil { diff --git a/sui/core/data.go b/sui/core/data.go index 888765462a..b94136948f 100644 --- a/sui/core/data.go +++ b/sui/core/data.go @@ -9,6 +9,7 @@ import ( "github.com/antonmedv/expr/ast" "github.com/antonmedv/expr/vm" "github.com/yaoapp/gou/process" + "github.com/yaoapp/kun/log" ) var stmtRe = regexp.MustCompile(`\{\{([^}]+)\}\}`) @@ -56,6 +57,20 @@ func (data Data) ExecString(stmt string) (string, error) { 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) { if len(args) < 1 { diff --git a/sui/core/editor.go b/sui/core/editor.go index 0821fa031d..baf1737d9e 100644 --- a/sui/core/editor.go +++ b/sui/core/editor.go @@ -66,6 +66,13 @@ func (page *Page) EditorRender() (*ResponseEditorRender, error) { } res.Render(data) + + // Set the title + res.Config.Rendered = &PageConfigRendered{ + Title: page.RenderTitle(data), + Link: page.Link(request), + } + return res, nil } @@ -92,6 +99,7 @@ func (res *ResponseEditorRender) Render(data map[string]interface{}) error { res.Warnings = append(res.Warnings, err.Error()) } } + return nil } diff --git a/sui/core/interfaces.go b/sui/core/interfaces.go index 9f090ab1fe..2e11dfa146 100644 --- a/sui/core/interfaces.go +++ b/sui/core/interfaces.go @@ -27,6 +27,7 @@ type SUI interface { WithSid(sid string) PublicRootMatcher() *Matcher GetPublic() *Public + PublicRootWithSid(sid string) (string, error) } // ITemplate is the interface for the ITemplate diff --git a/sui/core/page.go b/sui/core/page.go index c3b7dbb6cb..ee2f3d3699 100644 --- a/sui/core/page.go +++ b/sui/core/page.go @@ -1,9 +1,12 @@ package core import ( + "path/filepath" "strings" + "github.com/PuerkitoBio/goquery" jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/kun/log" ) // Get get the base info @@ -14,12 +17,6 @@ func (page *Page) Get() *Page { // GetConfig get the config func (page *Page) GetConfig() *PageConfig { - if page.Config == nil { - page.Config = &PageConfig{ - Mock: &PageMock{Method: "GET"}, - } - } - if page.Codes.CONF.Code != "" { var config PageConfig err := jsoniter.Unmarshal([]byte(page.Codes.CONF.Code), &config) @@ -28,6 +25,12 @@ func (page *Page) GetConfig() *PageConfig { } } + if page.Config == nil { + page.Config = &PageConfig{ + Mock: &PageMock{Method: "GET"}, + } + } + if page.Config.Mock == nil { page.Config.Mock = &PageMock{Method: "GET"} } @@ -35,7 +38,7 @@ func (page *Page) GetConfig() *PageConfig { return page.Config } -// Data get the data +// Data get the data (deprecated) func (page *Page) Data(request *Request) (Data, map[string]interface{}, error) { setting := map[string]interface{}{ @@ -63,5 +66,130 @@ func (page *Page) Exec(request *Request) (Data, error) { 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 } + +// 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 { + continue + } + + paths = append(paths, value) + } + paths = append(paths, part) + } + + return filepath.Join(paths...) +} + +// 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) + } + } + } + +} diff --git a/sui/core/parser.go b/sui/core/parser.go index 22484aab61..84738df8df 100644 --- a/sui/core/parser.go +++ b/sui/core/parser.go @@ -112,17 +112,7 @@ func (parser *TemplateParser) parseElementNode(sel *goquery.Selection) { func (parser *TemplateParser) parseTextNode(node *html.Node) { parser.sequence = parser.sequence + 1 - hasStmt := false - 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) - }) - + res, hasStmt := parser.data.Replace(node.Data) // Bind the variable to the parent node if node.Parent != nil && hasStmt { bindings := strings.TrimSpace(node.Data) @@ -134,8 +124,7 @@ func (parser *TemplateParser) parseTextNode(node *html.Node) { }...) } } - - node.Data = string(res) + node.Data = res } func (parser *TemplateParser) forStatementNode(sel *goquery.Selection) { diff --git a/sui/core/request.go b/sui/core/request.go index 6ed2f53855..5de6585020 100644 --- a/sui/core/request.go +++ b/sui/core/request.go @@ -11,8 +11,9 @@ import ( // Cache the cache type Cache struct { - Data string - HTML string + Data string + Global string + HTML string } // Caches the caches @@ -247,10 +248,11 @@ func (r *Request) parseArgs(args []interface{}) ([]interface{}, error) { } // 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{ - Data: data, - HTML: html, + Data: data, + HTML: html, + Global: global, } return Caches[file] } diff --git a/sui/core/sui.go b/sui/core/sui.go index 7845e4c455..5632740b4d 100644 --- a/sui/core/sui.go +++ b/sui/core/sui.go @@ -45,6 +45,28 @@ func (sui *DSL) PublicRootMatcher() *Matcher { 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 func (sui *DSL) PublicRoot() (string, error) { // Cache the public root diff --git a/sui/core/types.go b/sui/core/types.go index 2c138cfd05..c5ffa74275 100644 --- a/sui/core/types.go +++ b/sui/core/types.go @@ -33,6 +33,7 @@ type Page struct { Path string `json:"-"` Codes SourceCodes `json:"-"` Document []byte `json:"-"` + GlobalData []byte `json:"-"` } // PageTreeNode is the struct for the page tree node @@ -86,6 +87,7 @@ type Template struct { Screenshots []string `json:"screenshots"` Themes []SelectOption `json:"themes"` Document []byte `json:"-"` + GlobalData []byte `json:"-"` } // Theme is the struct for the theme @@ -216,7 +218,8 @@ type PageMock struct { // PageConfig is the struct for the page config type PageConfig struct { 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 @@ -226,6 +229,12 @@ type PageSetting struct { 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 type PageSEO struct { Title string `json:"title,omitempty"` diff --git a/sui/storages/local/local.go b/sui/storages/local/local.go index 3e7c64f5eb..99cb89636b 100644 --- a/sui/storages/local/local.go +++ b/sui/storages/local/local.go @@ -146,6 +146,16 @@ func (local *Local) getTemplate(id string, path string) (*Template, error) { 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 } diff --git a/sui/storages/local/page.go b/sui/storages/local/page.go index 0047bb7127..11b02b68e9 100644 --- a/sui/storages/local/page.go +++ b/sui/storages/local/page.go @@ -374,6 +374,9 @@ func (page *Page) Load() error { // Set the page document page.Document = page.tmpl.Document + + // Set the page global data + page.GlobalData = page.tmpl.GlobalData return nil } From 4bc84c362372be70f27b42c8e34a981e680ad9ca Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 24 Nov 2023 14:06:14 +0800 Subject: [PATCH 3/4] [_] --- sui/core/page.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sui/core/page.go b/sui/core/page.go index ee2f3d3699..4086a6156a 100644 --- a/sui/core/page.go +++ b/sui/core/page.go @@ -131,10 +131,12 @@ func (page *Page) Link(r *Request) string { value, has := r.Params[name] if !has { + paths = append(paths, name) continue } paths = append(paths, value) + continue } paths = append(paths, part) } From 26b03fe8a0de45712ac1c4161df136d5275bf414 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 24 Nov 2023 14:09:15 +0800 Subject: [PATCH 4/4] [_] --- sui/core/page.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sui/core/page.go b/sui/core/page.go index 4086a6156a..942ee13348 100644 --- a/sui/core/page.go +++ b/sui/core/page.go @@ -141,7 +141,15 @@ func (page *Page) Link(r *Request) string { paths = append(paths, part) } - return filepath.Join(paths...) + url := filepath.Join(paths...) + if r.Query != nil { + query := r.Query.Encode() + if query != "" { + url = url + "?" + query + } + } + + return url } // ReplaceDocument replace the document