Files
mostlymatter/server/channels/app/draft.go
Harshil Sharma 6e5a67caec Feature edit attachments (#29769)
* Updated patch/update post API to allow file modification (#29447)

* WIP

* WIP

* Atatched new files ton post

* WIP: deleting removed files

* Deleted removed files and invalidated file metadata cache

* removed file ignore logif from update post API

* Added TestFindExclusives

* Added tests for DeleteForPostByIds

* Added app layer tests

* Added tests

* Added API level tests

* test enhancements

* Fixed a test

* Edit history include file metadata (#29505)

* Send file metadata in edit history metadata

* Added app tests

* Added store tests

* Added tests for populateEditHistoryFileMetadata{

* Added cache to avoid repetitigve DB calls for edits with only message changes

* Added API tests

* i18m fix

* removed commented code

* Improved test helper

* Show attachments in edit history RHS (#29519)

* Send file metadata in edit history metadata

* Added app tests

* Added store tests

* Added tests for populateEditHistoryFileMetadata{

* Added cache to avoid repetitigve DB calls for edits with only message changes

* Added API tests

* i18m fix

* WIUP: displa files in edit

* removed commented code

* Displayed file in edit history

* Handled file icon

* Fixed closing history component on clicking on file

* Simplified selector

* Simplified selector

* Improved test helper

* Disabled action menu on edit history file

* Added tests

* Improved selector

* Updated snapshot

* review Fixes

* restructured componnets

* Updated test

* Updated test

* Restore post api (#29643)

* Restore post version API WIP

* Undelete files WIP

* Added store tests

* Created post restore API

* Updated updatepost safeUpdate signature

* review fixex and improvements

* Fixed an app test

* Added API laer tests

* Added API tests and OpenAPI specs

* Fixed a typo

* Allow editing files when editing posts (#29709)

* WIP - basic view files when editing post

* Cleanup

* bg color

* Added text editor tests for files

* WIP

* WIP

* removed debug log

* Allowed admin to add and remove files on someone else's post

* Handled drafts and scheduled posts

* linter fixes

* Updated snapshot

* server test fix

* CI

* Added doc

* Restore post api integration (#29719)

* WIP - basic view files when editing post

* Cleanup

* bg color

* Added text editor tests for files

* WIP

* WIP

* removed debug log

* Allowed admin to add and remove files on someone else's post

* Handled drafts and scheduled posts

* linter fixes

* Updated snapshot

* server test fix

* Used new API to restore post

* handled edut limit and undo

* lint fix

* added comments

* Fixed edit post item tests

* Fixed buttons

* Aded snapshots

* fix test

* Updated snapshot

* Minor fixes

* fixed snapshot

* Edit file dnd area (#29763)

* dnd wip

* DND continued

* Supported multiple unbind dragster funcs

* lint fixes

* Got center channel file drop working when editing a post

* file dnd working with center channel and rhs

* file dnd working with center channel and rhs

* removed unneeded stopPropogation calls

* cleanup

* DND overlay fix

* Lint fix

* Advanced text editor test updates for file upload overlay

* fixed use upload hook tests

* Updated some more snapshots

* minor cleanup

* Updated i18n

* removed need of array for dragster unbind events

* lint fixes

* edit history cursor

* Fixed bugu causing faliure to delete empty posts (#29778)

* Files in restore confirmation (#29781)

* Added files to restore post confirmation dialog

* Fixed post restore toast colors

* Fixed restore bug

* Fixed restore confirmation toast tests

* a11y improvement and modal width fix

* Edit attachment misc fixes (#29808)

* Removed single image actions in restore post confirmation dialog

* Fixed file drop overlay size and position

* Made edit indiator accessible

* Lint fix

* Added bunch of more tests

* ANother test migrated from enzyme to react testing library

* More test enhancements

* More test enhancements

* More test enhancements

* lint fixes

* Fixed  a test

* Added missing snapshots

* Test fixes
2025-01-13 18:16:56 +05:30

162 строки
5.7 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"context"
"encoding/json"
"errors"
"net/http"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/public/shared/request"
"github.com/mattermost/mattermost/server/v8/channels/store"
)
func (a *App) GetDraft(userID, channelID, rootID string) (*model.Draft, *model.AppError) {
if !*a.Config().ServiceSettings.AllowSyncedDrafts {
return nil, model.NewAppError("GetDraft", "app.draft.feature_disabled", nil, "", http.StatusNotImplemented)
}
draft, err := a.Srv().Store().Draft().Get(userID, channelID, rootID, false)
if err != nil {
var nfErr *store.ErrNotFound
switch {
case errors.As(err, &nfErr):
return nil, model.NewAppError("GetDraft", "app.draft.get.app_error", nil, "", http.StatusNotFound).Wrap(err)
default:
return nil, model.NewAppError("GetDraft", "app.draft.get.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
return draft, nil
}
func (a *App) UpsertDraft(c request.CTX, draft *model.Draft, connectionID string) (*model.Draft, *model.AppError) {
if !*a.Config().ServiceSettings.AllowSyncedDrafts {
return nil, model.NewAppError("CreateDraft", "app.draft.feature_disabled", nil, "", http.StatusNotImplemented)
}
// Check that channel exists and has not been deleted
channel, errCh := a.Srv().Store().Channel().Get(draft.ChannelId, true)
if errCh != nil {
err := model.NewAppError("CreateDraft", "api.context.invalid_param.app_error", map[string]any{"Name": "draft.channel_id"}, "", http.StatusBadRequest).Wrap(errCh)
return nil, err
}
if channel.DeleteAt != 0 {
err := model.NewAppError("CreateDraft", "api.draft.create_draft.can_not_draft_to_deleted.error", nil, "", http.StatusBadRequest)
return nil, err
}
_, nErr := a.Srv().Store().User().Get(context.Background(), draft.UserId)
if nErr != nil {
return nil, model.NewAppError("CreateDraft", "app.user.get.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
}
// If the draft is empty, just delete it
if draft.Message == "" {
deleteErr := a.Srv().Store().Draft().Delete(draft.UserId, draft.ChannelId, draft.RootId)
if deleteErr != nil {
return nil, model.NewAppError("CreateDraft", "app.draft.save.app_error", nil, "", http.StatusInternalServerError).Wrap(deleteErr)
}
return nil, nil
}
dt, nErr := a.Srv().Store().Draft().Upsert(draft)
if nErr != nil {
return nil, model.NewAppError("CreateDraft", "app.draft.save.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
}
dt = a.prepareDraftWithFileInfos(c, draft.UserId, dt)
message := model.NewWebSocketEvent(model.WebsocketEventDraftCreated, "", dt.ChannelId, dt.UserId, nil, connectionID)
draftJSON, jsonErr := json.Marshal(dt)
if jsonErr != nil {
c.Logger().Warn("Failed to encode draft to JSON", mlog.Err(jsonErr))
}
message.Add("draft", string(draftJSON))
a.Publish(message)
return dt, nil
}
func (a *App) GetDraftsForUser(rctx request.CTX, userID, teamID string) ([]*model.Draft, *model.AppError) {
if !*a.Config().ServiceSettings.AllowSyncedDrafts {
return nil, model.NewAppError("GetDraftsForUser", "app.draft.feature_disabled", nil, "", http.StatusNotImplemented)
}
drafts, err := a.Srv().Store().Draft().GetDraftsForUser(userID, teamID)
if err != nil {
return nil, model.NewAppError("GetDraftsForUser", "app.draft.get_drafts.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
for _, draft := range drafts {
a.prepareDraftWithFileInfos(rctx, userID, draft)
}
return drafts, nil
}
func (a *App) prepareDraftWithFileInfos(rctx request.CTX, userID string, draft *model.Draft) *model.Draft {
if fileInfos, err := a.getFileInfosForDraft(rctx, draft); err != nil {
rctx.Logger().Error("Failed to get files for a user's drafts", mlog.String("user_id", userID), mlog.Err(err))
} else {
draft.Metadata = &model.PostMetadata{}
draft.Metadata.Files = fileInfos
}
return draft
}
func (a *App) getFileInfosForDraft(rctx request.CTX, draft *model.Draft) ([]*model.FileInfo, *model.AppError) {
if len(draft.FileIds) == 0 {
return nil, nil
}
allFileInfos, err := a.Srv().Store().FileInfo().GetByIds(draft.FileIds, false, true)
if err != nil {
return nil, model.NewAppError("GetFileInfosForDraft", "app.draft.get_for_draft.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
fileInfos := []*model.FileInfo{}
for _, fileInfo := range allFileInfos {
if fileInfo.PostId == "" && fileInfo.CreatorId == draft.UserId {
fileInfos = append(fileInfos, fileInfo)
} else {
rctx.Logger().Debug("Invalid file id in draft", mlog.String("file_id", fileInfo.Id), mlog.String("user_id", draft.UserId))
}
}
if len(fileInfos) == 0 {
return nil, nil
}
a.generateMiniPreviewForInfos(rctx, fileInfos)
return fileInfos, nil
}
func (a *App) DeleteDraft(rctx request.CTX, draft *model.Draft, connectionID string) *model.AppError {
if !*a.Config().ServiceSettings.AllowSyncedDrafts {
return model.NewAppError("DeleteDraft", "app.draft.feature_disabled", nil, "", http.StatusNotImplemented)
}
if err := a.Srv().Store().Draft().Delete(draft.UserId, draft.ChannelId, draft.RootId); err != nil {
return model.NewAppError("DeleteDraft", "app.draft.delete.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
draftJSON, jsonErr := json.Marshal(draft)
if jsonErr != nil {
rctx.Logger().Warn("Failed to encode draft to JSON")
}
message := model.NewWebSocketEvent(model.WebsocketEventDraftDeleted, "", draft.ChannelId, draft.UserId, nil, connectionID)
message.Add("draft", string(draftJSON))
a.Publish(message)
return nil
}