From 38551a4104af179d0ce13df97a5555f6be9e6bec Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Fri, 23 Oct 2026 18:40:59 +0200 Subject: [PATCH] fix: organisation attachment cleanup runs in FormAfterDelete FormBeforeDelete keeps its declaration and only releases the members; the avatar and logo rows are deleted by a new FormAfterDelete hook in the same transaction, their blobs after the commit. Keeps the plugin additive (no changed exported method declaration). --- controllers/organisations_admin_controller.go | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/controllers/organisations_admin_controller.go b/controllers/organisations_admin_controller.go index d1e93be..4656509 100644 --- a/controllers/organisations_admin_controller.go +++ b/controllers/organisations_admin_controller.go @@ -109,10 +109,8 @@ func (organisationsAdminController) RelationExtendManageQuery(_ context.Context, // FormBeforeDelete releases the members of the organisation in the delete's // transaction: organisation_id and organisation_role are cleared, on // deactivated users too, so the delete does not fail on the users foreign key -// and no user is deleted. The organisation's attachments (avatar and logo) -// are deleted in the same transaction, and their blobs once it commits, so -// no system_files row outlives the organisation row. -func (c organisationsAdminController) FormBeforeDelete(ctx context.Context, model any) error { +// and no user is deleted. +func (organisationsAdminController) FormBeforeDelete(ctx context.Context, model any) error { organisation, ok := model.(*models.Organisation) if !ok || organisation == nil { return errors.New("organisations admin: form model is not an organisation") @@ -121,10 +119,22 @@ func (c organisationsAdminController) FormBeforeDelete(ctx context.Context, mode if !ok { return errors.New("organisations admin: a delete needs its transaction") } - if err := tx.Session(&gorm.Session{NewDB: true}).WithContext(ctx).Unscoped(). + return tx.Session(&gorm.Session{NewDB: true}).WithContext(ctx).Unscoped(). Model(&models.User{}).Where("organisation_id = ?", organisation.ID). - Updates(map[string]any{"organisation_id": nil, "organisation_role": nil}).Error; err != nil { - return err + Updates(map[string]any{"organisation_id": nil, "organisation_role": nil}).Error +} + +// FormAfterDelete deletes the organisation's attachments (avatar and logo) +// in the delete's transaction, and their blobs once it commits, so no +// system_files row outlives the organisation row. +func (c organisationsAdminController) FormAfterDelete(ctx context.Context, model any) error { + organisation, ok := model.(*models.Organisation) + if !ok || organisation == nil { + return errors.New("organisations admin: form model is not an organisation") + } + tx, ok := cabana.TxFromContext(ctx) + if !ok { + return errors.New("organisations admin: a delete needs its transaction") } var keys []string if err := attach.DeleteForOwner(tx.Session(&gorm.Session{NewDB: true}).WithContext(ctx), organisation, strconv.FormatUint(uint64(organisation.ID), 10), func(blobKeys []string) error {