feat(19.3-04): pact.ReorderAfterSave hook inside cabana's reorder transaction

- optional ReorderAfterSave(ctx, partition) runs after the new order is
  written; an error rolls the reorder back through lifecycleFailure
- reorderPost runs in lagoon.Transaction so lagoon.AfterCommit work a hook
  registers (a site-cache purge) runs only after the commit
- pact and cabana READMEs, admin hook table, tree reorder docs
This commit is contained in:
Jakub Zych
2026-10-15 18:27:07 +02:00
parent 0bc390f39f
commit af9dad634c
7 changed files with 28 additions and 6 deletions

View File

@@ -13,6 +13,7 @@ import (
"strconv"
"strings"
"git.golem15.com/golem15/summercms/modules/lagoon"
"git.golem15.com/golem15/summercms/modules/pact"
"git.golem15.com/golem15/summercms/modules/towel"
"gorm.io/gorm"
@@ -293,7 +294,10 @@ func (s *service) reorderGet(w http.ResponseWriter, r *http.Request) {
// requires the submitted ids to equal it exactly (409 reorder_stale
// otherwise), refuses a duplicate id, a parent outside the set, a cycle and a
// depth above maxDepth (422), then writes the parent and sort columns of
// every node and answers the new tree.
// every node, runs the controller's optional ReorderAfterSave hook (pact) with
// the partition (its error rolls the reorder back) and answers the new tree.
// The transaction is a lagoon.Transaction, so a hook can register work with
// lagoon.AfterCommit that runs only once the new order is committed.
func (s *service) reorderPost(w http.ResponseWriter, r *http.Request) {
s.protect(w, r, func(cc *CompiledController) {
if cc.reorder == nil || cc.singleton {
@@ -312,8 +316,8 @@ func (s *service) reorderPost(w http.ResponseWriter, r *http.Request) {
}
ctx := towel.WithLocale(r.Context(), schemaLocale(r.Context(), s.translator()))
var tree AdminReorderTree
err = db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
txCtx := withTx(ctx, tx)
err = lagoon.Transaction(ctx, db, func(lctx context.Context, tx *gorm.DB) error {
txCtx := withTx(lctx, tx)
if cc.reorder.partition != nil && in.Partition == nil {
return &ValidationError{Details: map[string]any{"partition": []string{"The partition field is required."}}}
}
@@ -347,6 +351,11 @@ func (s *service) reorderPost(w http.ResponseWriter, r *http.Request) {
return lifecycleFailure(cc, err)
}
}
if hook, ok := cc.Controller.(pact.ReorderAfterSave); ok && hook != nil {
if err := lifecycleFailure(cc, hook.ReorderAfterSave(txCtx, in.Partition)); err != nil {
return err
}
}
tree, err = s.reorderTree(txCtx, tx, cc, in.Partition, false)
return err
})