Merge pull request #288 from trheyi/main
[add] widget form permission support
This commit is contained in:
commit
4e9a378e23
7 changed files with 218 additions and 43 deletions
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue