func New(db *gorm.DB) *Publish { tableHandler := gorm.DefaultTableNameHandler gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string { tableName := tableHandler(db, defaultTableName) if db != nil { if IsPublishableModel(db.Value) { // Set join table handler typ := utils.ModelType(db.Value) if !injectedJoinTableHandler[typ] { injectedJoinTableHandler[typ] = true scope := db.NewScope(db.Value) for _, field := range scope.GetModelStruct().StructFields { if many2many := utils.ParseTagOption(field.Tag.Get("gorm"))["MANY2MANY"]; many2many != "" { db.SetJoinTableHandler(db.Value, field.Name, &publishJoinTableHandler{}) db.AutoMigrate(db.Value) } } } var forceDraftTable bool if forceDraftTable, ok := db.Get("publish:force_draft_table"); ok { if forceMode, ok := forceDraftTable.(bool); ok && forceMode { forceDraftTable = true } } if IsDraftMode(db) || forceDraftTable { return DraftTableName(tableName) } } } return tableName } db.AutoMigrate(&PublishEvent{}) db.Callback().Create().Before("gorm:begin_transaction").Register("publish:set_table_to_draft", setTableAndPublishStatus(true)) db.Callback().Create().Before("gorm:commit_or_rollback_transaction"). Register("publish:sync_to_production_after_create", syncCreateFromProductionToDraft) db.Callback().Create().Before("gorm:commit_or_rollback_transaction").Register("gorm:create_publish_event", createPublishEvent) db.Callback().Delete().Before("gorm:begin_transaction").Register("publish:set_table_to_draft", setTableAndPublishStatus(true)) db.Callback().Delete().Replace("gorm:delete", deleteScope) db.Callback().Delete().Before("gorm:commit_or_rollback_transaction"). Register("publish:sync_to_production_after_delete", syncDeleteFromProductionToDraft) db.Callback().Delete().Before("gorm:commit_or_rollback_transaction").Register("gorm:create_publish_event", createPublishEvent) db.Callback().Update().Before("gorm:begin_transaction").Register("publish:set_table_to_draft", setTableAndPublishStatus(true)) db.Callback().Update().Before("gorm:commit_or_rollback_transaction"). Register("publish:sync_to_production", syncUpdateFromProductionToDraft) db.Callback().Update().Before("gorm:commit_or_rollback_transaction").Register("gorm:create_publish_event", createPublishEvent) db.Callback().RowQuery().Register("publish:set_table_in_draft_mode", setTableAndPublishStatus(false)) db.Callback().Query().Before("gorm:query").Register("publish:set_table_in_draft_mode", setTableAndPublishStatus(false)) return &Publish{DB: db} }
func parseTagOption(str string) *Option { option := Option(utils.ParseTagOption(str)) return &option }
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() { 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) } } }
func isSyncField(field *gorm.StructField) bool { if _, ok := utils.ParseTagOption(field.Tag.Get("l10n"))["SYNC"]; ok { return true } return false }
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() }