package controllers import ( "context" "errors" "sync/atomic" "git.golem15.com/golem15/summercms/modules/cabana" "git.golem15.com/golem15/summercms/modules/pact" "git.golem15.com/jakub/sm-bm-plugin/models" "gorm.io/gorm" ) var ( _ pact.FormAfterCreate = materialsAdmin{} _ pact.FormAfterUpdate = materialsAdmin{} _ pact.FormAfterDelete = materialsAdmin{} ) // MaterialIndex keeps the materials search index current once a write // commits (classes.MaterialIndexer). QueueExtract enqueues the file text job // inside tx. Its calls never fail the write. type MaterialIndex interface { SyncAfterCommit(ctx context.Context, tx *gorm.DB, id uint) RemoveAfterCommit(ctx context.Context, tx *gorm.DB, id uint) QueueExtract(ctx context.Context, tx *gorm.DB, id uint) } type materialIndexHolder struct{ ix MaterialIndex } var materialIndex atomic.Value // UseMaterialIndex installs the index the materials admin syncs after a // save or delete; nil turns the syncs off. Boot installs it, because the // generated controller value has no field to carry it. func UseMaterialIndex(ix MaterialIndex) { materialIndex.Store(materialIndexHolder{ix: ix}) } func installedMaterialIndex() MaterialIndex { h, _ := materialIndex.Load().(materialIndexHolder) return h.ix } // FormAfterCreate queues the file text job in the save transaction (the // form's deferred files commit in the same one) and syncs the new material // once it commits. func (materialsAdmin) FormAfterCreate(ctx context.Context, model any) error { return materialAfterCommit(ctx, model, false) } // FormAfterUpdate queues the file text job in the save transaction and // syncs the material once it commits. A save that added no file queues a // job that finds no new PDF and only resyncs; a file removed in the form // leaves the document through the cascade and this sync. func (materialsAdmin) FormAfterUpdate(ctx context.Context, model any) error { return materialAfterCommit(ctx, model, false) } // FormAfterDelete removes the material's document once the delete commits. func (materialsAdmin) FormAfterDelete(ctx context.Context, model any) error { return materialAfterCommit(ctx, model, true) } // materialAfterCommit schedules the index work of an admin save on the save // transaction. Only a model that is not a material is an error: the index // never fails a save. func materialAfterCommit(ctx context.Context, model any, remove bool) error { m, ok := model.(*models.Material) if !ok || m == nil { return errors.New("materials admin: form model is not a material") } ix := installedMaterialIndex() if ix == nil || m.ID == 0 { return nil } tx, _ := cabana.TxFromContext(ctx) if remove { ix.RemoveAfterCommit(ctx, tx, m.ID) return nil } ix.QueueExtract(ctx, tx, m.ID) ix.SyncAfterCommit(ctx, tx, m.ID) return nil }