Files
mattermost/server/channels/api4/content_flagging_report.go
T
f0360a838a Data spillage report generation UI (#36340)
* Added base fr report generation

* WIP

* implemented UI flow

* implemented UI flow

* restructured the modal code into sub components

* Refactoring and cleanup

* lint fixes, added new tests

* i18n fix

* test fix

* Updated test

* CI

* Several improvements

* WIP

* Added tests

* Addressed some security enhancements

* Created zip writer entery later

* Improved a test to check for file content

* Improved error handling

* Made a geneeric function

* Updated classes

* accepting comment in report API

* Added more tests

* Integrated new API param

* Removed an unnecessary check

* Made a geneeric function

* Made a geneeric function

* Made the comment body not required and updated API docs

* Updated report generation API call in download report button

* Included decision in report and removed confirmation when keeping message

* Updated test

* Add explicit wait for removeWithoutReportButton visibility in test

Prevent race condition by waiting for the button to be visible after UI transitions to skip-confirm step before clicking it.

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

* PR Feedback

* explicitelly added return statement

* Included actor details in report

* Updated tests

---------

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-05-18 20:24:50 +05:30

104 lines
3.0 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package api4
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"github.com/mattermost/mattermost/server/v8/channels/app"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
)
func generateFlaggedPostReport(c *Context, w http.ResponseWriter, r *http.Request) {
if c.Err != nil {
return
}
c.RequirePostId()
if c.Err != nil {
return
}
var actionRequest model.FlagContentActionRequest
if err := json.NewDecoder(r.Body).Decode(&actionRequest); err != nil && !errors.Is(err, io.EOF) {
c.SetInvalidParamWithErr("flagContentActionRequestBody", err)
return
}
postId := c.Params.PostId
userId := c.AppContext.Session().UserId
auditRec := c.MakeAuditRecord(model.AuditEventGenerateFlaggedPostReport, model.AuditStatusFail)
defer c.LogAuditRecWithLevel(auditRec, app.LevelContent)
model.AddEventParameterToAuditRec(auditRec, "flaggedPostId", postId)
model.AddEventParameterToAuditRec(auditRec, "userId", userId)
model.AddEventParameterToAuditRec(auditRec, "comment", actionRequest.Comment)
model.AddEventParameterToAuditRec(auditRec, "action", actionRequest.Action)
post, appErr := c.App.GetSinglePost(c.AppContext, postId, true)
if appErr != nil {
c.Err = appErr
return
}
channel, appErr := c.App.GetChannel(c.AppContext, post.ChannelId)
if appErr != nil {
c.Err = appErr
return
}
requireTeamContentReviewer(c, userId, channel.TeamId)
if c.Err != nil {
return
}
// This validates that the post is flagged
requireFlaggedPost(c, postId)
if c.Err != nil {
return
}
reportPath, appErr := c.App.GenerateFlaggedPostReport(c.AppContext, postId, userId, actionRequest.Comment, actionRequest.Action)
if appErr != nil {
c.Err = appErr
return
}
defer func() {
if err := os.Remove(reportPath); err != nil && !os.IsNotExist(err) {
c.Logger.Warn("Failed to remove flagged post report temp file", mlog.String("path", reportPath), mlog.Err(err))
}
}()
f, err := os.Open(reportPath)
if err != nil {
c.Err = model.NewAppError("generateFlaggedPostReport", "api.data_spillage.report.open.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
return
}
defer f.Close()
stat, err := f.Stat()
if err != nil {
c.Err = model.NewAppError("generateFlaggedPostReport", "api.data_spillage.report.stat.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
return
}
// Notify all team reviewers that a report has been generated. Best-effort:
// must run before http.ServeContent (which writes the response and may block).
c.App.NotifyReviewersOfFlaggedPostReportGeneration(c.AppContext, postId, userId)
filename := fmt.Sprintf("flagged-post-%s-%d.zip", postId, model.GetMillis())
w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
http.ServeContent(w, r, filename, stat.ModTime(), f)
auditRec.Success()
}