123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- package activity
- import (
- "context"
- "errors"
- "fmt"
- "reflect"
- "github.com/qor5/admin/presets"
- "github.com/qor5/web"
- "gorm.io/gorm"
- )
- const (
- Create = 1 << iota
- Delete
- Update
- )
- type contextKey int
- const (
- CreatorContextKey contextKey = iota
- DBContextKey
- )
- // @snippet_begin(ActivityBuilder)
- type ActivityBuilder struct {
- db *gorm.DB // global db
- creatorContextKey interface{} // get the creator from context
- dbContextKey interface{} // get the db from context
- lmb *presets.ModelBuilder // log model builder
- logModel ActivityLogInterface // log model
- models []*ModelBuilder // registered model builders
- tabHeading func(ActivityLogInterface) string // tab heading format
- }
- // @snippet_end
- func New(b *presets.Builder, db *gorm.DB, logModel ...ActivityLogInterface) *ActivityBuilder {
- ab := &ActivityBuilder{
- db: db,
- creatorContextKey: CreatorContextKey,
- dbContextKey: DBContextKey,
- }
- if len(logModel) > 0 {
- ab.logModel = logModel[0]
- } else {
- ab.logModel = &ActivityLog{}
- }
- if err := db.AutoMigrate(ab.logModel); err != nil {
- panic(err)
- }
- ab.configureAdmin(b)
- return ab
- }
- // GetPresetModelBuilder return the preset model builder
- func (ab ActivityBuilder) GetPresetModelBuilder() *presets.ModelBuilder {
- return ab.lmb
- }
- // GetActivityLogs get activity logs
- func (ab ActivityBuilder) GetActivityLogs(m interface{}, db *gorm.DB) []*ActivityLog {
- objs := ab.GetCustomizeActivityLogs(m, db)
- if objs == nil {
- return nil
- }
- logs, ok := objs.(*[]*ActivityLog)
- if !ok {
- return nil
- }
- return *logs
- }
- // GetCustomizeActivityLogs get customize activity logs
- func (ab ActivityBuilder) GetCustomizeActivityLogs(m interface{}, db *gorm.DB) interface{} {
- mb, ok := ab.GetModelBuilder(m)
- if !ok {
- return nil
- }
- if db == nil {
- db = ab.db
- }
- keys := mb.KeysValue(m)
- logs := ab.NewLogModelSlice()
- err := db.Where("model_name = ? AND model_keys = ?", mb.typ.Name(), keys).Find(logs).Error
- if err != nil {
- return nil
- }
- return logs
- }
- // NewLogModelData new a log model data
- func (ab ActivityBuilder) NewLogModelData() interface{} {
- return reflect.New(reflect.Indirect(reflect.ValueOf(ab.logModel)).Type()).Interface()
- }
- // NewLogModelSlice new a log model slice
- func (ab ActivityBuilder) NewLogModelSlice() interface{} {
- sliceType := reflect.SliceOf(reflect.PtrTo(reflect.Indirect(reflect.ValueOf(ab.logModel)).Type()))
- slice := reflect.New(sliceType)
- slice.Elem().Set(reflect.MakeSlice(sliceType, 0, 0))
- return slice.Interface()
- }
- // SetCreatorContextKey change the default creator context key
- func (ab *ActivityBuilder) SetCreatorContextKey(key interface{}) *ActivityBuilder {
- ab.creatorContextKey = key
- return ab
- }
- // SetDBContextKey change the default db context key
- func (ab *ActivityBuilder) SetDBContextKey(key interface{}) *ActivityBuilder {
- ab.dbContextKey = key
- return ab
- }
- func (ab *ActivityBuilder) SetTabHeading(f func(log ActivityLogInterface) string) *ActivityBuilder {
- ab.tabHeading = f
- return ab
- }
- // RegisterModels register mutiple models
- func (ab *ActivityBuilder) RegisterModels(models ...interface{}) *ActivityBuilder {
- for _, model := range models {
- ab.RegisterModel(model)
- }
- return ab
- }
- // Model register a model and return model builder
- func (ab *ActivityBuilder) RegisterModel(m interface{}) (mb *ModelBuilder) {
- if m, exist := ab.GetModelBuilder(m); exist {
- return m
- }
- model := getBasicModel(m)
- if model == nil {
- panic(fmt.Sprintf("%v is nil", m))
- }
- reflectType := reflect.Indirect(reflect.ValueOf(model)).Type()
- if reflectType.Kind() != reflect.Struct {
- panic(fmt.Sprintf("%v is not a struct", reflectType.Name()))
- }
- keys := getPrimaryKey(reflectType)
- mb = &ModelBuilder{
- typ: reflectType,
- activity: ab,
- keys: keys,
- ignoredFields: keys,
- }
- ab.models = append(ab.models, mb)
- if presetModel, ok := m.(*presets.ModelBuilder); ok {
- mb.presetModel = presetModel
- var (
- editing = presetModel.Editing()
- oldSaver = editing.Saver
- oldDeleter = editing.Deleter
- )
- editing.SaveFunc(func(obj interface{}, id string, ctx *web.EventContext) (err error) {
- if mb.skip&Update != 0 && mb.skip&Create != 0 {
- return oldSaver(obj, id, ctx)
- }
- old, ok := findOld(obj, ab.getDBFromContext(ctx.R.Context()))
- if err = oldSaver(obj, id, ctx); err != nil {
- return err
- }
- if (!ok || id == "") && mb.skip&Create == 0 {
- return mb.AddRecords(ActivityCreate, ctx.R.Context(), obj)
- }
- if ok && id != "" && mb.skip&Update == 0 {
- return mb.AddEditRecordWithOld(ab.getCreatorFromContext(ctx.R.Context()), old, obj, ab.getDBFromContext(ctx.R.Context()))
- }
- return
- })
- editing.DeleteFunc(func(obj interface{}, id string, ctx *web.EventContext) (err error) {
- if mb.skip&Delete != 0 {
- return oldDeleter(obj, id, ctx)
- }
- old, ok := findOldWithSlug(obj, id, ab.getDBFromContext(ctx.R.Context()))
- if err = oldDeleter(obj, id, ctx); err != nil {
- return err
- }
- if ok {
- return mb.AddRecords(ActivityDelete, ctx.R.Context(), old)
- }
- return
- })
- }
- return mb
- }
- // GetModelBuilder get model builder
- func (ab ActivityBuilder) GetModelBuilder(v interface{}) (*ModelBuilder, bool) {
- var isPreset bool
- if _, ok := v.(*presets.ModelBuilder); ok {
- isPreset = true
- }
- typ := reflect.Indirect(reflect.ValueOf(getBasicModel(v))).Type()
- for _, m := range ab.models {
- if m.typ == typ {
- if !isPreset {
- return m, true
- }
- if isPreset && m.presetModel == v {
- return m, true
- }
- }
- }
- return &ModelBuilder{}, false
- }
- // GetModelBuilder get model builder
- func (ab ActivityBuilder) MustGetModelBuilder(v interface{}) *ModelBuilder {
- mb, ok := ab.GetModelBuilder(v)
- if !ok {
- panic(fmt.Sprintf("model %v is not registered", v))
- }
- return mb
- }
- // GetModelBuilders get all model builders
- func (ab ActivityBuilder) GetModelBuilders() []*ModelBuilder {
- return ab.models
- }
- // AddRecords add records log
- func (ab *ActivityBuilder) AddRecords(action string, ctx context.Context, vs ...interface{}) error {
- if len(vs) == 0 {
- return errors.New("data are empty")
- }
- for _, v := range vs {
- if mb, ok := ab.GetModelBuilder(v); ok {
- if err := mb.AddRecords(action, ctx, v); err != nil {
- return err
- }
- }
- }
- return nil
- }
- // AddCustomizedRecord add customized record
- func (ab *ActivityBuilder) AddCustomizedRecord(action string, diff bool, ctx context.Context, obj interface{}) error {
- if mb, ok := ab.GetModelBuilder(obj); ok {
- return mb.AddCustomizedRecord(action, diff, ctx, obj)
- }
- return fmt.Errorf("can't find model builder for %v", obj)
- }
- // AddViewRecord add view record
- func (ab *ActivityBuilder) AddViewRecord(creator interface{}, v interface{}, db *gorm.DB) error {
- if mb, ok := ab.GetModelBuilder(v); ok {
- return mb.AddViewRecord(creator, v, db)
- }
- return fmt.Errorf("can't find model builder for %v", v)
- }
- // AddDeleteRecord add delete record
- func (ab *ActivityBuilder) AddDeleteRecord(creator interface{}, v interface{}, db *gorm.DB) error {
- if mb, ok := ab.GetModelBuilder(v); ok {
- return mb.AddDeleteRecord(creator, v, db)
- }
- return fmt.Errorf("can't find model builder for %v", v)
- }
- // AddSaverRecord will save a create log or a edit log
- func (ab *ActivityBuilder) AddSaveRecord(creator interface{}, now interface{}, db *gorm.DB) error {
- if mb, ok := ab.GetModelBuilder(now); ok {
- return mb.AddSaveRecord(creator, now, db)
- }
- return fmt.Errorf("can't find model builder for %v", now)
- }
- // AddCreateRecord add create record
- func (ab *ActivityBuilder) AddCreateRecord(creator interface{}, v interface{}, db *gorm.DB) error {
- if mb, ok := ab.GetModelBuilder(v); ok {
- return mb.AddCreateRecord(creator, v, db)
- }
- return fmt.Errorf("can't find model builder for %v", v)
- }
- // AddEditRecord add edit record
- func (ab *ActivityBuilder) AddEditRecord(creator interface{}, now interface{}, db *gorm.DB) error {
- if mb, ok := ab.GetModelBuilder(now); ok {
- return mb.AddEditRecord(creator, now, db)
- }
- return fmt.Errorf("can't find model builder for %v", now)
- }
- // AddEditRecord add edit record
- func (ab *ActivityBuilder) AddEditRecordWithOld(creator interface{}, old, now interface{}, db *gorm.DB) error {
- if mb, ok := ab.GetModelBuilder(now); ok {
- return mb.AddEditRecordWithOld(creator, old, now, db)
- }
- return fmt.Errorf("can't find model builder for %v", now)
- }
- // AddEditRecordWithOldAndContext add edit record
- func (ab *ActivityBuilder) AddEditRecordWithOldAndContext(ctx context.Context, old, now interface{}) error {
- if mb, ok := ab.GetModelBuilder(now); ok {
- return mb.AddEditRecordWithOld(ab.getCreatorFromContext(ctx), old, now, ab.getDBFromContext(ctx))
- }
- return fmt.Errorf("can't find model builder for %v", now)
- }
- // GetDB get db from context
- func (ab *ActivityBuilder) getDBFromContext(ctx context.Context) *gorm.DB {
- if contextdb := ctx.Value(ab.dbContextKey); contextdb != nil {
- return contextdb.(*gorm.DB)
- }
- return ab.db
- }
- // GetDB get creator from context
- func (ab *ActivityBuilder) getCreatorFromContext(ctx context.Context) interface{} {
- if creator := ctx.Value(ab.creatorContextKey); creator != nil {
- return creator
- }
- return ""
- }
|