diff --git a/widgets/form/action.go b/widgets/form/action.go index 342d382c..e1abdcd0 100644 --- a/widgets/form/action.go +++ b/widgets/form/action.go @@ -60,48 +60,61 @@ var processActionDefaults = map[string]*action.Process{ }, } +func (act *ActionDSL) getDefaults() map[string]*action.Process { + defaults := map[string]*action.Process{} + for key, action := range processActionDefaults { + new := *action + if act.Guard != "" { + new.Guard = act.Guard + } + defaults[key] = &new + } + return defaults +} + // SetDefaultProcess set the default value of action func (act *ActionDSL) SetDefaultProcess() { + defaults := act.getDefaults() act.Setting = action.ProcessOf(act.Setting). - Merge(processActionDefaults["Setting"]). + Merge(defaults["Setting"]). SetHandler(processHandler) act.Component = action.ProcessOf(act.Component). - Merge(processActionDefaults["Component"]). + Merge(defaults["Component"]). SetHandler(processHandler) act.Upload = action.ProcessOf(act.Upload). - Merge(processActionDefaults["Upload"]). + Merge(defaults["Upload"]). SetHandler(processHandler) act.Download = action.ProcessOf(act.Download). - Merge(processActionDefaults["Download"]). + Merge(defaults["Download"]). SetHandler(processHandler) act.Find = action.ProcessOf(act.Find). WithBefore(act.BeforeFind).WithAfter(act.AfterFind). - Merge(processActionDefaults["Find"]). + Merge(defaults["Find"]). SetHandler(processHandler) act.Save = action.ProcessOf(act.Save). WithBefore(act.BeforeSave).WithAfter(act.AfterSave). - Merge(processActionDefaults["Save"]). + Merge(defaults["Save"]). SetHandler(processHandler) act.Create = action.ProcessOf(act.Create). WithBefore(act.BeforeCreate).WithAfter(act.AfterCreate). - Merge(processActionDefaults["Create"]). + Merge(defaults["Create"]). SetHandler(processHandler) act.Update = action.ProcessOf(act.Update). WithBefore(act.BeforeUpdate).WithAfter(act.AfterUpdate). - Merge(processActionDefaults["Update"]). + Merge(defaults["Update"]). SetHandler(processHandler) act.Delete = action.ProcessOf(act.Delete). WithBefore(act.BeforeDelete).WithAfter(act.AfterDelete). - Merge(processActionDefaults["Delete"]). + Merge(defaults["Delete"]). SetHandler(processHandler) } diff --git a/widgets/form/form.go b/widgets/form/form.go index 3fa59b33..f7f57ed1 100644 --- a/widgets/form/form.go +++ b/widgets/form/form.go @@ -151,10 +151,10 @@ func LoadData(data []byte, id string, root string) error { return fmt.Errorf("[Form] LoadData Bind %s %s", id, err.Error()) } - // Parse - err = dsl.Parse() + // mapping + err = dsl.mapping() if err != nil { - return fmt.Errorf("[Form] LoadData Parse %s %s", id, err.Error()) + return fmt.Errorf("[Form] LoadData Mapping %s %s", id, err.Error()) } // Validate @@ -195,23 +195,8 @@ func MustGet(form interface{}) *DSL { return t } -// Parse Layout -func (dsl *DSL) Parse() error { - - // ComputeFields - err := dsl.computeMapping() - if err != nil { - return err - } - - // Columns - return dsl.Fields.Form.CPropsMerge(dsl.CProps, func(name string, kind string, column field.ColumnDSL) (xpath string) { - return fmt.Sprintf("fields.form.%s.%s.props", name, kind) - }) -} - // Xgen trans to xgen setting -func (dsl *DSL) Xgen() (map[string]interface{}, error) { +func (dsl *DSL) Xgen(data map[string]interface{}, excludes map[string]bool) (map[string]interface{}, error) { if dsl.Layout == nil { dsl.Layout = &LayoutDSL{Form: &ViewLayoutDSL{}} @@ -221,12 +206,12 @@ func (dsl *DSL) Xgen() (map[string]interface{}, error) { dsl.Layout.Form = &ViewLayoutDSL{} } - setting, err := dsl.Layout.Xgen() + layout, err := dsl.Layout.Xgen(data, excludes, dsl.Mapping) if err != nil { return nil, err } - fields, err := dsl.Fields.Xgen(dsl.Layout) + fields, err := dsl.Fields.Xgen(layout) if err != nil { return nil, err } @@ -236,6 +221,17 @@ func (dsl *DSL) Xgen() (map[string]interface{}, error) { dsl.Config["full"] = true } + setting := map[string]interface{}{} + bytes, err := jsoniter.Marshal(layout) + if err != nil { + return nil, err + } + + err = jsoniter.Unmarshal(bytes, &setting) + if err != nil { + return nil, err + } + // Hooks onChange := map[string]interface{}{} setting["fields"] = fields @@ -283,7 +279,7 @@ func (dsl *DSL) Actions() []component.ActionsExport { len(dsl.Layout.Actions) > 0 { res = append(res, component.ActionsExport{ Type: "operation", - Xpath: "layout.operation.actions", + Xpath: "layout.actions", Actions: dsl.Layout.Actions, }) } diff --git a/widgets/form/layout.go b/widgets/form/layout.go index 23b6b253..77b0cce3 100644 --- a/widgets/form/layout.go +++ b/widgets/form/layout.go @@ -6,6 +6,7 @@ import ( jsoniter "github.com/json-iterator/go" "github.com/yaoapp/gou" "github.com/yaoapp/yao/widgets/component" + "github.com/yaoapp/yao/widgets/mapping" "github.com/yaoapp/yao/widgets/table" ) @@ -177,17 +178,89 @@ func (layout *LayoutDSL) listColumns(fn func(string, Column), path string, secti } // Xgen trans to Xgen setting -func (layout *LayoutDSL) Xgen() (map[string]interface{}, error) { - res := map[string]interface{}{} - data, err := jsoniter.Marshal(layout) +func (layout *LayoutDSL) Xgen(data map[string]interface{}, excludes map[string]bool, mapping *mapping.Mapping) (*LayoutDSL, error) { + clone, err := layout.Clone() if err != nil { return nil, err } - err = jsoniter.Unmarshal(data, &res) - if err != nil { - return nil, err + // layout.actions + if clone.Actions != nil && len(clone.Actions) > 0 { + clone.Actions = clone.Actions.Filter(excludes) } - return res, nil + // layout.form.sections + if clone.Form != nil && clone.Form.Sections != nil { + sections := []SectionDSL{} + for _, section := range clone.Form.Sections { + new, err := section.Filter(excludes, mapping) + if err != nil { + return nil, err + } + + if len(new.Columns) > 0 { + sections = append(sections, new) + } + } + clone.Form.Sections = sections + } + + return clone, nil +} + +// Filter exclude filter +func (section SectionDSL) Filter(excludes map[string]bool, mapping *mapping.Mapping) (SectionDSL, error) { + new := SectionDSL{Columns: []Column{}, Title: section.Title, Desc: section.Desc} + columns, err := section.filterColumns(section.Columns, excludes, mapping) + if err != nil { + return new, err + } + new.Columns = columns + return new, nil +} + +func (section SectionDSL) filterColumns(columns []Column, excludes map[string]bool, mapping *mapping.Mapping) ([]Column, error) { + + new := []Column{} + for i, column := range columns { + + if column.Tabs != nil { + for j, tab := range column.Tabs { + tabColumns, err := tab.filterColumns(columns[i].Tabs[j].Columns, excludes, mapping) + if err != nil { + return nil, err + } + column.Tabs[j].Columns = tabColumns + } + + new = append(new, column) + continue + } + + id, has := mapping.Columns[column.Name] + if !has { + continue + } + + if _, has := excludes[id]; has { + continue + } + + new = append(new, column) + } + return new, nil +} + +// Clone layout for output +func (layout *LayoutDSL) Clone() (*LayoutDSL, error) { + new := LayoutDSL{} + bytes, err := jsoniter.Marshal(layout) + if err != nil { + return nil, err + } + err = jsoniter.Unmarshal(bytes, &new) + if err != nil { + return nil, err + } + return &new, nil } diff --git a/widgets/form/compute.go b/widgets/form/mapping.go similarity index 57% rename from widgets/form/compute.go rename to widgets/form/mapping.go index 7618e6ae..a6654488 100644 --- a/widgets/form/compute.go +++ b/widgets/form/mapping.go @@ -5,6 +5,7 @@ import ( "github.com/yaoapp/yao/widgets/compute" "github.com/yaoapp/yao/widgets/field" + "github.com/yaoapp/yao/widgets/mapping" ) func (dsl *DSL) getField() func(string) (*field.ColumnDSL, string, string, error) { @@ -17,7 +18,7 @@ func (dsl *DSL) getField() func(string) (*field.ColumnDSL, string, string, error } } -func (dsl *DSL) computeMapping() error { +func (dsl *DSL) mapping() error { if dsl.Computes == nil { dsl.Computes = &compute.Maps{ Filter: map[string][]compute.Unit{}, @@ -26,10 +27,23 @@ func (dsl *DSL) computeMapping() error { } } + if dsl.Mapping == nil { + dsl.Mapping = &mapping.Mapping{} + } + + if dsl.Mapping.Filters == nil { + dsl.Mapping.Filters = map[string]string{} + } + + if dsl.Mapping.Columns == nil { + dsl.Mapping.Columns = map[string]string{} + } + if dsl.Fields == nil { return nil } + // Mapping compute and id if dsl.Fields.Form != nil && dsl.Layout.Form != nil && dsl.Layout.Form.Sections != nil { for _, section := range dsl.Layout.Form.Sections { @@ -37,6 +51,10 @@ func (dsl *DSL) computeMapping() error { if field, has := dsl.Fields.Form[inst.Name]; has { + // Mapping ID + dsl.Mapping.Columns[field.ID] = inst.Name + dsl.Mapping.Columns[inst.Name] = field.ID + // View if field.View != nil && field.View.Compute != nil { bind := field.ViewBind() @@ -59,5 +77,34 @@ func (dsl *DSL) computeMapping() error { } } - return nil + // Mapping Actions + dsl.mappingActions() + + // Columns + return dsl.Fields.Form.CPropsMerge(dsl.CProps, func(name string, kind string, column field.ColumnDSL) (xpath string) { + return fmt.Sprintf("fields.form.%s.%s.props", name, kind) + }) +} + +// Actions get the table actions +func (dsl *DSL) mappingActions() { + + if dsl.Mapping == nil { + dsl.Mapping = &mapping.Mapping{} + } + + if dsl.Mapping.Actions == nil { + dsl.Mapping.Actions = map[string]string{} + } + + // layout.operation.actions + if dsl.Layout != nil && + dsl.Layout.Actions != nil && + len(dsl.Layout.Actions) > 0 { + for idx, action := range dsl.Layout.Actions { + xpath := fmt.Sprintf("layout.actions[%d]", idx) + dsl.Mapping.Actions[action.ID] = xpath + dsl.Mapping.Actions[xpath] = action.ID + } + } } diff --git a/widgets/form/process.go b/widgets/form/process.go index a6b29d48..d1f6419e 100644 --- a/widgets/form/process.go +++ b/widgets/form/process.go @@ -10,6 +10,7 @@ import ( "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/helper" + "github.com/yaoapp/yao/widgets/app" ) // Export process @@ -29,7 +30,9 @@ func exportProcess() { func processXgen(process *gou.Process) interface{} { form := MustGet(process) - setting, err := form.Xgen() + data := process.ArgsMap(1, map[string]interface{}{}) + excludes := app.Permissions(process, "forms", form.ID) + setting, err := form.Xgen(data, excludes) if err != nil { exception.New(err.Error(), 500).Throw() } @@ -147,8 +150,7 @@ func processUpload(process *gou.Process) interface{} { func processSetting(process *gou.Process) interface{} { form := MustGet(process) - args := []interface{}{form.ID} - process.Args = append(args, process.Args...) // form name + process.Args = append(process.Args, process.Args[0]) // form name return form.Action.Setting.MustExec(process) } diff --git a/widgets/form/process_test.go b/widgets/form/process_test.go index 7670fce4..e10f19e9 100644 --- a/widgets/form/process_test.go +++ b/widgets/form/process_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/yaoapp/gou" "github.com/yaoapp/gou/fs" + "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/maps" "github.com/yaoapp/yao/config" @@ -252,6 +253,46 @@ func TestProcessXgen(t *testing.T) { assert.Equal(t, "/api/__yao/form/pet/upload/fields.form."+url.QueryEscape("相关图片")+".edit.props/api", data.Get("fields.form.相关图片.edit.props.api")) } +func TestProcessXgenWithPermissions(t *testing.T) { + load(t) + clear(t) + testData(t) + + session.Global().Set("__permissions", map[string]interface{}{ + "forms.pet": []string{ + "b57eff5c9bac87d74e2a26596ed2b76f", // actions[0] 删除 + "773bee07c83276b4627b5bd7b99844ed", // fields.form.相关图片 + }, + }) + + args := []interface{}{"pet"} + res, err := gou.NewProcess("yao.form.Xgen", args...).Exec() + if err != nil { + t.Fatal(err) + } + + data := any.Of(res).MapStr().Dot() + assert.Equal(t, "/api/__yao/form/pet/component/fields.form."+url.QueryEscape("住院天数")+".edit.props/"+url.QueryEscape("on:change"), data.Get("hooks.onChange.住院天数.api")) + assert.Equal(t, "开发者定义数据", data.Get("hooks.onChange.住院天数.params.extra")) + assert.Equal(t, "/api/__yao/form/pet/component/fields.form."+url.QueryEscape("状态")+".edit.props.xProps/remote", data.Get("fields.form.状态.edit.props.xProps.remote.api")) + assert.Equal(t, nil, data.Get("fields.form.相关图片")) + assert.NotEqual(t, "删除", data.Get("actions[0].title")) + + session.Global().Set("__permissions", nil) + res, err = gou.NewProcess("yao.form.Xgen", args...).Exec() + if err != nil { + t.Fatal(err) + } + + data = any.Of(res).MapStr().Dot() + assert.Equal(t, "/api/__yao/form/pet/component/fields.form."+url.QueryEscape("住院天数")+".edit.props/"+url.QueryEscape("on:change"), data.Get("hooks.onChange.住院天数.api")) + assert.Equal(t, "开发者定义数据", data.Get("hooks.onChange.住院天数.params.extra")) + assert.Equal(t, "/api/__yao/form/pet/component/fields.form."+url.QueryEscape("状态")+".edit.props.xProps/remote", data.Get("fields.form.状态.edit.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/form/pet/upload/fields.form."+url.QueryEscape("相关图片")+".edit.props/api", data.Get("fields.form.相关图片.edit.props.api")) + assert.Equal(t, "删除", data.Get("actions[0].title")) + +} + func load(t *testing.T) { prepare(t) err := Load(config.Conf) diff --git a/widgets/form/types.go b/widgets/form/types.go index e5574ca2..c92f33e5 100644 --- a/widgets/form/types.go +++ b/widgets/form/types.go @@ -6,6 +6,7 @@ import ( "github.com/yaoapp/yao/widgets/compute" "github.com/yaoapp/yao/widgets/field" "github.com/yaoapp/yao/widgets/hook" + "github.com/yaoapp/yao/widgets/mapping" ) // DSL the form DSL @@ -19,10 +20,12 @@ type DSL struct { Config map[string]interface{} `json:"config,omitempty"` CProps field.CloudProps `json:"-"` compute.Computable + *mapping.Mapping } // ActionDSL the form action DSL type ActionDSL struct { + Guard string `json:"guard,omitempty"` // the default guard Bind *BindActionDSL `json:"bind,omitempty"` Setting *action.Process `json:"setting,omitempty"` Component *action.Process `json:"component,omitempty"`