landed "delete topic" function
This commit is contained in:
@@ -273,6 +273,48 @@ func StickTopic(ctxt ui.AmContext) (string, any, error) {
|
|||||||
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number), nil
|
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* DeleteTopic deletes the current topic.
|
||||||
|
* 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 DeleteTopic(ctxt ui.AmContext) (string, any, error) {
|
||||||
|
if ctxt.CurrentUser().IsAnon {
|
||||||
|
ctxt.SetRC(http.StatusForbidden)
|
||||||
|
return ui.ErrorPage(ctxt, ENOPERM)
|
||||||
|
}
|
||||||
|
conf := ctxt.GetScratch("currentConference").(*database.Conference)
|
||||||
|
myLevel := ctxt.GetScratch("levelInConference").(uint16)
|
||||||
|
topic := ctxt.GetScratch("currentTopic").(*database.Topic)
|
||||||
|
if !conf.TestPermission("Conference.Nuke", myLevel) {
|
||||||
|
ctxt.SetRC(http.StatusForbidden)
|
||||||
|
return ui.ErrorPage(ctxt, ENOPERM)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the message box, and, if we have a valid "yes," then perform the delete
|
||||||
|
mbox, err := ui.AmLoadMessageBox("deleteTopic")
|
||||||
|
if err != nil {
|
||||||
|
return ui.ErrorPage(ctxt, err)
|
||||||
|
}
|
||||||
|
if mbox.Validate(ctxt, "yes") {
|
||||||
|
err := topic.Delete(ctxt.Ctx(), ctxt.CurrentUser(), ctxt.RemoteIP(), ampool)
|
||||||
|
if err != nil {
|
||||||
|
return ui.ErrorPage(ctxt, err)
|
||||||
|
}
|
||||||
|
return "redirect", fmt.Sprintf("/comm/%s/conf/%s", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up to display the message box.
|
||||||
|
mbox.SetMessage(fmt.Sprintf(`You are about to detele the topic <span class="font-bold text-red-600">"%s"</span>
|
||||||
|
from the <span class="font-bold text-red-600">"%s"</span> conference!`, topic.Name, conf.Name))
|
||||||
|
mbox.SetLink("no", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number))
|
||||||
|
mbox.SetLink("yes", fmt.Sprintf("/comm/%s/conf/%s/op/%d/delete", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number))
|
||||||
|
return mbox.Render(ctxt)
|
||||||
|
}
|
||||||
|
|
||||||
/* HideMessage hides or shows a topic message.
|
/* HideMessage hides or shows a topic message.
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* ctxt - The AmContext for the request.
|
* ctxt - The AmContext for the request.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.erbosoft.com/amy/amsterdam/util"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@@ -289,6 +290,116 @@ func (t *Topic) GetSubscribers(ctx context.Context) ([]int32, error) {
|
|||||||
return rc, err
|
return rc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// backgroundPurgeTopic removes all posts from a topic that's been deleted.
|
||||||
|
func backgroundPurgeTopic(ctx context.Context, topicid int32) error {
|
||||||
|
success := false
|
||||||
|
tx := amdb.MustBegin()
|
||||||
|
defer func() {
|
||||||
|
if !success {
|
||||||
|
tx.Rollback()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Get some stats on the posts we have to remove.
|
||||||
|
row := tx.QueryRowContext(ctx, "SELECT MAX(postid) FROM posts WHERE topicid = ?", topicid)
|
||||||
|
var postMax int32
|
||||||
|
err := row.Scan(&postMax)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform wholesale deletes on auxiliary tables.
|
||||||
|
_, err = tx.ExecContext(ctx, "DELETE FROM postdata WHERE postid IN (SELECT postid FROM posts WHERE topicid = ? AND postid <= ?)", topicid, postMax)
|
||||||
|
if err == nil {
|
||||||
|
_, err = tx.ExecContext(ctx, "DELETE FROM postattach WHERE postid IN (SELECT postid FROM posts WHERE topicid = ? AND postid <= ?)", topicid, postMax)
|
||||||
|
if err == nil {
|
||||||
|
_, err = tx.ExecContext(ctx, "DELETE FROM postdogear WHERE postid IN (SELECT postid FROM posts WHERE topicid = ? AND postid <= ?)", topicid, postMax)
|
||||||
|
if err == nil {
|
||||||
|
_, err = tx.ExecContext(ctx, "DELETE FROM postpublish WHERE postid IN (SELECT postid FROM posts WHERE topicid = ? AND postid <= ?)", topicid, postMax)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now delete from the main posts table.
|
||||||
|
_, err = tx.ExecContext(ctx, "DELETE FROM posts WHERE topicid = ? AND postid <= ?", topicid, postMax)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = tx.Commit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
success = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes this topic.
|
||||||
|
func (t *Topic) Delete(ctx context.Context, u *User, ipaddr string, background *util.WorkerPool) error {
|
||||||
|
var ar *AuditRecord = nil
|
||||||
|
defer func() {
|
||||||
|
AmStoreAudit(ar)
|
||||||
|
}()
|
||||||
|
success := false
|
||||||
|
tx := amdb.MustBegin()
|
||||||
|
defer func() {
|
||||||
|
if !success {
|
||||||
|
tx.Rollback()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
unlock := true
|
||||||
|
tx.ExecContext(ctx, "LOCK TABLES confs WRITE, topics WRITE, topicsettings WRITE, topicbozo WRITE;")
|
||||||
|
defer func() {
|
||||||
|
if unlock {
|
||||||
|
tx.ExecContext(ctx, "UNLOCK TABLES;")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
conf, err := AmGetConference(ctx, t.ConfId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.ExecContext(ctx, "DELETE FROM topics WHERE topicid = ?", t.TopicId)
|
||||||
|
if err == nil {
|
||||||
|
_, err = tx.ExecContext(ctx, "DELETE FROM topicsettings WHERE topicid = ?", t.TopicId)
|
||||||
|
if err == nil {
|
||||||
|
_, err = tx.ExecContext(ctx, "DELETE FROM topicbozo WHERE topicid = ?", t.TopicId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = conf.TouchUpdate(ctx, tx, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tx.ExecContext(ctx, "UNLOCK TABLES;")
|
||||||
|
unlock = false
|
||||||
|
if err = tx.Commit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
success = true
|
||||||
|
|
||||||
|
// create audit record
|
||||||
|
ar = AmNewAudit(AuditConferenceDeleteTopic, u.Uid, ipaddr, fmt.Sprintf("confid=%d", conf.ConfId),
|
||||||
|
fmt.Sprintf("topic=%d", t.TopicId))
|
||||||
|
|
||||||
|
// Spin off a background task to finish deleting this topic.
|
||||||
|
myTopicId := t.TopicId
|
||||||
|
background.Submit(func(ctx context.Context) {
|
||||||
|
err := backgroundPurgeTopic(ctx, myTopicId)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("backgroundTopicPurge FAILED with %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// TopicSettings contains per-user settings for topics, including the "last read" message pointer.
|
// TopicSettings contains per-user settings for topics, including the "last read" message pointer.
|
||||||
type TopicSettings struct {
|
type TopicSettings struct {
|
||||||
TopicId int32 `db:"topicid"` // unique ID of the topic
|
TopicId int32 `db:"topicid"` // unique ID of the topic
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ _(italicized items can be deferred)_
|
|||||||
- ~~Stick/Unstick~~
|
- ~~Stick/Unstick~~
|
||||||
- ~~Freeze/Unfreeze~~
|
- ~~Freeze/Unfreeze~~
|
||||||
- ~~Archive/Unarchive~~
|
- ~~Archive/Unarchive~~
|
||||||
- Delete
|
- ~~Delete~~
|
||||||
- ~~Post Scribble~~
|
- ~~Post Scribble~~
|
||||||
- ~~Post Nuke~~
|
- ~~Post Nuke~~
|
||||||
- ~~Post Filter User~~
|
- ~~Post Filter User~~
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ func setupEcho() *echo.Echo {
|
|||||||
opsGroup.GET("/freeze", ui.AmWrap(FreezeTopic))
|
opsGroup.GET("/freeze", ui.AmWrap(FreezeTopic))
|
||||||
opsGroup.GET("/archive", ui.AmWrap(ArchiveTopic))
|
opsGroup.GET("/archive", ui.AmWrap(ArchiveTopic))
|
||||||
opsGroup.GET("/stick", ui.AmWrap(StickTopic))
|
opsGroup.GET("/stick", ui.AmWrap(StickTopic))
|
||||||
|
opsGroup.GET("/delete", ui.AmWrap(DeleteTopic))
|
||||||
opsGroup.GET("/hide/:msg", ui.AmWrap(HideMessage))
|
opsGroup.GET("/hide/:msg", ui.AmWrap(HideMessage))
|
||||||
opsGroup.GET("/scribble/:msg", ui.AmWrap(ScribbleMessage))
|
opsGroup.GET("/scribble/:msg", ui.AmWrap(ScribbleMessage))
|
||||||
opsGroup.GET("/nuke/:msg", ui.AmWrap(NukeMessage))
|
opsGroup.GET("/nuke/:msg", ui.AmWrap(NukeMessage))
|
||||||
|
|||||||
@@ -31,3 +31,27 @@ messagedefs:
|
|||||||
tone: "green"
|
tone: "green"
|
||||||
icon: "✗"
|
icon: "✗"
|
||||||
text: "No, Cancel"
|
text: "No, Cancel"
|
||||||
|
- id: "deleteTopic"
|
||||||
|
title: "Delete Topic"
|
||||||
|
tone: "red"
|
||||||
|
destructive: true
|
||||||
|
message: "You are about to delete a topic!"
|
||||||
|
warningIcon: "💣"
|
||||||
|
warningLines:
|
||||||
|
- text: "Warning: This action cannot be undone!"
|
||||||
|
bold: true
|
||||||
|
- text: "Deleting this topic will permanently remove it from the system."
|
||||||
|
bold: false
|
||||||
|
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"
|
||||||
|
|||||||
+1
-1
@@ -61,7 +61,7 @@
|
|||||||
</a>
|
</a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ if canDelete }}
|
{{ if canDelete }}
|
||||||
<a href="/TODO"
|
<a href="{{ topicListLink }}/op/{{ topicNum }}/delete"
|
||||||
class="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded text-sm font-medium transition-colors">Delete Topic</a>
|
class="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded text-sm font-medium transition-colors">Delete Topic</a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user