diff --git a/communityadmin.go b/communityadmin.go index 875e31d..9f95f76 100644 --- a/communityadmin.go +++ b/communityadmin.go @@ -730,6 +730,40 @@ func CommunityEmail(ctxt ui.AmContext) (string, any) { return "redirect", fmt.Sprintf("/comm/%s/admin", comm.Alias) } +/* DeleteCommunity handles the deletion of a community. + * Parameters: + * ctxt - The AmContext for the request. + * Returns: + * Command string dictating what to be rendered. + * Data as a parameter for the command string. + * Standard Go error status. + */ +func DeleteCommunity(ctxt ui.AmContext) (string, any) { + comm := ctxt.CurrentCommunity() + if !comm.TestPermission("Community.Delete", ctxt.EffectiveLevel()) { + return "error", ENOACCESS + } + + // Load the message box, and, if we have a valid "yes," then perform the delete + mbox, err := ui.AmLoadMessageBox("deleteComm") + if err != nil { + return "error", err + } + if mbox.Validate(ctxt, "yes") { + err := comm.Delete(ctxt.Ctx(), ctxt.CurrentUser(), ctxt.RemoteIP(), ampool) + if err != nil { + return "error", err + } + return "redirect", "/" + } + + // Set up to display the message box. + mbox.SetMessage(fmt.Sprintf(`You are about to permanently delete the "%s" community!`, comm.Name)) + mbox.SetLink("no", fmt.Sprintf("/comm/%s/admin", comm.Alias)) + mbox.SetLink("yes", fmt.Sprintf("/comm/%s/admin/delete", comm.Alias)) + return mbox.Render(ctxt) +} + /* CreateCommunityForm renders the form for creating a new community. * Parameters: * ctxt - The AmContext for the request. diff --git a/database/community.go b/database/community.go index 57ac1c2..9664d9f 100644 --- a/database/community.go +++ b/database/community.go @@ -591,6 +591,51 @@ func (c *Community) GetMemberEMailAddrs(ctx context.Context) ([]string, error) { return rc, err } +// Delete deletes this community. +func (c *Community) Delete(ctx context.Context, u *User, ipaddr string, background *util.WorkerPool) error { + tx, commit, rollback := transaction(ctx) + defer rollback() + + // Start by deleting all the community's services. This will purge the conferences, among other things. + err := AmDeleteCommunityServices(ctx, tx, c.Id, background) + if err != nil { + return err + } + + // Now erase from the other tables as well: members, bans, properties, and communities themselves. + _, err = tx.ExecContext(ctx, "DELETE FROM commmember WHERE commid = ?", c.Id) + if err == nil { + _, err = tx.ExecContext(ctx, "DELETE FROM commban WHERE commid = ?", c.Id) + if err == nil { + _, err = tx.ExecContext(ctx, "DELETE FROM propcomm WHERE cid = ?", c.Id) + if err == nil { + _, err = tx.ExecContext(ctx, "DELETE FROM communities WHERE commid = ?", c.Id) + } + } + } + if err != nil { + return err + } + if err = commit(); err != nil { + return err + } + + // Purge the member and properties caches, and punt this community from the community cache. + memberMutex.Lock() + memberCache.Purge() + memberMutex.Unlock() + getCommunityPropMutex.Lock() + communityPropCache.Purge() + getCommunityPropMutex.Unlock() + getCommunityMutex.Lock() + communityCache.Remove(c.Id) + getCommunityMutex.Unlock() + + // Save off an audit record for the delete. + AmStoreAudit(AmNewCommAudit(AuditCommunityDelete, u.Uid, c.Id, ipaddr)) + return nil +} + /* AmGetCommunity returns a reference to the specified community. * Parameters: * ctx - Standard Go context value. diff --git a/database/conference.go b/database/conference.go index 678bb9e..cfbd819 100644 --- a/database/conference.go +++ b/database/conference.go @@ -937,6 +937,11 @@ func (*conferenceServiceVTable) OnDeleteCommunity(ctx context.Context, tx *sqlx. if err != nil { return err } + // Delete any mention of this community from the conference hotlists. + _, err = tx.ExecContext(ctx, "DELETE FROM confhotlist WHERE commid = ?", commid) + if err != nil { + return err + } for i, confid := range confids { // any references to conference other than this community? refCount := 0 diff --git a/docs/MISSINGFUNCS.md b/docs/MISSINGFUNCS.md index 700a884..b2cb793 100644 --- a/docs/MISSINGFUNCS.md +++ b/docs/MISSINGFUNCS.md @@ -23,12 +23,12 @@ _(italicized items can be deferred)_ - ~~Manage (reorder/show/hide/delete)~~ - ~~Create New~~ - ~~Conferences List honor "hide in list" flag~~ -- Community Admin Menu: +- ~~Community Admin Menu:~~ - ~~Set Community Category~~ - ~~Membership Control~~ - ~~E-Mail to All Members~~ - ~~Display Audit Records~~ - - Delete Community + - ~~Delete Community~~ - ~~Community Profile: Invite~~ - _Help link atop page headers_ - _Policy page_ diff --git a/main.go b/main.go index b23127c..93e61af 100644 --- a/main.go +++ b/main.go @@ -142,6 +142,7 @@ func setupEcho() *echo.Echo { adminGroup.Match(GetAndPost, "/members", ui.AmWrap(CommunityMembers)) adminGroup.GET("/massmail", ui.AmWrap(CommunityEmailForm)) adminGroup.POST("/massmail", ui.AmWrap(CommunityEmail)) + adminGroup.GET("/delete", ui.AmWrap(DeleteCommunity)) // conference group commGroup.GET("/create_conf", ui.AmWrap(CreateConferenceForm)) diff --git a/ui/menudefs.yaml b/ui/menudefs.yaml index 0d96513..4f53153 100644 --- a/ui/menudefs.yaml +++ b/ui/menudefs.yaml @@ -81,7 +81,7 @@ menudefs: link: "/comm/[CID]/admin/audit" permission: "Community.ShowAdmin" - text: "Delete Community" - link: "/TODO/comm/[CID]/admin/delete" + link: "/comm/[CID]/admin/delete" permission: "Community.Delete" hazard: true - id: "confhost" diff --git a/ui/messagedefs.yaml b/ui/messagedefs.yaml index e171a40..c6e202e 100644 --- a/ui/messagedefs.yaml +++ b/ui/messagedefs.yaml @@ -105,3 +105,31 @@ messagedefs: tone: "green" icon: "✗" text: "No, Cancel" + - id: "deleteComm" + title: "Delete Community" + tone: "red" + destructive: true + message: "You are about to delete a community!" + warningIcon: "💣" + warningLines: + - text: "Warning: This action cannot be undone!" + bold: true + - text: "Deleting this community will permanently remove it and all its contents from the system." + bold: false + - text: "Hundreds or even thousands of individual contributions will be lost forever." + bold: false + - text: "Please consider this action very carefully!" + bold: true + buttons: + - id: "yes" + link: "placeholder" + confirm: true + tone: "red" + icon: "✓" + text: "Yes, Delete It" + - id: "no" + link: "placeholder" + confirm: false + tone: "green" + icon: "✗" + text: "No, Cancel"