func (context *Context) valueOf(valuer func(interface{}, *qor.Context) interface{}, value interface{}, meta *Meta) interface{} { if valuer != nil { reflectValue := reflect.ValueOf(value) if reflectValue.Kind() != reflect.Ptr { reflectPtr := reflect.New(reflectValue.Type()) reflectPtr.Elem().Set(reflectValue) value = reflectPtr.Interface() } result := valuer(value, context.Context) if reflectValue := reflect.ValueOf(result); reflectValue.IsValid() { if reflectValue.Kind() == reflect.Ptr { if reflectValue.IsNil() || !reflectValue.Elem().IsValid() { return nil } result = reflectValue.Elem().Interface() } if meta.Type == "number" || meta.Type == "float" { if context.isNewRecord(value) && equal(reflect.Zero(reflect.TypeOf(result)).Interface(), result) { return nil } } return result } else { return nil } } utils.ExitWithMsg(fmt.Sprintf("No valuer found for meta %v of resource %v", meta.Name, meta.baseResource.Name)) return nil }
func (res *Resource) generateSections(values ...interface{}) []*Section { var sections []*Section var hasColumns []string var excludedColumns []string // Reverse values to make the last one as a key one // e.g. Name, Code, -Name (`-Name` will get first and will skip `Name`) for i := len(values) - 1; i >= 0; i-- { value := values[i] if section, ok := value.(*Section); ok { sections = append(sections, uniqueSection(section, &hasColumns)) } else if column, ok := value.(string); ok { if strings.HasPrefix(column, "-") { excludedColumns = append(excludedColumns, column) } else if !isContainsColumn(excludedColumns, column) { sections = append(sections, &Section{Rows: [][]string{{column}}}) } hasColumns = append(hasColumns, column) } else if row, ok := value.([]string); ok { for j := len(row) - 1; j >= 0; j-- { column = row[j] sections = append(sections, &Section{Rows: [][]string{{column}}}) hasColumns = append(hasColumns, column) } } else { utils.ExitWithMsg(fmt.Sprintf("Qor Resource: attributes should be Section or String, but it is %+v", value)) } } sections = reverseSections(sections) for _, section := range sections { section.Resource = *res } return sections }
func (res *Resource) Meta(meta *Meta) *Meta { if res.GetMeta(meta.Name) != nil { utils.ExitWithMsg("Duplicated meta %v defined for resource %v", meta.Name, res.Name) } res.Metas = append(res.Metas, meta) meta.baseResource = res meta.updateMeta() return meta }
func (context *Context) Execute(name string, result interface{}) { var tmpl *template.Template var cacheKey string if name == "show" && !context.Resource.isSetShowAttrs { name = "edit" } if context.Action == "" { context.Action = name } if context.Resource != nil { cacheKey = path.Join(context.resourcePath(), name) } else { cacheKey = name } if t, ok := templates[cacheKey]; !ok || true { if file, err := context.FindTemplate("layout.tmpl"); err == nil { if tmpl, err = template.New(filepath.Base(file)).Funcs(context.FuncMap()).ParseFiles(file); err == nil { for _, name := range []string{"header", "footer"} { if tmpl.Lookup(name) == nil { if file, err := context.FindTemplate(name + ".tmpl"); err == nil { tmpl.ParseFiles(file) } } else { utils.ExitWithMsg(err) } } } else { utils.ExitWithMsg(err) } } } else { tmpl = t } context.Result = result context.Content = context.Render(name, result) if err := tmpl.Execute(context.Writer, context); err != nil { utils.ExitWithMsg(err) } }
func (context *Context) RenderMeta(meta *Meta, value interface{}, prefix []string, metaType string, writer *bytes.Buffer) { prefix = append(prefix, meta.Name) funcsMap := context.FuncMap() funcsMap["render_form"] = func(value interface{}, sections []*Section, index ...int) template.HTML { var result = bytes.NewBufferString("") newPrefix := append([]string{}, prefix...) if len(index) > 0 { last := newPrefix[len(newPrefix)-1] newPrefix = append(newPrefix[:len(newPrefix)-1], fmt.Sprintf("%v[%v]", last, index[0])) } for _, field := range context.GetDB().NewScope(value).PrimaryFields() { if meta := sections[0].Resource.GetMetaOrNew(field.Name); meta != nil { context.RenderMeta(meta, value, newPrefix, "form", result) } } context.renderForm(value, sections, newPrefix, result) return template.HTML(result.String()) } var tmpl *template.Template var err error if file, err := context.FindTemplate(fmt.Sprintf("metas/%v/%v.tmpl", metaType, meta.Name), fmt.Sprintf("metas/%v/%v.tmpl", metaType, meta.Type)); err == nil { tmpl, err = template.New(filepath.Base(file)).Funcs(funcsMap).ParseFiles(file) } else { tmpl, err = template.New(meta.Type + ".tmpl").Funcs(funcsMap).Parse("{{.Value}}") } if err == nil { var scope = context.GetDB().NewScope(value) var data = map[string]interface{}{ "Context": context, "BaseResource": meta.baseResource, "ResourceValue": value, "InputId": fmt.Sprintf("%v_%v_%v", scope.GetModelStruct().ModelType.Name(), scope.PrimaryKeyValue(), meta.Name), "Label": meta.Label, "InputName": strings.Join(prefix, "."), "Value": context.FormattedValueOf(value, meta), "Meta": meta, } if meta.GetCollection != nil { data["CollectionValue"] = meta.GetCollection(value, context.Context) } err = tmpl.Execute(writer, data) } if err != nil { utils.ExitWithMsg(fmt.Sprintf("got error when render %v template for %v(%v):%v", metaType, meta.Name, meta.Type, err)) } }
func (context *Context) renderMeta(writer *bytes.Buffer, meta *Meta, value interface{}, prefix []string) { prefix = append(prefix, meta.Name) funcsMap := context.FuncMap() funcsMap["render_form"] = func(value interface{}, metas []*Meta, index ...int) template.HTML { var result = bytes.NewBufferString("") newPrefix := append([]string{}, prefix...) if len(index) > 0 { last := newPrefix[len(newPrefix)-1] newPrefix = append(newPrefix[:len(newPrefix)-1], fmt.Sprintf("%v[%v]", last, index[0])) } context.renderForm(result, value, metas, newPrefix) return template.HTML(result.String()) } var tmpl = template.New(meta.Type + ".tmpl").Funcs(funcsMap) if tmpl, err := context.FindTemplate(tmpl, fmt.Sprintf("metas/form/%v.tmpl", meta.Type)); err == nil { data := map[string]interface{}{} data["Base"] = meta.base scope := context.GetDB().NewScope(value) data["InputId"] = fmt.Sprintf("%v_%v_%v", scope.GetModelStruct().ModelType.Name(), scope.PrimaryKeyValue(), meta.Name) data["Label"] = meta.Label data["InputName"] = strings.Join(prefix, ".") data["Result"] = value data["Value"] = context.ValueOf(value, meta) if meta.GetCollection != nil { data["CollectionValue"] = meta.GetCollection(value, context.Context) } data["Meta"] = meta if err := tmpl.Execute(writer, data); err != nil { utils.ExitWithMsg(fmt.Sprintf("got error when parse template for %v(%v):%v", meta.Name, meta.Type, err)) } } else { utils.ExitWithMsg(fmt.Sprintf("%v: form type %v not supported: got error %v", meta.Name, meta.Type, err)) } }
func (context *Context) Render(name string, results ...interface{}) template.HTML { if file, err := context.FindTemplate(name + ".tmpl"); err == nil { var clone = context.clone() var result = bytes.NewBufferString("") if len(results) > 0 { clone.Result = results[0] } if tmpl, err := template.New(filepath.Base(file)).Funcs(clone.FuncMap()).ParseFiles(file); err == nil { if err := tmpl.Execute(result, clone); err != nil { utils.ExitWithMsg(err) } } else { utils.ExitWithMsg(err) } return template.HTML(result.String()) } return "" }
func isContainsPositiveValue(values ...interface{}) bool { for _, value := range values { if _, ok := value.(*Section); ok { return true } else if column, ok := value.(string); ok { if !strings.HasPrefix(column, "-") { return true } } else { utils.ExitWithMsg(fmt.Sprintf("Qor Resource: attributes should be Section or String, but it is %+v", value)) } } return false }
func (context *Context) renderIndexMeta(value interface{}, meta *Meta) template.HTML { var err error var result = bytes.NewBufferString("") var tmpl = template.New(meta.Type + ".tmpl").Funcs(context.FuncMap()) if tmpl, err = context.FindTemplate(tmpl, fmt.Sprintf("metas/index/%v.tmpl", meta.Type)); err != nil { tmpl, _ = tmpl.Parse("{{.Value}}") } data := map[string]interface{}{"Value": context.ValueOf(value, meta), "Meta": meta} if err := tmpl.Execute(result, data); err != nil { utils.ExitWithMsg(err.Error()) } return template.HTML(result.String()) }
func (publish *Publish) ConfigureQorResource(res *admin.Resource) { if !injected { injected = true for _, gopath := range strings.Split(os.Getenv("GOPATH"), ":") { admin.RegisterViewPath(path.Join(gopath, "src/github.com/qor/qor/publish/views")) } } res.UseTheme("publish") if event := res.GetAdmin().GetResource("PublishEvent"); event == nil { eventResource := res.GetAdmin().AddResource(&PublishEvent{}, &admin.Config{Invisible: true}) eventResource.IndexAttrs("Name", "Description", "CreatedAt") } controller := publishController{publish} router := res.GetAdmin().GetRouter() router.Get(fmt.Sprintf("^/%v/diff/", res.ToParam()), controller.Diff) router.Get(fmt.Sprintf("^/%v", res.ToParam()), controller.Preview) router.Post(fmt.Sprintf("^/%v", res.ToParam()), controller.PublishOrDiscard) res.GetAdmin().RegisterFuncMap("render_publish_meta", func(value interface{}, meta *admin.Meta, context *admin.Context) template.HTML { var err error var result = bytes.NewBufferString("") var tmpl = template.New(meta.Type + ".tmpl").Funcs(context.FuncMap()) if tmpl, err = context.FindTemplate(tmpl, fmt.Sprintf("metas/publish/%v.tmpl", meta.Type)); err != nil { if tmpl, err = context.FindTemplate(tmpl, fmt.Sprintf("metas/index/%v.tmpl", meta.Type)); err != nil { tmpl, _ = tmpl.Parse("{{.Value}}") } } data := map[string]interface{}{"Value": context.ValueOf(value, meta), "Meta": meta} if err := tmpl.Execute(result, data); err != nil { utils.ExitWithMsg(err.Error()) } return template.HTML(result.String()) }) res.GetAdmin().RegisterFuncMap("publish_unique_key", func(res *admin.Resource, record interface{}, context *admin.Context) string { return fmt.Sprintf("%s__%v", res.ToParam(), context.GetDB().NewScope(record).PrimaryKeyValue()) }) res.GetAdmin().RegisterFuncMap("is_publish_event_resource", func(res *admin.Resource) bool { return IsPublishEvent(res.Value) }) }
func (res *Resource) setSections(sections *[]*Section, values ...interface{}) { if len(*sections) > 0 && len(values) == 0 { return } if len(*sections) == 0 && len(values) == 0 { *sections = res.generateSections(res.allAttrs()) } else { var flattenValues []interface{} for _, value := range values { if columns, ok := value.([]string); ok { for _, column := range columns { flattenValues = append(flattenValues, column) } } else if _sections, ok := value.([]*Section); ok { for _, section := range _sections { flattenValues = append(flattenValues, section) } } else if section, ok := value.(*Section); ok { flattenValues = append(flattenValues, section) } else if column, ok := value.(string); ok { flattenValues = append(flattenValues, column) } else { utils.ExitWithMsg(fmt.Sprintf("Qor Resource: attributes should be Section or String, but it is %+v", value)) } } if isContainsPositiveValue(flattenValues...) { *sections = res.generateSections(flattenValues...) } else { var valueStrs []string var availbleColumns []string for _, value := range flattenValues { if column, ok := value.(string); ok { valueStrs = append(valueStrs, column) } } for _, column := range res.allAttrs() { if !isContainsColumn(valueStrs, column) { availbleColumns = append(availbleColumns, column) } } *sections = res.generateSections(availbleColumns) } } }
func (meta *Meta) PreInitialize() error { if meta.Name == "" { utils.ExitWithMsg("Meta should have name: %v", reflect.TypeOf(meta)) } else if meta.FieldName == "" { meta.FieldName = meta.Name } // parseNestedField used to handle case like Profile.Name var parseNestedField = func(value reflect.Value, name string) (reflect.Value, string) { fields := strings.Split(name, ".") value = reflect.Indirect(value) for _, field := range fields[:len(fields)-1] { value = value.FieldByName(field) } return value, fields[len(fields)-1] } var getField = func(fields []*gorm.StructField, name string) *gorm.StructField { for _, field := range fields { if field.Name == name || field.DBName == name { return field } } return nil } var nestedField = strings.Contains(meta.FieldName, ".") var scope = &gorm.Scope{Value: meta.Resource.GetResource().Value} if nestedField { subModel, name := parseNestedField(reflect.ValueOf(meta.Resource.GetResource().Value), meta.FieldName) meta.FieldStruct = getField(scope.New(subModel.Interface()).GetStructFields(), name) } else { meta.FieldStruct = getField(scope.GetStructFields(), meta.FieldName) } return nil }
func (meta *Meta) updateMeta() { meta.Meta = resource.Meta{ Name: meta.Name, FieldName: meta.FieldName, Setter: meta.Setter, FormattedValuer: meta.FormattedValuer, Valuer: meta.Valuer, Permission: meta.Permission.Concat(meta.baseResource.Permission), Resource: meta.baseResource, } meta.PreInitialize() if meta.FieldStruct != nil { if injector, ok := reflect.New(meta.FieldStruct.Struct.Type).Interface().(resource.ConfigureMetaBeforeInitializeInterface); ok { injector.ConfigureQorMetaBeforeInitialize(meta) } } meta.Initialize() if meta.Label == "" { meta.Label = utils.HumanizeString(meta.Name) } var fieldType reflect.Type var hasColumn = meta.FieldStruct != nil if hasColumn { fieldType = meta.FieldStruct.Struct.Type for fieldType.Kind() == reflect.Ptr { fieldType = fieldType.Elem() } } // Set Meta Type if meta.Type == "" && hasColumn { if relationship := meta.FieldStruct.Relationship; relationship != nil { if relationship.Kind == "has_one" { meta.Type = "single_edit" } else if relationship.Kind == "has_many" { meta.Type = "collection_edit" } else if relationship.Kind == "belongs_to" { meta.Type = "select_one" } else if relationship.Kind == "many_to_many" { meta.Type = "select_many" } } else { switch fieldType.Kind().String() { case "string": var tag = meta.FieldStruct.Tag if size, ok := utils.ParseTagOption(tag.Get("sql"))["SIZE"]; ok { if i, _ := strconv.Atoi(size); i > 255 { meta.Type = "text" } else { meta.Type = "string" } } else if text, ok := utils.ParseTagOption(tag.Get("sql"))["TYPE"]; ok && text == "text" { meta.Type = "text" } else { meta.Type = "string" } case "bool": meta.Type = "checkbox" default: if regexp.MustCompile(`^(.*)?(u)?(int)(\d+)?`).MatchString(fieldType.Kind().String()) { meta.Type = "number" } else if regexp.MustCompile(`^(.*)?(float)(\d+)?`).MatchString(fieldType.Kind().String()) { meta.Type = "float" } else if _, ok := reflect.New(fieldType).Interface().(*time.Time); ok { meta.Type = "datetime" } } } } { // Set Meta Resource if hasColumn && (meta.FieldStruct.Relationship != nil) { if meta.Resource == nil { var result interface{} if fieldType.Kind() == reflect.Struct { result = reflect.New(fieldType).Interface() } else if fieldType.Kind() == reflect.Slice { refelectType := fieldType.Elem() for refelectType.Kind() == reflect.Ptr { refelectType = refelectType.Elem() } result = reflect.New(refelectType).Interface() } res := meta.baseResource.GetAdmin().NewResource(result) res.configure() meta.Resource = res } if meta.Resource != nil { permission := meta.Resource.Permission.Concat(meta.Meta.Permission) meta.Resource.Permission = permission meta.SetPermission(permission) meta.setBaseResource(meta.baseResource) } } } scope := &gorm.Scope{Value: meta.baseResource.Value} scopeField, _ := scope.FieldByName(meta.GetFieldName()) { // Format Meta FormattedValueOf if meta.FormattedValuer == nil { if meta.Type == "select_one" { meta.SetFormattedValuer(func(value interface{}, context *qor.Context) interface{} { return utils.Stringify(meta.GetValuer()(value, context)) }) } else if meta.Type == "select_many" { meta.SetFormattedValuer(func(value interface{}, context *qor.Context) interface{} { reflectValue := reflect.Indirect(reflect.ValueOf(meta.GetValuer()(value, context))) var results []string for i := 0; i < reflectValue.Len(); i++ { results = append(results, utils.Stringify(reflectValue.Index(i).Interface())) } return results }) } } } { // Format Meta Collection if meta.Collection != nil { if maps, ok := meta.Collection.([]string); ok { meta.GetCollection = func(interface{}, *qor.Context) (results [][]string) { for _, value := range maps { results = append(results, []string{value, value}) } return } } else if maps, ok := meta.Collection.([][]string); ok { meta.GetCollection = func(interface{}, *qor.Context) [][]string { return maps } } else if f, ok := meta.Collection.(func(interface{}, *qor.Context) [][]string); ok { meta.GetCollection = f } else { utils.ExitWithMsg("Unsupported Collection format for meta %v of resource %v", meta.Name, reflect.TypeOf(meta.baseResource.Value)) } } else if meta.Type == "select_one" || meta.Type == "select_many" { if scopeField.Relationship != nil { fieldType := scopeField.StructField.Struct.Type if fieldType.Kind() == reflect.Slice { fieldType = fieldType.Elem() } meta.GetCollection = func(value interface{}, context *qor.Context) (results [][]string) { values := reflect.New(reflect.SliceOf(fieldType)).Interface() context.GetDB().Find(values) reflectValues := reflect.Indirect(reflect.ValueOf(values)) for i := 0; i < reflectValues.Len(); i++ { scope := scope.New(reflectValues.Index(i).Interface()) primaryKey := fmt.Sprintf("%v", scope.PrimaryKeyValue()) results = append(results, []string{primaryKey, utils.Stringify(reflectValues.Index(i).Interface())}) } return } } else { utils.ExitWithMsg("%v meta type %v needs Collection", meta.Name, meta.Type) } } } meta.FieldName = meta.GetFieldName() if meta.FieldStruct != nil { if injector, ok := reflect.New(meta.FieldStruct.Struct.Type).Interface().(resource.ConfigureMetaInterface); ok { injector.ConfigureQorMeta(meta) } } }
func (meta *Meta) updateMeta() { meta.Meta = resource.Meta{ Name: meta.Name, FieldName: meta.FieldName, Setter: meta.Setter, Valuer: meta.Valuer, Permission: meta.Permission, ResourceValue: meta.base.Value, } meta.UpdateMeta() if meta.Label == "" { meta.Label = utils.HumanizeString(meta.Name) } var ( scope = &gorm.Scope{Value: meta.base.Value} nestedField = strings.Contains(meta.GetFieldName(), ".") field *gorm.StructField hasColumn bool ) if nestedField { subModel, name := parseNestedField(reflect.ValueOf(meta.base.Value), meta.GetFieldName()) subScope := &gorm.Scope{Value: subModel.Interface()} field, hasColumn = getField(subScope.GetStructFields(), name) } else { if field, hasColumn = getField(scope.GetStructFields(), meta.GetFieldName()); hasColumn { meta.SetFieldName(field.Name) if field.IsNormal { meta.DBName = field.DBName } } } var fieldType reflect.Type if hasColumn { fieldType = field.Struct.Type for fieldType.Kind() == reflect.Ptr { fieldType = fieldType.Elem() } } // Set Meta Type if meta.Type == "" && hasColumn { if relationship := field.Relationship; relationship != nil { if relationship.Kind == "has_one" { meta.Type = "single_edit" } else if relationship.Kind == "has_many" { meta.Type = "collection_edit" } else if relationship.Kind == "belongs_to" { meta.Type = "select_one" } else if relationship.Kind == "many_to_many" { meta.Type = "select_many" } } else { switch fieldType.Kind().String() { case "string": if size, ok := utils.ParseTagOption(field.Tag.Get("sql"))["SIZE"]; ok { if i, _ := strconv.Atoi(size); i > 255 { meta.Type = "text" } else { meta.Type = "string" } } else if text, ok := utils.ParseTagOption(field.Tag.Get("sql"))["TYPE"]; ok && text == "text" { meta.Type = "text" } else { meta.Type = "string" } case "bool": meta.Type = "checkbox" default: if regexp.MustCompile(`^(.*)?(u)?(int)(\d+)?`).MatchString(fieldType.Kind().String()) { meta.Type = "number" } else if regexp.MustCompile(`^(.*)?(float)(\d+)?`).MatchString(fieldType.Kind().String()) { meta.Type = "float" } else if _, ok := reflect.New(fieldType).Interface().(*time.Time); ok { meta.Type = "datetime" } else if _, ok := reflect.New(fieldType).Interface().(media_library.MediaLibrary); ok { meta.Type = "file" } } } } // Set Meta Resource if meta.Resource == nil { if hasColumn && (field.Relationship != nil) { var result interface{} if fieldType.Kind().String() == "struct" { result = reflect.New(fieldType).Interface() } else if fieldType.Kind().String() == "slice" { refelectType := fieldType.Elem() for refelectType.Kind() == reflect.Ptr { refelectType = refelectType.Elem() } result = reflect.New(refelectType).Interface() } res := meta.base.GetAdmin().NewResource(result) res.compile() meta.Resource = res } } scopeField, _ := scope.FieldByName(meta.GetFieldName()) // Set Meta Collection if meta.Collection != nil { if maps, ok := meta.Collection.([]string); ok { meta.GetCollection = func(interface{}, *qor.Context) (results [][]string) { for _, value := range maps { results = append(results, []string{value, value}) } return } } else if maps, ok := meta.Collection.([][]string); ok { meta.GetCollection = func(interface{}, *qor.Context) [][]string { return maps } } else if f, ok := meta.Collection.(func(interface{}, *qor.Context) [][]string); ok { meta.GetCollection = f } else { utils.ExitWithMsg("Unsupported Collection format for meta %v of resource %v", meta.Name, reflect.TypeOf(meta.base.Value)) } } else if meta.Type == "select_one" || meta.Type == "select_many" { if scopeField.Relationship != nil { fieldType := scopeField.StructField.Struct.Type if fieldType.Kind() == reflect.Slice { fieldType = fieldType.Elem() } meta.GetCollection = func(value interface{}, context *qor.Context) (results [][]string) { values := reflect.New(reflect.SliceOf(fieldType)).Interface() context.GetDB().Find(values) reflectValues := reflect.Indirect(reflect.ValueOf(values)) for i := 0; i < reflectValues.Len(); i++ { scope := scope.New(reflectValues.Index(i).Interface()) primaryKey := fmt.Sprintf("%v", scope.PrimaryKeyValue()) results = append(results, []string{primaryKey, utils.Stringify(reflectValues.Index(i).Interface())}) } return } } else { utils.ExitWithMsg("%v meta type %v needs Collection", meta.Name, meta.Type) } } meta.FieldName = meta.GetFieldName() }
func (mediaBox MediaBox) ConfigureQorMeta(metaor resource.Metaor) { if meta, ok := metaor.(*admin.Meta); ok { if meta.Config == nil { meta.Config = &MediaBoxConfig{} } if meta.FormattedValuer == nil { meta.FormattedValuer = func(record interface{}, context *qor.Context) interface{} { if mediaBox, ok := meta.GetValuer()(record, context).(*MediaBox); ok { return mediaBox.URL() } return "" } meta.SetFormattedValuer(meta.FormattedValuer) } if config, ok := meta.Config.(*MediaBoxConfig); ok { Admin := meta.GetBaseResource().(*admin.Resource).GetAdmin() if config.RemoteDataResource == nil { mediaLibraryResource := Admin.GetResource("MediaLibrary") if mediaLibraryResource == nil { mediaLibraryResource = Admin.NewResource(&MediaLibrary{}) } config.RemoteDataResource = mediaLibraryResource } if _, ok := config.RemoteDataResource.Value.(MediaLibraryInterface); !ok { utils.ExitWithMsg("%v havn't implement MediaLibraryInterface, please fix that.", reflect.TypeOf(config.RemoteDataResource.Value)) } if meta := config.RemoteDataResource.GetMeta("MediaOption"); meta == nil { config.RemoteDataResource.Meta(&admin.Meta{ Name: "MediaOption", Type: "hidden", Setter: func(record interface{}, metaValue *resource.MetaValue, context *qor.Context) { if mediaLibrary, ok := record.(MediaLibraryInterface); ok { var mediaOption MediaOption if err := json.Unmarshal([]byte(utils.ToString(metaValue.Value)), &mediaOption); err == nil { mediaOption.FileName = "" mediaOption.URL = "" mediaOption.OriginalURL = "" mediaLibrary.ScanMediaOptions(mediaOption) } } }, Valuer: func(record interface{}, context *qor.Context) interface{} { if mediaLibrary, ok := record.(MediaLibraryInterface); ok { if value, err := json.Marshal(mediaLibrary.GetMediaOption()); err == nil { return string(value) } } return "" }, }) } if meta := config.RemoteDataResource.GetMeta("SelectedType"); meta == nil { config.RemoteDataResource.Meta(&admin.Meta{ Name: "SelectedType", Type: "hidden", Valuer: func(record interface{}, context *qor.Context) interface{} { if mediaLibrary, ok := record.(MediaLibraryInterface); ok { return mediaLibrary.GetSelectedType() } return "" }, }) } config.RemoteDataResource.AddProcessor(func(record interface{}, metaValues *resource.MetaValues, context *qor.Context) error { if mediaLibrary, ok := record.(MediaLibraryInterface); ok { var filename string var mediaOption MediaOption for _, metaValue := range metaValues.Values { if fileHeaders, ok := metaValue.Value.([]*multipart.FileHeader); ok { for _, fileHeader := range fileHeaders { filename = fileHeader.Filename } } } if metaValue := metaValues.Get("MediaOption"); metaValue != nil { mediaOptionStr := utils.ToString(metaValue.Value) json.Unmarshal([]byte(mediaOptionStr), &mediaOption) } if mediaOption.SelectedType == "video_link" { mediaLibrary.SetSelectedType("video_link") } else if filename != "" { if _, err := getImageFormat(filename); err == nil { mediaLibrary.SetSelectedType("image") } else if isVideoFormat(filename) { mediaLibrary.SetSelectedType("video") } else { mediaLibrary.SetSelectedType("file") } } } return nil }) config.RemoteDataResource.UseTheme("grid") config.RemoteDataResource.UseTheme("media_library") config.RemoteDataResource.IndexAttrs(config.RemoteDataResource.IndexAttrs(), "-MediaOption") config.RemoteDataResource.NewAttrs(config.RemoteDataResource.NewAttrs(), "MediaOption") config.RemoteDataResource.EditAttrs(config.RemoteDataResource.EditAttrs(), "MediaOption") config.SelectManyConfig.RemoteDataResource = config.RemoteDataResource config.SelectManyConfig.ConfigureQorMeta(meta) } meta.Type = "media_box" } }
func (meta *Meta) Initialize() error { var ( nestedField = strings.Contains(meta.FieldName, ".") field = meta.FieldStruct hasColumn = meta.FieldStruct != nil ) var fieldType reflect.Type if hasColumn { fieldType = field.Struct.Type for fieldType.Kind() == reflect.Ptr { fieldType = fieldType.Elem() } } // Set Meta Valuer if meta.Valuer == nil { if hasColumn { meta.Valuer = func(value interface{}, context *qor.Context) interface{} { scope := context.GetDB().NewScope(value) fieldName := meta.FieldName if nestedField { fields := strings.Split(fieldName, ".") fieldName = fields[len(fields)-1] } if f, ok := scope.FieldByName(fieldName); ok { if f.Relationship != nil && f.Field.CanAddr() && !scope.PrimaryKeyZero() { context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName) } return f.Field.Interface() } return "" } } else { utils.ExitWithMsg("Unsupported meta name %v for resource %v", meta.FieldName, reflect.TypeOf(meta.Resource.GetResource().Value)) } } if meta.Setter == nil && hasColumn { if relationship := field.Relationship; relationship != nil { if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" { meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) { scope := &gorm.Scope{Value: resource} reflectValue := reflect.Indirect(reflect.ValueOf(resource)) field := reflectValue.FieldByName(meta.FieldName) if field.Kind() == reflect.Ptr { if field.IsNil() { field.Set(utils.NewValue(field.Type()).Elem()) } for field.Kind() == reflect.Ptr { field = field.Elem() } } primaryKeys := utils.ToArray(metaValue.Value) // associations not changed for belongs to if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 { oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface()) // if not changed if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) { return } // if removed if len(primaryKeys) == 0 { field := reflectValue.FieldByName(relationship.ForeignFieldNames[0]) field.Set(reflect.Zero(field.Type())) } } if len(primaryKeys) > 0 { context.GetDB().Where(primaryKeys).Find(field.Addr().Interface()) } // Replace many 2 many relations if relationship.Kind == "many_to_many" { if !scope.PrimaryKeyZero() { context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface()) field.Set(reflect.Zero(field.Type())) } } } } } else { meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) { if metaValue == nil { return } value := metaValue.Value fieldName := meta.FieldName if nestedField { fields := strings.Split(fieldName, ".") fieldName = fields[len(fields)-1] } field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName) if field.Kind() == reflect.Ptr { if field.IsNil() { field.Set(utils.NewValue(field.Type()).Elem()) } for field.Kind() == reflect.Ptr { field = field.Elem() } } if field.IsValid() && field.CanAddr() { switch field.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: field.SetInt(utils.ToInt(value)) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: field.SetUint(utils.ToUint(value)) case reflect.Float32, reflect.Float64: field.SetFloat(utils.ToFloat(value)) case reflect.Bool: // TODO: add test if utils.ToString(value) == "true" { field.SetBool(true) } else { field.SetBool(false) } default: if scanner, ok := field.Addr().Interface().(sql.Scanner); ok { if scanner.Scan(value) != nil { scanner.Scan(utils.ToString(value)) } } else if reflect.TypeOf("").ConvertibleTo(field.Type()) { field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type())) } else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) { field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type())) } else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) { field.Set(rvalue.Convert(field.Type())) } else if _, ok := field.Addr().Interface().(*time.Time); ok { if str := utils.ToString(value); str != "" { if newTime, err := now.Parse(str); err == nil { field.Set(reflect.ValueOf(newTime)) } } } else { var buf = bytes.NewBufferString("") json.NewEncoder(buf).Encode(value) if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil { utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.TypeOf(value), field.Type(), meta) } } } } } } } if nestedField { oldvalue := meta.Valuer meta.Valuer = func(value interface{}, context *qor.Context) interface{} { return oldvalue(getNestedModel(value, meta.FieldName, context), context) } oldSetter := meta.Setter meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) { oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context) } } return nil }
func (meta *Meta) updateMeta() { if meta.Name == "" { utils.ExitWithMsg("Meta should have name: %v", reflect.ValueOf(meta).Type()) } else if meta.Alias == "" { meta.Alias = meta.Name } if meta.Label == "" { meta.Label = utils.HumanizeString(meta.Name) } var ( scope = &gorm.Scope{Value: meta.base.Value} nestedField = strings.Contains(meta.Alias, ".") field *gorm.StructField hasColumn bool ) if nestedField { subModel, name := utils.ParseNestedField(reflect.ValueOf(meta.base.Value), meta.Alias) subScope := &gorm.Scope{Value: subModel.Interface()} field, hasColumn = getField(subScope.GetStructFields(), name) } else { if field, hasColumn = getField(scope.GetStructFields(), meta.Alias); hasColumn { meta.Alias = field.Name if field.IsNormal { meta.DBName = field.DBName } } } var fieldType reflect.Type if hasColumn { fieldType = field.Struct.Type for fieldType.Kind() == reflect.Ptr { fieldType = fieldType.Elem() } } // Set Meta Type if meta.Type == "" && hasColumn { if relationship := field.Relationship; relationship != nil { if relationship.Kind == "has_one" { meta.Type = "single_edit" } else if relationship.Kind == "has_many" { meta.Type = "collection_edit" } else if relationship.Kind == "belongs_to" { meta.Type = "select_one" } else if relationship.Kind == "many_to_many" { meta.Type = "select_many" } } else { switch fieldType.Kind().String() { case "string": if size, ok := utils.ParseTagOption(field.Tag.Get("sql"))["SIZE"]; ok { if i, _ := strconv.Atoi(size); i > 255 { meta.Type = "text" } else { meta.Type = "string" } } else if text, ok := utils.ParseTagOption(field.Tag.Get("sql"))["TYPE"]; ok && text == "text" { meta.Type = "text" } else { meta.Type = "string" } case "bool": meta.Type = "checkbox" default: if regexp.MustCompile(`^(.*)?(u)?(int)(\d+)?`).MatchString(fieldType.Kind().String()) { meta.Type = "number" } else if regexp.MustCompile(`^(.*)?(float)(\d+)?`).MatchString(fieldType.Kind().String()) { meta.Type = "float" } else if _, ok := reflect.New(fieldType).Interface().(*time.Time); ok { meta.Type = "datetime" } else if _, ok := reflect.New(fieldType).Interface().(media_library.MediaLibrary); ok { meta.Type = "file" } } } } // Set Meta Resource if meta.Resource == nil { if hasColumn && (field.Relationship != nil) { var result interface{} if fieldType.Kind().String() == "struct" { result = reflect.New(fieldType).Interface() } else if fieldType.Kind().String() == "slice" { refelectType := fieldType.Elem() for refelectType.Kind() == reflect.Ptr { refelectType = refelectType.Elem() } result = reflect.New(refelectType).Interface() } res := meta.base.GetAdmin().NewResource(result) res.compile() meta.Resource = res } } // Set Meta Valuer if meta.Valuer == nil { if hasColumn { meta.Valuer = func(value interface{}, context *qor.Context) interface{} { scope := context.GetDB().NewScope(value) alias := meta.Alias if nestedField { fields := strings.Split(alias, ".") alias = fields[len(fields)-1] } if f, ok := scope.FieldByName(alias); ok { if field.Relationship != nil { if f.Field.CanAddr() && !scope.PrimaryKeyZero() { context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.Alias) } } if f.Field.CanAddr() { return f.Field.Addr().Interface() } else { return f.Field.Interface() } } return "" } } else { utils.ExitWithMsg("Unsupported meta name %v for resource %v", meta.Name, reflect.TypeOf(meta.base.Value)) } } scopeField, _ := scope.FieldByName(meta.Alias) // Set Meta Collection if meta.Collection != nil { if maps, ok := meta.Collection.([]string); ok { meta.GetCollection = func(interface{}, *qor.Context) (results [][]string) { for _, value := range maps { results = append(results, []string{value, value}) } return } } else if maps, ok := meta.Collection.([][]string); ok { meta.GetCollection = func(interface{}, *qor.Context) [][]string { return maps } } else if f, ok := meta.Collection.(func(interface{}, *qor.Context) [][]string); ok { meta.GetCollection = f } else { utils.ExitWithMsg("Unsupported Collection format for meta %v of resource %v", meta.Name, reflect.TypeOf(meta.base.Value)) } } else if meta.Type == "select_one" || meta.Type == "select_many" { if scopeField.Relationship != nil { fieldType := scopeField.StructField.Struct.Type if fieldType.Kind() == reflect.Slice { fieldType = fieldType.Elem() } meta.GetCollection = func(value interface{}, context *qor.Context) (results [][]string) { values := reflect.New(reflect.SliceOf(fieldType)).Interface() context.GetDB().Find(values) reflectValues := reflect.Indirect(reflect.ValueOf(values)) for i := 0; i < reflectValues.Len(); i++ { scope := scope.New(reflectValues.Index(i).Interface()) primaryKey := fmt.Sprintf("%v", scope.PrimaryKeyValue()) results = append(results, []string{primaryKey, utils.Stringify(reflectValues.Index(i).Interface())}) } return } } else { utils.ExitWithMsg("%v meta type %v needs Collection", meta.Name, meta.Type) } } if meta.Setter == nil && hasColumn { if relationship := field.Relationship; relationship != nil { if meta.Type == "select_one" || meta.Type == "select_many" { meta.Setter = func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context) { scope := &gorm.Scope{Value: resource} reflectValue := reflect.Indirect(reflect.ValueOf(resource)) field := reflectValue.FieldByName(meta.Alias) if field.Kind() == reflect.Ptr { if field.IsNil() { field.Set(utils.NewValue(field.Type()).Elem()) } for field.Kind() == reflect.Ptr { field = field.Elem() } } primaryKeys := utils.ToArray(metaValue.Value) // associations not changed for belongs to if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 { oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface()) // if not changed if equalAsString(primaryKeys, oldPrimaryKeys) { return } // if removed if len(primaryKeys) == 0 { field := reflectValue.FieldByName(relationship.ForeignFieldNames[0]) field.Set(reflect.Zero(field.Type())) } } if len(primaryKeys) > 0 { context.GetDB().Where(primaryKeys).Find(field.Addr().Interface()) } // Replace many 2 many relations if relationship.Kind == "many_to_many" { if !scope.PrimaryKeyZero() { context.GetDB().Model(resource).Association(meta.Alias).Replace(field.Interface()) field.Set(reflect.Zero(field.Type())) } } } } } else { meta.Setter = func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context) { if metaValue == nil { return } value := metaValue.Value alias := meta.Alias if nestedField { fields := strings.Split(alias, ".") alias = fields[len(fields)-1] } field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(alias) if field.Kind() == reflect.Ptr { if field.IsNil() { field.Set(utils.NewValue(field.Type()).Elem()) } for field.Kind() == reflect.Ptr { field = field.Elem() } } if field.IsValid() && field.CanAddr() { switch field.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: field.SetInt(utils.ToInt(value)) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: field.SetUint(utils.ToUint(value)) case reflect.Float32, reflect.Float64: field.SetFloat(utils.ToFloat(value)) case reflect.Bool: // TODO: add test if utils.ToString(value) == "true" { field.SetBool(true) } else { field.SetBool(false) } default: if scanner, ok := field.Addr().Interface().(sql.Scanner); ok { if scanner.Scan(value) != nil { scanner.Scan(utils.ToString(value)) } } else if reflect.TypeOf("").ConvertibleTo(field.Type()) { field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type())) } else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) { field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type())) } else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) { field.Set(rvalue.Convert(field.Type())) } else if _, ok := field.Addr().Interface().(*time.Time); ok { if str := utils.ToString(value); str != "" { if newTime, err := now.Parse(str); err == nil { field.Set(reflect.ValueOf(newTime)) } } } else { var buf = bytes.NewBufferString("") json.NewEncoder(buf).Encode(value) if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil { utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.ValueOf(value).Type(), field.Type(), meta) } } } } } } } if nestedField { oldvalue := meta.Valuer meta.Valuer = func(value interface{}, context *qor.Context) interface{} { return oldvalue(utils.GetNestedModel(value, meta.Alias, context), context) } oldSetter := meta.Setter meta.Setter = func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context) { oldSetter(utils.GetNestedModel(resource, meta.Alias, context), metaValue, context) } } }
// ConfigureQorResourceBeforeInitialize configure qor resource for qor admin func (serialize *SerializableMeta) ConfigureQorResourceBeforeInitialize(res resource.Resourcer) { if res, ok := res.(*admin.Resource); ok { res.GetAdmin().RegisterViewPath("github.com/qor/serializable_meta/views") if _, ok := res.Value.(SerializableMetaInterface); ok { if res.GetMeta("Kind") == nil { res.Meta(&admin.Meta{ Name: "Kind", Type: "hidden", Valuer: func(value interface{}, context *qor.Context) interface{} { defer func() { if r := recover(); r != nil { utils.ExitWithMsg("SerializableMeta: Can't Get Kind") } }() return value.(SerializableMetaInterface).GetSerializableArgumentKind() }, Setter: func(value interface{}, metaValue *resource.MetaValue, context *qor.Context) { value.(SerializableMetaInterface).SetSerializableArgumentKind(utils.ToString(metaValue.Value)) }, }) } if res.GetMeta("SerializableMeta") == nil { res.Meta(&admin.Meta{ Name: "SerializableMeta", Type: "serializable_meta", Valuer: func(value interface{}, context *qor.Context) interface{} { if serializeArgument, ok := value.(SerializableMetaInterface); ok { return struct { Value interface{} Resource *admin.Resource }{ Value: serializeArgument.GetSerializableArgument(serializeArgument), Resource: serializeArgument.GetSerializableArgumentResource(), } } return nil }, FormattedValuer: func(value interface{}, context *qor.Context) interface{} { if serializeArgument, ok := value.(SerializableMetaInterface); ok { return serializeArgument.GetSerializableArgument(serializeArgument) } return nil }, Setter: func(result interface{}, metaValue *resource.MetaValue, context *qor.Context) { if serializeArgument, ok := result.(SerializableMetaInterface); ok { if serializeArgumentResource := serializeArgument.GetSerializableArgumentResource(); serializeArgumentResource != nil { var clearUpRecord, fillUpRecord func(record interface{}, metaors []resource.Metaor, metaValues []*resource.MetaValue) // Keep original value, so if user don't have permission to update some fields, we won't lost the data value := serializeArgument.GetSerializableArgument(serializeArgument) for _, fc := range serializeArgumentResource.Validators { context.AddError(fc(value, metaValue.MetaValues, context)) } // Clear all nested slices if has related form data clearUpRecord = func(record interface{}, metaors []resource.Metaor, metaValues []*resource.MetaValue) { for _, meta := range metaors { for _, metaValue := range metaValues { if meta.GetName() == metaValue.Name { if metaResource, ok := meta.GetResource().(*admin.Resource); ok && metaResource != nil && metaValue.MetaValues != nil { nestedFieldValue := reflect.Indirect(reflect.ValueOf(record)).FieldByName(meta.GetFieldName()) if nestedFieldValue.Kind() == reflect.Struct { clearUpRecord(nestedFieldValue.Addr().Interface(), metaResource.GetMetas([]string{}), metaValue.MetaValues.Values) } else if nestedFieldValue.Kind() == reflect.Slice { nestedFieldValue.Set(reflect.Zero(nestedFieldValue.Type())) } } } } } } clearUpRecord(value, serializeArgumentResource.GetMetas([]string{}), metaValue.MetaValues.Values) fillUpRecord = func(record interface{}, metaors []resource.Metaor, metaValues []*resource.MetaValue) { for _, meta := range metaors { for _, metaValue := range metaValues { if meta.GetName() == metaValue.Name { if metaResource, ok := meta.GetResource().(*admin.Resource); ok && metaResource != nil && metaValue.MetaValues != nil { nestedFieldValue := reflect.Indirect(reflect.ValueOf(record)).FieldByName(meta.GetFieldName()) if nestedFieldValue.Kind() == reflect.Struct { nestedValue := nestedFieldValue.Addr().Interface() for _, fc := range metaResource.Validators { context.AddError(fc(nestedValue, metaValue.MetaValues, context)) } fillUpRecord(nestedValue, metaResource.GetMetas([]string{}), metaValue.MetaValues.Values) for _, fc := range metaResource.Processors { context.AddError(fc(nestedValue, metaValue.MetaValues, context)) } } if nestedFieldValue.Kind() == reflect.Slice { nestedValue := reflect.New(nestedFieldValue.Type().Elem()) for _, fc := range metaResource.Validators { context.AddError(fc(nestedValue, metaValue.MetaValues, context)) } if destroy := metaValue.MetaValues.Get("_destroy"); destroy == nil || fmt.Sprint(destroy.Value) == "0" { fillUpRecord(nestedValue.Interface(), metaResource.GetMetas([]string{}), metaValue.MetaValues.Values) if !reflect.DeepEqual(reflect.Zero(nestedFieldValue.Type().Elem()).Interface(), nestedValue.Elem().Interface()) { nestedFieldValue.Set(reflect.Append(nestedFieldValue, nestedValue.Elem())) for _, fc := range metaResource.Processors { context.AddError(fc(nestedValue, metaValue.MetaValues, context)) } } } } continue } if setter := meta.GetSetter(); setter != nil { setter(record, metaValue, context) continue } } } } } fillUpRecord(value, serializeArgumentResource.GetMetas([]string{}), metaValue.MetaValues.Values) for _, fc := range serializeArgumentResource.Processors { context.AddError(fc(value, metaValue.MetaValues, context)) } serializeArgument.SetSerializableArgumentValue(value) } } }, }) } res.NewAttrs("Kind", "SerializableMeta") res.EditAttrs("ID", "Kind", "SerializableMeta") } } }