From 8a7903db1e4064219a4ccc972e8e65c0f4666e68 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 29 Sep 2023 12:20:20 +0800 Subject: [PATCH 1/2] [add] page api and change api route --- sui/api/api.go | 37 ++++++++++----- sui/api/process.go | 84 +++++++++++++++++++++++++-------- sui/api/process_test.go | 56 +++++++++++++++++++--- sui/core/interfaces.go | 1 + sui/core/types.go | 8 ++++ sui/storages/local/page.go | 84 +++++++++++++++++++++++++++++++++ sui/storages/local/page_test.go | 32 +++++++++++++ 7 files changed, 264 insertions(+), 38 deletions(-) diff --git a/sui/api/api.go b/sui/api/api.go index 0d57fc5d3a..8256080fe8 100644 --- a/sui/api/api.go +++ b/sui/api/api.go @@ -25,47 +25,60 @@ var dsl = []byte(` }, { - "path": "/:id/template/:template_id/locale", + "path": "/:id/locale/:template_id", "method": "GET", - "process": "sui.Template.Locale.Get", + "process": "sui.Locale.Get", "in": ["$param.id", "$param.template_id"], "out": { "status": 200, "type": "application/json" } },{ - "path": "/:id/template/:template_id/theme", + "path": "/:id/theme/:template_id", "method": "GET", - "process": "sui.Template.Theme.Get", + "process": "sui.Theme.Get", "in": ["$param.id", "$param.template_id"], "out": { "status": 200, "type": "application/json" } }, { - "path": "/:id/template/:template_id/block", + "path": "/:id/block/:template_id", "method": "GET", - "process": "sui.Template.Block.Get", + "process": "sui.Block.Get", "in": ["$param.id", "$param.template_id"], "out": { "status": 200, "type": "application/json" } },{ - "path": "/:id/template/:template_id/block/:block_id", + "path": "/:id/block/:template_id/:block_id", "method": "GET", - "process": "sui.Template.Block.Find", + "process": "sui.Block.Find", "in": ["$param.id", "$param.template_id", "$param.block_id"], "out": { "status": 200, "type": "text/javascript" } }, { - "path": "/:id/template/:template_id/component", + "path": "/:id/component/:template_id", "method": "GET", - "process": "sui.Template.Component.Get", + "process": "sui.Component.Get", "in": ["$param.id", "$param.template_id"], "out": { "status": 200, "type": "application/json" } },{ - "path": "/:id/template/:template_id/component/:component_id", + "path": "/:id/component/:template_id/:component_id", "method": "GET", - "process": "sui.Template.Component.Find", + "process": "sui.Component.Find", "in": ["$param.id", "$param.template_id", "$param.component_id"], "out": { "status": 200, "type": "text/javascript" } }, + { + "path": "/:id/page/:template_id/*route", + "method": "GET", + "process": "sui.Page.Get", + "in": ["$param.id", "$param.template_id", "$param.route"], + "out": { "status": 200, "type": "application/json" } + },{ + "path": "/:id/page/tree/:template_id/*route", + "method": "GET", + "process": "sui.Page.Tree", + "in": ["$param.id", "$param.template_id", "$param.route"], + "out": { "status": 200, "type": "application/json" } + }, { "path": "/:id/editor/render/:template_id/*route", diff --git a/sui/api/process.go b/sui/api/process.go index 78e9a73eee..18d28e2bdc 100644 --- a/sui/api/process.go +++ b/sui/api/process.go @@ -8,14 +8,20 @@ import ( func init() { process.RegisterGroup("sui", map[string]process.Handler{ - "template.get": TemplateGet, - "template.find": TemplateFind, - "template.locale.get": TemplateLocaleGet, - "template.theme.get": TemplateThemeGet, - "template.block.get": TemplateBlockGet, - "template.block.find": TemplateBlockFind, - "template.component.get": TemplateComponentGet, - "template.component.find": TemplateComponentFind, + "template.get": TemplateGet, + "template.find": TemplateFind, + + "locale.get": LocaleGet, + "theme.get": ThemeGet, + + "block.get": BlockGet, + "block.find": BlockFind, + + "component.get": ComponentGet, + "component.find": ComponentFind, + + "page.tree": PageTree, + "page.get": PageGet, "editor.render": EditorRender, "editor.source": EditorSource, @@ -48,8 +54,8 @@ func TemplateFind(process *process.Process) interface{} { return template } -// TemplateLocaleGet handle the find Template request -func TemplateLocaleGet(process *process.Process) interface{} { +// LocaleGet handle the find Template request +func LocaleGet(process *process.Process) interface{} { process.ValidateArgNums(2) sui := get(process) @@ -61,8 +67,8 @@ func TemplateLocaleGet(process *process.Process) interface{} { return template.Locales() } -// TemplateThemeGet handle the find Template request -func TemplateThemeGet(process *process.Process) interface{} { +// ThemeGet handle the find Template request +func ThemeGet(process *process.Process) interface{} { process.ValidateArgNums(2) sui := get(process) @@ -74,8 +80,8 @@ func TemplateThemeGet(process *process.Process) interface{} { return template.Themes() } -// TemplateBlockGet handle the find Template request -func TemplateBlockGet(process *process.Process) interface{} { +// BlockGet handle the find Template request +func BlockGet(process *process.Process) interface{} { process.ValidateArgNums(2) sui := get(process) @@ -93,8 +99,8 @@ func TemplateBlockGet(process *process.Process) interface{} { return blocks } -// TemplateBlockFind handle the find Template request -func TemplateBlockFind(process *process.Process) interface{} { +// BlockFind handle the find Template request +func BlockFind(process *process.Process) interface{} { process.ValidateArgNums(3) sui := get(process) @@ -114,8 +120,8 @@ func TemplateBlockFind(process *process.Process) interface{} { return block.Source() } -// TemplateComponentGet handle the find Template request -func TemplateComponentGet(process *process.Process) interface{} { +// ComponentGet handle the find Template request +func ComponentGet(process *process.Process) interface{} { process.ValidateArgNums(2) sui := get(process) @@ -133,8 +139,8 @@ func TemplateComponentGet(process *process.Process) interface{} { return components } -// TemplateComponentFind handle the find Template request -func TemplateComponentFind(process *process.Process) interface{} { +// ComponentFind handle the find Template request +func ComponentFind(process *process.Process) interface{} { process.ValidateArgNums(3) sui := get(process) @@ -153,6 +159,44 @@ func TemplateComponentFind(process *process.Process) interface{} { return component.Source() } +// PageTree handle the find Template request +func PageTree(process *process.Process) interface{} { + process.ValidateArgNums(2) + + sui := get(process) + templateID := process.ArgsString(1) + + tmpl, err := sui.GetTemplate(templateID) + if err != nil { + exception.New(err.Error(), 500).Throw() + } + + tree, err := tmpl.PageTree() + if err != nil { + exception.New(err.Error(), 500).Throw() + } + return tree +} + +// PageGet handle the find Template request +func PageGet(process *process.Process) interface{} { + process.ValidateArgNums(2) + + sui := get(process) + templateID := process.ArgsString(1) + + tmpl, err := sui.GetTemplate(templateID) + if err != nil { + exception.New(err.Error(), 500).Throw() + } + + tree, err := tmpl.Pages() + if err != nil { + exception.New(err.Error(), 500).Throw() + } + return tree +} + // EditorRender handle the render page request func EditorRender(process *process.Process) interface{} { process.ValidateArgNums(3) diff --git a/sui/api/process_test.go b/sui/api/process_test.go index 066c441384..86dfb3e5ca 100644 --- a/sui/api/process_test.go +++ b/sui/api/process_test.go @@ -53,7 +53,7 @@ func TestTemplateLocaleGet(t *testing.T) { defer clean() // test demo - p, err := process.Of("sui.template.locale.get", "demo", "tech-blue") + p, err := process.Of("sui.locale.get", "demo", "tech-blue") if err != nil { t.Fatal(err) } @@ -75,7 +75,7 @@ func TestTemplateThemeGet(t *testing.T) { defer clean() // test demo - p, err := process.Of("sui.template.theme.get", "demo", "tech-blue") + p, err := process.Of("sui.theme.get", "demo", "tech-blue") if err != nil { t.Fatal(err) } @@ -96,7 +96,7 @@ func TestTemplateBlockGet(t *testing.T) { defer clean() // test demo - p, err := process.Of("sui.template.block.get", "demo", "tech-blue") + p, err := process.Of("sui.block.get", "demo", "tech-blue") if err != nil { t.Fatal(err) } @@ -119,7 +119,7 @@ func TestTemplateBlockFind(t *testing.T) { defer clean() // test demo - p, err := process.Of("sui.template.block.find", "demo", "tech-blue", "ColumnsTwo") + p, err := process.Of("sui.block.find", "demo", "tech-blue", "ColumnsTwo") if err != nil { t.Fatal(err) } @@ -138,7 +138,7 @@ func TestTemplateComponentGet(t *testing.T) { defer clean() // test demo - p, err := process.Of("sui.template.component.get", "demo", "tech-blue") + p, err := process.Of("sui.component.get", "demo", "tech-blue") if err != nil { t.Fatal(err) } @@ -160,7 +160,7 @@ func TestTemplateComponentFind(t *testing.T) { defer clean() // test demo - p, err := process.Of("sui.template.component.find", "demo", "tech-blue", "Box") + p, err := process.Of("sui.component.find", "demo", "tech-blue", "Box") if err != nil { t.Fatal(err) } @@ -174,6 +174,50 @@ func TestTemplateComponentFind(t *testing.T) { assert.Contains(t, res.(string), "window.component__Box=") } +func TestTemplatePageTree(t *testing.T) { + load(t) + defer clean() + + // test demo + p, err := process.Of("sui.page.tree", "demo", "tech-blue") + if err != nil { + t.Fatal(err) + } + + res, err := p.Exec() + if err != nil { + t.Fatal(err) + } + + assert.IsType(t, []*core.PageTreeNode{}, res) + assert.Equal(t, 5, len(res.([]*core.PageTreeNode))) + assert.Equal(t, "error", res.([]*core.PageTreeNode)[0].Name) + assert.Equal(t, "index", res.([]*core.PageTreeNode)[1].Name) +} + +func TestTemplatePageGet(t *testing.T) { + load(t) + defer clean() + + // test demo + p, err := process.Of("sui.page.get", "demo", "tech-blue", "/index/[invite]") + if err != nil { + t.Fatal(err) + } + + res, err := p.Exec() + if err != nil { + t.Fatal(err) + } + + pages := res.([]core.IPage) + assert.IsType(t, []core.IPage{}, pages) + assert.Equal(t, 8, len(pages)) + for _, page := range pages { + assert.IsType(t, &local.Page{}, page) + } +} + func TestEditorRender(t *testing.T) { load(t) defer clean() diff --git a/sui/core/interfaces.go b/sui/core/interfaces.go index 1104877e4f..ea51495233 100644 --- a/sui/core/interfaces.go +++ b/sui/core/interfaces.go @@ -13,6 +13,7 @@ type SUI interface { // ITemplate is the interface for the ITemplate type ITemplate interface { Pages() ([]IPage, error) + PageTree() ([]*PageTreeNode, error) Page(route string) (IPage, error) Blocks() ([]IBlock, error) diff --git a/sui/core/types.go b/sui/core/types.go index a0b1f5b1b6..67dac14846 100644 --- a/sui/core/types.go +++ b/sui/core/types.go @@ -17,6 +17,14 @@ type Page struct { Document []byte `json:"-"` } +// PageTreeNode is the struct for the page tree node +type PageTreeNode struct { + Name string `json:"name,omitempty"` + IsDir bool `json:"is_dir,omitempty"` + Children []*PageTreeNode `json:"children,omitempty"` + IPage IPage `json:"page,omitempty"` +} + // Component is the struct for the component type Component struct { ID string `json:"id"` diff --git a/sui/storages/local/page.go b/sui/storages/local/page.go index 0e66ac81dd..ed3fa08c25 100644 --- a/sui/storages/local/page.go +++ b/sui/storages/local/page.go @@ -40,6 +40,90 @@ func (tmpl *Template) Pages() ([]core.IPage, error) { return pages, nil } +// PageTree gets the page tree. +func (tmpl *Template) PageTree() ([]*core.PageTreeNode, error) { + exts := []string{"*.sui", "*.html", "*.htm", "*.page"} + rootNode := &core.PageTreeNode{ + Name: tmpl.Name, + IsDir: true, + Children: []*core.PageTreeNode{}, // 初始为空的切片 + } + + tmpl.local.fs.Walk(tmpl.Root, func(root, file string, isdir bool) error { + name := filepath.Base(file) + relPath := file + + if isdir { + if strings.HasPrefix(name, "__") { + return filepath.SkipDir + } + + // Create directory nodes in the tree structure. + currentDir := rootNode + dirs := strings.Split(relPath, string(filepath.Separator)) + + for _, dir := range dirs { + if dir == "" { + continue + } + + // Check if the directory node already exists. + var found bool + for _, child := range currentDir.Children { + if child.Name == dir { + currentDir = child + found = true + break + } + } + // If not found, create a new directory node. + if !found { + newDir := &core.PageTreeNode{ + Name: dir, + IsDir: true, + Children: []*core.PageTreeNode{}, + } + currentDir.Children = append(currentDir.Children, newDir) + currentDir = newDir + } + } + return nil + } + + if strings.HasPrefix(name, "__") { + return nil + } + + page, err := tmpl.getPageFrom(file) + if err != nil { + log.Error("Get page error: %v", err) + return err + } + + // Attach the page to the appropriate directory node. + dirs := strings.Split(relPath, string(filepath.Separator)) + currentDir := rootNode + for _, dir := range dirs { + for _, child := range currentDir.Children { + if child.Name == dir { + currentDir = child + break + } + } + } + + currentDir.Children = append(currentDir.Children, &core.PageTreeNode{ + Name: tmpl.getPageBase(currentDir.Name), + IsDir: false, + IPage: page, + }) + + return nil + }, exts...) + + return rootNode.Children, nil +} + // Page get the page func (tmpl *Template) Page(route string) (core.IPage, error) { path := tmpl.getPagePath(route) diff --git a/sui/storages/local/page_test.go b/sui/storages/local/page_test.go index 2c13da4b60..ff32262256 100644 --- a/sui/storages/local/page_test.go +++ b/sui/storages/local/page_test.go @@ -44,6 +44,38 @@ func TestTemplatePages(t *testing.T) { } } +func TestTemplatePageTree(t *testing.T) { + tests := prepare(t) + defer clean() + + tmpl, err := tests.Demo.GetTemplate("tech-blue") + if err != nil { + t.Fatalf("GetTemplate error: %v", err) + } + + pages, err := tmpl.PageTree() + if err != nil { + t.Fatalf("Pages error: %v", err) + } + + assert.Equal(t, 5, len(pages)) + assert.Equal(t, "error", pages[0].Name) + assert.Equal(t, true, pages[0].IsDir) + assert.Equal(t, "error", pages[0].Children[0].Name) + assert.Equal(t, "/error", pages[0].Children[0].IPage.(*Page).Route) + assert.Equal(t, "error", pages[0].Children[0].IPage.(*Page).Name) + + assert.Equal(t, "index", pages[1].Name) + assert.Equal(t, true, pages[1].IsDir) + assert.Equal(t, "[invite]", pages[1].Children[0].Name) + assert.Equal(t, true, pages[1].Children[0].IsDir) + assert.Equal(t, "/index/[invite]", pages[1].Children[0].Children[0].IPage.(*Page).Route) + assert.Equal(t, "[invite]", pages[1].Children[0].Children[0].IPage.(*Page).Name) + assert.Equal(t, "/index", pages[1].Children[1].IPage.(*Page).Route) + assert.Equal(t, "index", pages[1].Children[1].IPage.(*Page).Name) + +} + func TestTemplatePageTS(t *testing.T) { tests := prepare(t) From 1aade992b21b1e16a1e97e6d2627dec2add6732a Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 29 Sep 2023 12:44:05 +0800 Subject: [PATCH 2/2] [add] active & extend support --- sui/api/process.go | 3 ++- sui/core/interfaces.go | 5 ++++- sui/core/page.go | 5 +++++ sui/core/types.go | 2 ++ sui/storages/local/page.go | 16 ++++++++++++---- sui/storages/local/page_test.go | 2 +- 6 files changed, 26 insertions(+), 7 deletions(-) diff --git a/sui/api/process.go b/sui/api/process.go index 18d28e2bdc..890166ebce 100644 --- a/sui/api/process.go +++ b/sui/api/process.go @@ -171,7 +171,8 @@ func PageTree(process *process.Process) interface{} { exception.New(err.Error(), 500).Throw() } - tree, err := tmpl.PageTree() + route := route(process, 2) + tree, err := tmpl.PageTree(route) if err != nil { exception.New(err.Error(), 500).Throw() } diff --git a/sui/core/interfaces.go b/sui/core/interfaces.go index ea51495233..aa017fff84 100644 --- a/sui/core/interfaces.go +++ b/sui/core/interfaces.go @@ -13,7 +13,7 @@ type SUI interface { // ITemplate is the interface for the ITemplate type ITemplate interface { Pages() ([]IPage, error) - PageTree() ([]*PageTreeNode, error) + PageTree(route string) ([]*PageTreeNode, error) Page(route string) (IPage, error) Blocks() ([]IBlock, error) @@ -30,6 +30,9 @@ type ITemplate interface { // IPage is the interface for the page type IPage interface { Load() error + + Get() *Page + EditorRender(request *Request) (*ResponseEditor, error) EditorPageSource() ResponseSource EditorScriptSource() ResponseSource diff --git a/sui/core/page.go b/sui/core/page.go index 68138f2c89..c113f2b8bd 100644 --- a/sui/core/page.go +++ b/sui/core/page.go @@ -1,5 +1,10 @@ package core +// Get get the base info +func (page *Page) Get() *Page { + return page +} + // GetHTML get the html func (page *Page) GetHTML() {} diff --git a/sui/core/types.go b/sui/core/types.go index 67dac14846..f7a36b2ef8 100644 --- a/sui/core/types.go +++ b/sui/core/types.go @@ -23,6 +23,8 @@ type PageTreeNode struct { IsDir bool `json:"is_dir,omitempty"` Children []*PageTreeNode `json:"children,omitempty"` IPage IPage `json:"page,omitempty"` + Expand bool `json:"expand,omitempty"` + Active bool `json:"active,omitempty"` } // Component is the struct for the component diff --git a/sui/storages/local/page.go b/sui/storages/local/page.go index ed3fa08c25..102d979e28 100644 --- a/sui/storages/local/page.go +++ b/sui/storages/local/page.go @@ -41,11 +41,13 @@ func (tmpl *Template) Pages() ([]core.IPage, error) { } // PageTree gets the page tree. -func (tmpl *Template) PageTree() ([]*core.PageTreeNode, error) { +func (tmpl *Template) PageTree(route string) ([]*core.PageTreeNode, error) { + exts := []string{"*.sui", "*.html", "*.htm", "*.page"} rootNode := &core.PageTreeNode{ Name: tmpl.Name, IsDir: true, + Expand: true, Children: []*core.PageTreeNode{}, // 初始为空的切片 } @@ -82,6 +84,7 @@ func (tmpl *Template) PageTree() ([]*core.PageTreeNode, error) { Name: dir, IsDir: true, Children: []*core.PageTreeNode{}, + Expand: true, } currentDir.Children = append(currentDir.Children, newDir) currentDir = newDir @@ -100,6 +103,9 @@ func (tmpl *Template) PageTree() ([]*core.PageTreeNode, error) { return err } + pageInfo := page.Get() + active := route == pageInfo.Route + // Attach the page to the appropriate directory node. dirs := strings.Split(relPath, string(filepath.Separator)) currentDir := rootNode @@ -112,10 +118,12 @@ func (tmpl *Template) PageTree() ([]*core.PageTreeNode, error) { } } + currentDir.Expand = active currentDir.Children = append(currentDir.Children, &core.PageTreeNode{ - Name: tmpl.getPageBase(currentDir.Name), - IsDir: false, - IPage: page, + Name: tmpl.getPageBase(currentDir.Name), + IsDir: false, + IPage: page, + Active: active, }) return nil diff --git a/sui/storages/local/page_test.go b/sui/storages/local/page_test.go index ff32262256..be9197b4c2 100644 --- a/sui/storages/local/page_test.go +++ b/sui/storages/local/page_test.go @@ -53,7 +53,7 @@ func TestTemplatePageTree(t *testing.T) { t.Fatalf("GetTemplate error: %v", err) } - pages, err := tmpl.PageTree() + pages, err := tmpl.PageTree("/page/[id]") if err != nil { t.Fatalf("Pages error: %v", err) }