fix(17): WR-01 keep a menu entry's subtree and parent in its menu
- The admin refuses a parent from another menu than the one the form names (errors.parent_menu) instead of moving the entry to the parent's menu without a word; BeforeSave records the named menu (MenuEntry.RequestedMenuID). - An entry moved to another menu moves its descendants in the same transaction, so the public tree no longer drops them.
This commit is contained in:
@@ -24,6 +24,7 @@ const (
|
||||
errTypeUnknown = "golem15.menu::lang.errors.type_unknown"
|
||||
errMenuRequired = "golem15.menu::lang.errors.menu_required"
|
||||
errParentCycle = "golem15.menu::lang.errors.parent_cycle"
|
||||
errParentMenu = "golem15.menu::lang.errors.parent_menu"
|
||||
labelTypeURL = "golem15.menu::lang.models.menuentry.type_url"
|
||||
labelTypeDivide = "golem15.menu::lang.models.menuentry.type_divider"
|
||||
)
|
||||
@@ -183,11 +184,33 @@ func (menuEntriesAdminController) FormAfterCreate(ctx context.Context, model any
|
||||
}
|
||||
|
||||
// FormAfterUpdate also refuses a parent that is the entry itself or one of
|
||||
// its descendants.
|
||||
// its descendants, then moves the entry's descendants into its menu, so a
|
||||
// subtree follows its root to another menu instead of staying behind with
|
||||
// parents in a menu it is not in.
|
||||
func (menuEntriesAdminController) FormAfterUpdate(ctx context.Context, model any) error {
|
||||
return checkPlacement(ctx, model)
|
||||
if err := checkPlacement(ctx, model); err != nil {
|
||||
return err
|
||||
}
|
||||
entry, ok := model.(*models.MenuEntry)
|
||||
if !ok || entry == nil || entry.ID == 0 {
|
||||
return nil
|
||||
}
|
||||
tx, ok := cabana.TxFromContext(ctx)
|
||||
if !ok {
|
||||
return errors.New("golem15.menu: the entry check needs the save's transaction")
|
||||
}
|
||||
return tx.Session(&gorm.Session{NewDB: true}).WithContext(ctx).Exec(`WITH RECURSIVE sub(id) AS (
|
||||
SELECT id FROM golem15_menu_menu_entries WHERE parent_id = ? AND id <> ?
|
||||
UNION
|
||||
SELECT e.id FROM golem15_menu_menu_entries e JOIN sub ON e.parent_id = sub.id
|
||||
)
|
||||
UPDATE golem15_menu_menu_entries SET menu_id = ? WHERE id IN (SELECT id FROM sub) AND id <> ? AND menu_id <> ?`,
|
||||
entry.ID, entry.ID, entry.MenuID, entry.ID, entry.MenuID).Error
|
||||
}
|
||||
|
||||
// checkPlacement refuses an entry in no menu, a parent from another menu
|
||||
// than the one the entry names (BeforeSave would otherwise move the entry to
|
||||
// the parent's menu without a word) and a parent cycle.
|
||||
func checkPlacement(ctx context.Context, model any) error {
|
||||
entry, ok := model.(*models.MenuEntry)
|
||||
if !ok || entry == nil {
|
||||
@@ -196,6 +219,9 @@ func checkPlacement(ctx context.Context, model any) error {
|
||||
if entry.MenuID == 0 {
|
||||
return &cabana.ValidationError{Details: map[string]any{"menu": []string{errMenuRequired}}}
|
||||
}
|
||||
if entry.RequestedMenuID() != 0 {
|
||||
return &cabana.ValidationError{Details: map[string]any{"parent": []string{errParentMenu}}}
|
||||
}
|
||||
if entry.ParentID == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user