Channel Bookmarks (#25449)
* create ChannelBookmarks table * ChannelBookmark model * channel bookamrks Store layer * add GetBookmarksForAllChannelByIdSince * add channel bookmarks to test store * Add channel bookmarks to app layer * remove index for createAt in channel bookmarks migrations * remove createAt from select channel bookmark query and enable store delete bookmark test * update reponse of UpdateBookmark * rename db migration files * channel bookmarks store update sort order * channel bookmarks app layer update sort order * fix lint & tests * Fix lint and introduce util functions to insert / remove from slice * remove model etag * i18n * defer remove file info after test run * Fix tests passing the request context * fix migrations * fix TestRetry * Add bookmark permissions (#25560) * Adds channel bookmarks permissions * Fix linter * Remove unnecessary empty lines * Remove scss change as it's not necessary anymore * Fix mock store * Fix mock store and add role entry * Fix test * Adds cypress test and update permissions migration to update admin roles * Adds channel bookmarks roles to default admin roles * Adds bookmark permissions to default role permissions constant in webapp * Update mmctl test * Update permission test after normalising the roles * fix store tests * fix app layer tests * Add new bookmark endpoint (#25624) * Adds channel bookmarks api scaffold and create endpoint * Applies review comments to the API docs * Adds websocket test to create channel bookmark --------- Co-authored-by: Mattermost Build <build@mattermost.com> * MM-54426 exclude Channel Bookmarks files from data retention (#25656) * Augment channel APIs to include bookmarks (#25567) * update files docs for server 9.4 * Adds update channel bookmark endpoint (#25653) * Adds update channel bookmark sort order endpoint (#25686) * Adds update channel bookmark endpoint * Updates edit app method to return the right deleted bookmark and adds tests * Adds the update channel bookmark sort order endpoint * Fix repeated test after merge * Assign right permissions to each test * Update store and app layer to return specific errors and add tests * Adds delete channel bookmark endpoint (#25693) * Updates edit app method to return the right deleted bookmark and adds tests * Fix repeated test after merge * Updates edit app method to return the right deleted bookmark and adds tests * Adds delete channel bookmark endpoint * Adds list channel bookmarks endpoint (#25700) * Add channel moderation to bookmarks (#25716) * fix migrations index * fix getChannelsForTeamForUser * fix getChannelsForTeamForUser * fix bad merge client4 * fix file api with bookmark permission * add ChannelBookmarks feature flag * add missing translations * Set DB column for type as enum * use custom type for bookmark query using sqlx * use transaction when saving bookmark * return NewErrNotFound instead of Sql.ErrNoRows * use squirrel for IN query * add a limit of 1K for records in GetBookmarksForAllChannelByIdSince * UpdateSortOrder with one single query instead of multiple updates * fix shadow declaration * fix channel bookmarks permission string definition in admin console * fix another shadow declaration * Fix model conversion * add SplitSliceInChunks * remove include bookmarks in channels api * Cap amount of bookmarks per channel * add etag back to get channels * feedback review * update file info when replacing a bookmark file * return 501 not implemented when the license is not available * add detail message when getting channel member on bookmark api * start audit before permission check on create bookmark api * use require.Eventuallyf for testing WS events * remove unnecessary log in app layer * use require instead of assert to avoid panics * enforce limit when querying bookmarks since * prevent to create/update bookmark if file is already attached * fix lint * delete file when a bookmark is deleted * Dot allow to set a fileId and a url at the same time to a bookmark * fix query to delete a file that belongs to a bookmark * do not patch the bookmark type * Server side FeatureFlag check (#26145) * use ff in server, set ff to false * turn on FF for unit tests * defer unset FF for unit tests * turn ff on for testing * only allow attaching files that were uploaded for bookmark * Set feature flag off as default * fix lint * update email templates as PR failed * revert templates * force the assignment of ID when creating a bookmark * Fix unit tests --------- Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Caleb Roseland <caleb@calebroseland.com> Co-authored-by: Scott Bishel <scott.bishel@mattermost.com>
Этот коммит содержится в:
@@ -506,6 +506,7 @@ type AppIface interface {
|
||||
CopyFileInfos(rctx request.CTX, userID string, fileIDs []string) ([]string, *model.AppError)
|
||||
CopyWranglerPostlist(c request.CTX, wpl *model.WranglerPostList, targetChannel *model.Channel) (*model.Post, *model.AppError)
|
||||
CreateChannel(c request.CTX, channel *model.Channel, addMember bool) (*model.Channel, *model.AppError)
|
||||
CreateChannelBookmark(c request.CTX, newBookmark *model.ChannelBookmark, connectionId string) (*model.ChannelBookmarkWithFileInfo, *model.AppError)
|
||||
CreateChannelWithUser(c request.CTX, channel *model.Channel, userID string) (*model.Channel, *model.AppError)
|
||||
CreateCommand(cmd *model.Command) (*model.Command, *model.AppError)
|
||||
CreateCommandWebhook(commandID string, args *model.CommandArgs) (*model.CommandWebhook, *model.AppError)
|
||||
@@ -550,6 +551,7 @@ type AppIface interface {
|
||||
DeleteAllKeysForPlugin(pluginID string) *model.AppError
|
||||
DeleteBrandImage(rctx request.CTX) *model.AppError
|
||||
DeleteChannel(c request.CTX, channel *model.Channel, userID string) *model.AppError
|
||||
DeleteChannelBookmark(bookmarkId, connectionId string) (*model.ChannelBookmarkWithFileInfo, *model.AppError)
|
||||
DeleteCommand(commandID string) *model.AppError
|
||||
DeleteDraft(rctx request.CTX, draft *model.Draft, connectionID string) *model.AppError
|
||||
DeleteEmoji(c request.CTX, emoji *model.Emoji) *model.AppError
|
||||
@@ -627,9 +629,11 @@ type AppIface interface {
|
||||
GetAuditsPage(rctx request.CTX, userID string, page int, perPage int) (model.Audits, *model.AppError)
|
||||
GetAuthorizationCode(c request.CTX, w http.ResponseWriter, r *http.Request, service string, props map[string]string, loginHint string) (string, *model.AppError)
|
||||
GetAuthorizedAppsForUser(userID string, page, perPage int) ([]*model.OAuthApp, *model.AppError)
|
||||
GetBookmark(bookmarkId string, includeDeleted bool) (*model.ChannelBookmarkWithFileInfo, *model.AppError)
|
||||
GetBrandImage(rctx request.CTX) ([]byte, *model.AppError)
|
||||
GetBulkReactionsForPosts(postIDs []string) (map[string][]*model.Reaction, *model.AppError)
|
||||
GetChannel(c request.CTX, channelID string) (*model.Channel, *model.AppError)
|
||||
GetChannelBookmarks(channelId string, since int64) ([]*model.ChannelBookmarkWithFileInfo, *model.AppError)
|
||||
GetChannelByName(c request.CTX, channelName, teamID string, includeDeleted bool) (*model.Channel, *model.AppError)
|
||||
GetChannelByNameForTeamName(c request.CTX, channelName, teamName string, includeDeleted bool) (*model.Channel, *model.AppError)
|
||||
GetChannelCounts(c request.CTX, teamID string, userID string) (*model.ChannelCounts, *model.AppError)
|
||||
@@ -1147,6 +1151,8 @@ type AppIface interface {
|
||||
UnregisterPluginForSharedChannels(pluginID string) error
|
||||
UnshareChannel(channelID string) (bool, error)
|
||||
UpdateActive(c request.CTX, user *model.User, active bool) (*model.User, *model.AppError)
|
||||
UpdateChannelBookmark(c request.CTX, updateBookmark *model.ChannelBookmarkWithFileInfo, connectionId string) (*model.UpdateChannelBookmarkResponse, *model.AppError)
|
||||
UpdateChannelBookmarkSortOrder(bookmarkId, channelId string, newIndex int64, connectionId string) ([]*model.ChannelBookmarkWithFileInfo, *model.AppError)
|
||||
UpdateChannelMemberNotifyProps(c request.CTX, data map[string]string, channelID string, userID string) (*model.ChannelMember, *model.AppError)
|
||||
UpdateChannelMemberRoles(c request.CTX, channelID string, userID string, newRoles string) (*model.ChannelMember, *model.AppError)
|
||||
UpdateChannelMemberSchemeRoles(c request.CTX, channelID string, userID string, isSchemeGuest bool, isSchemeUser bool, isSchemeAdmin bool) (*model.ChannelMember, *model.AppError)
|
||||
|
||||
@@ -127,10 +127,26 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) {
|
||||
model.PermissionManagePrivateChannelMembers.Id,
|
||||
model.PermissionDeletePost.Id,
|
||||
model.PermissionEditPost.Id,
|
||||
model.PermissionAddBookmarkPublicChannel.Id,
|
||||
model.PermissionEditBookmarkPublicChannel.Id,
|
||||
model.PermissionDeleteBookmarkPublicChannel.Id,
|
||||
model.PermissionOrderBookmarkPublicChannel.Id,
|
||||
model.PermissionAddBookmarkPrivateChannel.Id,
|
||||
model.PermissionEditBookmarkPrivateChannel.Id,
|
||||
model.PermissionDeleteBookmarkPrivateChannel.Id,
|
||||
model.PermissionOrderBookmarkPrivateChannel.Id,
|
||||
},
|
||||
"channel_admin": {
|
||||
model.PermissionManageChannelRoles.Id,
|
||||
model.PermissionUseGroupMentions.Id,
|
||||
model.PermissionAddBookmarkPublicChannel.Id,
|
||||
model.PermissionEditBookmarkPublicChannel.Id,
|
||||
model.PermissionDeleteBookmarkPublicChannel.Id,
|
||||
model.PermissionOrderBookmarkPublicChannel.Id,
|
||||
model.PermissionAddBookmarkPrivateChannel.Id,
|
||||
model.PermissionEditBookmarkPrivateChannel.Id,
|
||||
model.PermissionDeleteBookmarkPrivateChannel.Id,
|
||||
model.PermissionOrderBookmarkPrivateChannel.Id,
|
||||
},
|
||||
"team_user": {
|
||||
model.PermissionListTeamChannels.Id,
|
||||
@@ -166,6 +182,14 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) {
|
||||
model.PermissionConvertPrivateChannelToPublic.Id,
|
||||
model.PermissionDeletePost.Id,
|
||||
model.PermissionDeleteOthersPosts.Id,
|
||||
model.PermissionAddBookmarkPublicChannel.Id,
|
||||
model.PermissionEditBookmarkPublicChannel.Id,
|
||||
model.PermissionDeleteBookmarkPublicChannel.Id,
|
||||
model.PermissionOrderBookmarkPublicChannel.Id,
|
||||
model.PermissionAddBookmarkPrivateChannel.Id,
|
||||
model.PermissionEditBookmarkPrivateChannel.Id,
|
||||
model.PermissionDeleteBookmarkPrivateChannel.Id,
|
||||
model.PermissionOrderBookmarkPrivateChannel.Id,
|
||||
},
|
||||
"system_user": {
|
||||
model.PermissionListPublicTeams.Id,
|
||||
|
||||
@@ -1122,7 +1122,7 @@ func buildChannelModerations(c request.CTX, channelType model.ChannelType, membe
|
||||
Enabled: higherScopedMemberPermissions[permissionKey],
|
||||
}
|
||||
|
||||
if permissionKey == "manage_members" {
|
||||
if permissionKey == "manage_members" || permissionKey == "manage_bookmarks" {
|
||||
roles.Guests = nil
|
||||
} else {
|
||||
roles.Guests = &model.ChannelModeratedRole{
|
||||
|
||||
152
server/channels/app/channel_bookmark.go
Обычный файл
152
server/channels/app/channel_bookmark.go
Обычный файл
@@ -0,0 +1,152 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/store"
|
||||
)
|
||||
|
||||
func (a *App) GetChannelBookmarks(channelId string, since int64) ([]*model.ChannelBookmarkWithFileInfo, *model.AppError) {
|
||||
bookmarks, err := a.Srv().Store().ChannelBookmark().GetBookmarksForChannelSince(channelId, since)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetChannelBookmarks", "app.channel.bookmark.get.app_error", nil, "", http.StatusNotFound).Wrap(err)
|
||||
}
|
||||
|
||||
return bookmarks, nil
|
||||
}
|
||||
|
||||
func (a *App) GetBookmark(bookmarkId string, includeDeleted bool) (*model.ChannelBookmarkWithFileInfo, *model.AppError) {
|
||||
bookmark, err := a.Srv().Store().ChannelBookmark().Get(bookmarkId, includeDeleted)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetBookmark", "app.channel.bookmark.get.app_error", nil, "", http.StatusNotFound).Wrap(err)
|
||||
}
|
||||
|
||||
return bookmark, nil
|
||||
}
|
||||
|
||||
func (a *App) CreateChannelBookmark(c request.CTX, newBookmark *model.ChannelBookmark, connectionId string) (*model.ChannelBookmarkWithFileInfo, *model.AppError) {
|
||||
newBookmark.OwnerId = c.Session().UserId //ensure that the bookmark is being created by the user who owns the session
|
||||
newBookmark.Id = "" // ensure that creating a new bookmark generates a new ID
|
||||
bookmark, err := a.Srv().Store().ChannelBookmark().Save(newBookmark, true)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("CreateChannelBookmark", "app.channel.bookmark.save.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventChannelBookmarkCreated, "", bookmark.ChannelId, "", nil, connectionId)
|
||||
bookmarkJSON, jsonErr := json.Marshal(bookmark)
|
||||
if jsonErr != nil {
|
||||
return nil, model.NewAppError("CreateChannelBookmark", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
|
||||
}
|
||||
message.Add("bookmark", string(bookmarkJSON))
|
||||
a.Publish(message)
|
||||
return bookmark, nil
|
||||
}
|
||||
|
||||
func (a *App) UpdateChannelBookmark(c request.CTX, updateBookmark *model.ChannelBookmarkWithFileInfo, connectionId string) (*model.UpdateChannelBookmarkResponse, *model.AppError) {
|
||||
response := &model.UpdateChannelBookmarkResponse{}
|
||||
if updateBookmark.OwnerId == c.Session().UserId {
|
||||
isAnotherFile := updateBookmark.FileInfo != nil && updateBookmark.FileId != "" && updateBookmark.FileId != updateBookmark.FileInfo.Id
|
||||
|
||||
if isAnotherFile {
|
||||
if fileAlreadyAttachedErr := a.Srv().Store().ChannelBookmark().ErrorIfBookmarkFileInfoAlreadyAttached(updateBookmark.FileId); fileAlreadyAttachedErr != nil {
|
||||
return nil, model.NewAppError("UpdateChannelBookmark", "app.channel.bookmark.update.app_error", nil, "", http.StatusInternalServerError).Wrap(fileAlreadyAttachedErr)
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.Srv().Store().ChannelBookmark().Update(updateBookmark.ChannelBookmark); err != nil {
|
||||
return nil, model.NewAppError("UpdateChannelBookmark", "app.channel.bookmark.update.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
if isAnotherFile {
|
||||
fileInfo, fileErr := a.Srv().Store().FileInfo().Get(updateBookmark.FileId)
|
||||
if fileErr != nil {
|
||||
return nil, model.NewAppError("UpdateChannelBookmark", "app.channel.bookmark.get_existing.app_err", nil, "", http.StatusNotFound).Wrap(fileErr)
|
||||
}
|
||||
response.Updated = updateBookmark.ToBookmarkWithFileInfo(fileInfo)
|
||||
} else {
|
||||
response.Updated = updateBookmark.ToBookmarkWithFileInfo(updateBookmark.FileInfo)
|
||||
}
|
||||
} else {
|
||||
existingBookmark, ebErr := a.Srv().Store().ChannelBookmark().Get(updateBookmark.Id, false)
|
||||
if ebErr != nil {
|
||||
return nil, model.NewAppError("UpdateChannelBookmark", "app.channel.bookmark.get_existing.app_err", nil, "", http.StatusNotFound).Wrap(ebErr)
|
||||
}
|
||||
|
||||
existingBookmark.DeleteAt = model.GetMillis()
|
||||
if err := a.Srv().Store().ChannelBookmark().Delete(updateBookmark.Id, false); err != nil {
|
||||
return nil, model.NewAppError("UpdateChannelBookmark", "app.channel.bookmark.delete.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
newBookmark := updateBookmark.SetOriginal(c.Session().UserId)
|
||||
bookmark, err := a.Srv().Store().ChannelBookmark().Save(newBookmark, false)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("UpdateChannelBookmark", "app.channel.bookmark.save.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
response.Updated = bookmark
|
||||
response.Deleted = existingBookmark.ToBookmarkWithFileInfo(nil)
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventChannelBookmarkUpdated, "", updateBookmark.ChannelId, "", nil, connectionId)
|
||||
bookmarkJSON, jsonErr := json.Marshal(response)
|
||||
if jsonErr != nil {
|
||||
return nil, model.NewAppError("UpdateChannelBookmark", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
|
||||
}
|
||||
message.Add("bookmarks", string(bookmarkJSON))
|
||||
a.Publish(message)
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *App) DeleteChannelBookmark(bookmarkId, connectionId string) (*model.ChannelBookmarkWithFileInfo, *model.AppError) {
|
||||
if err := a.Srv().Store().ChannelBookmark().Delete(bookmarkId, true); err != nil {
|
||||
return nil, model.NewAppError("DeleteChannelBookmark", "app.channel.bookmark.delete.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
bookmark, err := a.GetBookmark(bookmarkId, true)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("DeleteChannelBookmark", "app.channel.bookmark.get.app_error", nil, "", http.StatusNotFound).Wrap(err)
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventChannelBookmarkDeleted, "", bookmark.ChannelId, "", nil, connectionId)
|
||||
bookmarkJSON, jsonErr := json.Marshal(bookmark)
|
||||
if jsonErr != nil {
|
||||
return nil, model.NewAppError("DeleteChannelBookmark", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
|
||||
}
|
||||
message.Add("bookmark", string(bookmarkJSON))
|
||||
a.Publish(message)
|
||||
|
||||
return bookmark, nil
|
||||
}
|
||||
|
||||
func (a *App) UpdateChannelBookmarkSortOrder(bookmarkId, channelId string, newIndex int64, connectionId string) ([]*model.ChannelBookmarkWithFileInfo, *model.AppError) {
|
||||
bookmarks, err := a.Srv().Store().ChannelBookmark().UpdateSortOrder(bookmarkId, channelId, newIndex)
|
||||
if err != nil {
|
||||
var iiErr *store.ErrInvalidInput
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(err, &iiErr):
|
||||
return nil, model.NewAppError("UpdateSortOrder", "app.channel.bookmark.update_sort.invalid_input.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
case errors.As(err, &nfErr):
|
||||
return nil, model.NewAppError("UpdateSortOrder", "app.channel.bookmark.update_sort.missing_bookmark.app_error", nil, "", http.StatusNotFound).Wrap(err)
|
||||
default:
|
||||
return nil, model.NewAppError("UpdateSortOrder", "app.channel.bookmark.update_sort.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventChannelBookmarkSorted, "", channelId, "", nil, connectionId)
|
||||
bookmarkJSON, jsonErr := json.Marshal(bookmarks)
|
||||
if jsonErr != nil {
|
||||
return nil, model.NewAppError("UpdateSortOrder", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
|
||||
}
|
||||
message.Add("bookmarks", string(bookmarkJSON))
|
||||
a.Publish(message)
|
||||
|
||||
return bookmarks, nil
|
||||
}
|
||||
498
server/channels/app/channel_bookmark_test.go
Обычный файл
498
server/channels/app/channel_bookmark_test.go
Обычный файл
@@ -0,0 +1,498 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func find_bookmark(slice []*model.ChannelBookmarkWithFileInfo, id string) *model.ChannelBookmarkWithFileInfo {
|
||||
for _, element := range slice {
|
||||
if element.Id == id {
|
||||
return element
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createBookmark(name string, bookmarkType model.ChannelBookmarkType, channelId string, fileId string) *model.ChannelBookmark {
|
||||
bookmark := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
DisplayName: name,
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: bookmarkType,
|
||||
Emoji: ":smile:",
|
||||
FileId: fileId,
|
||||
}
|
||||
|
||||
return bookmark
|
||||
}
|
||||
|
||||
func TestCreateBookmark(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
t.Run("create a channel bookmark", func(t *testing.T) {
|
||||
th.Context.Session().UserId = th.BasicUser.Id // set the user for the session
|
||||
|
||||
bookmark1 := createBookmark("Link bookmark test", model.ChannelBookmarkLink, th.BasicChannel.Id, "")
|
||||
bookmarkResp, err := th.App.CreateChannelBookmark(th.Context, bookmark1, "")
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bookmarkResp)
|
||||
|
||||
assert.Equal(t, bookmarkResp.ChannelId, th.BasicChannel.Id)
|
||||
assert.NotEmpty(t, bookmarkResp.Id)
|
||||
|
||||
bookmark2 := createBookmark("File bookmark test", model.ChannelBookmarkFile, th.BasicChannel.Id, "")
|
||||
|
||||
bookmarkResp, err = th.App.CreateChannelBookmark(th.Context, bookmark2, "")
|
||||
assert.Nil(t, bookmarkResp)
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Cannot create more than MaxBookmarksPerChannel", func(t *testing.T) {
|
||||
th.Context.Session().UserId = th.BasicUser.Id // set the user for the session
|
||||
|
||||
for i := 1; i < model.MaxBookmarksPerChannel; i++ {
|
||||
bookmark := createBookmark(fmt.Sprintf("Link bookmark test %d", i), model.ChannelBookmarkLink, th.BasicChannel.Id, "")
|
||||
bookmarkResp, err := th.App.CreateChannelBookmark(th.Context, bookmark, "")
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bookmarkResp)
|
||||
assert.Equal(t, bookmarkResp.ChannelId, th.BasicChannel.Id)
|
||||
assert.NotEmpty(t, bookmarkResp.Id)
|
||||
}
|
||||
|
||||
bookmark := createBookmark("Bookmark that should not be added", model.ChannelBookmarkLink, th.BasicChannel.Id, "")
|
||||
bookmarkResp, err := th.App.CreateChannelBookmark(th.Context, bookmark, "")
|
||||
assert.Nil(t, bookmarkResp)
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpdateBookmark(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
var updateBookmark *model.ChannelBookmarkWithFileInfo
|
||||
|
||||
var testUpdateAnotherFile = func(th *TestHelper, t *testing.T) {
|
||||
file := &model.FileInfo{
|
||||
Id: model.NewId(),
|
||||
CreatorId: model.BookmarkFileOwner,
|
||||
Path: "somepath",
|
||||
ThumbnailPath: "thumbpath",
|
||||
PreviewPath: "prevPath",
|
||||
Name: "test file",
|
||||
Extension: "png",
|
||||
MimeType: "images/png",
|
||||
Size: 873182,
|
||||
Width: 3076,
|
||||
Height: 2200,
|
||||
HasPreviewImage: true,
|
||||
}
|
||||
|
||||
th.App.Srv().Store().FileInfo().Save(th.Context, file)
|
||||
defer th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, file.Id)
|
||||
|
||||
bookmark2 := createBookmark("File to be updated", model.ChannelBookmarkFile, th.BasicChannel.Id, file.Id)
|
||||
bookmarkResp, err := th.App.CreateChannelBookmark(th.Context, bookmark2, "")
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bookmarkResp)
|
||||
|
||||
file2 := &model.FileInfo{
|
||||
Id: model.NewId(),
|
||||
CreatorId: model.BookmarkFileOwner,
|
||||
Path: "somepath",
|
||||
ThumbnailPath: "thumbpath",
|
||||
PreviewPath: "prevPath",
|
||||
Name: "test file",
|
||||
Extension: "png",
|
||||
MimeType: "images/png",
|
||||
Size: 873182,
|
||||
Width: 3076,
|
||||
Height: 2200,
|
||||
HasPreviewImage: true,
|
||||
}
|
||||
|
||||
th.App.Srv().Store().FileInfo().Save(th.Context, file2)
|
||||
th.App.Srv().Store().FileInfo().AttachToPost(th.Context, file2.Id, model.NewId(), th.BasicChannel.Id, model.BookmarkFileOwner)
|
||||
defer th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, file2.Id)
|
||||
|
||||
bookmark2.FileId = file2.Id
|
||||
bookmarkResp, err = th.App.CreateChannelBookmark(th.Context, bookmark2, "")
|
||||
require.NotNil(t, err)
|
||||
require.Nil(t, bookmarkResp)
|
||||
}
|
||||
|
||||
t.Run("same user update a channel bookmark", func(t *testing.T) {
|
||||
bookmark1 := &model.ChannelBookmark{
|
||||
ChannelId: th.BasicChannel.Id,
|
||||
DisplayName: "Link bookmark test",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
th.Context.Session().UserId = th.BasicUser.Id // set the user for the session
|
||||
bookmarkResp, err := th.App.CreateChannelBookmark(th.Context, bookmark1, "")
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bookmarkResp)
|
||||
|
||||
updateBookmark = bookmarkResp.Clone()
|
||||
updateBookmark.DisplayName = "New name"
|
||||
time.Sleep(1 * time.Millisecond) // to avoid collisions
|
||||
response, _ := th.App.UpdateChannelBookmark(th.Context, updateBookmark, "")
|
||||
require.NotNil(t, response)
|
||||
assert.Greater(t, response.Updated.UpdateAt, response.Updated.CreateAt)
|
||||
|
||||
testUpdateAnotherFile(th, t)
|
||||
})
|
||||
|
||||
t.Run("another user update a channel bookmark", func(t *testing.T) {
|
||||
updateBookmark2 := updateBookmark.Clone()
|
||||
updateBookmark2.DisplayName = "Another new name"
|
||||
th.Context.Session().UserId = th.BasicUser2.Id
|
||||
response, _ := th.App.UpdateChannelBookmark(th.Context, updateBookmark2, "")
|
||||
require.NotNil(t, response)
|
||||
assert.Equal(t, response.Updated.OriginalId, response.Deleted.Id)
|
||||
assert.Equal(t, response.Updated.DeleteAt, int64(0))
|
||||
assert.Greater(t, response.Deleted.DeleteAt, int64(0))
|
||||
assert.Equal(t, "Another new name", response.Updated.DisplayName)
|
||||
assert.Equal(t, "New name", response.Deleted.DisplayName)
|
||||
|
||||
testUpdateAnotherFile(th, t)
|
||||
})
|
||||
|
||||
t.Run("update an already deleted channel bookmark", func(t *testing.T) {
|
||||
bookmark1 := &model.ChannelBookmark{
|
||||
ChannelId: th.BasicChannel.Id,
|
||||
DisplayName: "Link bookmark test",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
th.Context.Session().UserId = th.BasicUser.Id // set the user for the session
|
||||
bookmarkResp, err := th.App.CreateChannelBookmark(th.Context, bookmark1, "")
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bookmarkResp)
|
||||
|
||||
updateBookmark = bookmarkResp.Clone()
|
||||
_, err = th.App.DeleteChannelBookmark(updateBookmark.Id, "")
|
||||
assert.Nil(t, err)
|
||||
|
||||
updateBookmark.DisplayName = "New name"
|
||||
_, err = th.App.UpdateChannelBookmark(th.Context, updateBookmark, "")
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
|
||||
t.Run("update a nonexisting channel bookmark", func(t *testing.T) {
|
||||
updateBookmark := &model.ChannelBookmark{
|
||||
Id: model.NewId(),
|
||||
ChannelId: th.BasicChannel.Id,
|
||||
DisplayName: "Link bookmark test",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
_, err := th.App.UpdateChannelBookmark(th.Context, updateBookmark.ToBookmarkWithFileInfo(nil), "")
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "app.channel.bookmark.get_existing.app_err", err.Id)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeleteBookmark(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
t.Run("delete a channel bookmark", func(t *testing.T) {
|
||||
bookmark1 := &model.ChannelBookmark{
|
||||
ChannelId: th.BasicChannel.Id,
|
||||
DisplayName: "Link bookmark test",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
th.Context.Session().UserId = th.BasicUser.Id // set the user for the session
|
||||
bookmarkResp, err := th.App.CreateChannelBookmark(th.Context, bookmark1, "")
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bookmarkResp)
|
||||
|
||||
bookmarkResp, err = th.App.DeleteChannelBookmark(bookmarkResp.Id, "")
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bookmarkResp)
|
||||
assert.Greater(t, bookmarkResp.DeleteAt, int64(0))
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetChannelBookmarks(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.Context.Session().UserId = th.BasicUser.Id // set the user for the session
|
||||
|
||||
bookmark1 := &model.ChannelBookmark{
|
||||
ChannelId: th.BasicChannel.Id,
|
||||
DisplayName: "Bookmark 1",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
th.App.CreateChannelBookmark(th.Context, bookmark1, "")
|
||||
|
||||
file := &model.FileInfo{
|
||||
Id: model.NewId(),
|
||||
CreatorId: model.BookmarkFileOwner,
|
||||
Path: "somepath",
|
||||
ThumbnailPath: "thumbpath",
|
||||
PreviewPath: "prevPath",
|
||||
Name: "test file",
|
||||
Extension: "png",
|
||||
MimeType: "images/png",
|
||||
Size: 873182,
|
||||
Width: 3076,
|
||||
Height: 2200,
|
||||
HasPreviewImage: true,
|
||||
}
|
||||
|
||||
th.App.Srv().Store().FileInfo().Save(th.Context, file)
|
||||
defer th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, file.Id)
|
||||
|
||||
bookmark2 := &model.ChannelBookmark{
|
||||
ChannelId: th.BasicChannel.Id,
|
||||
DisplayName: "Bookmark 2",
|
||||
FileId: file.Id,
|
||||
Type: model.ChannelBookmarkFile,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
th.App.CreateChannelBookmark(th.Context, bookmark2, "")
|
||||
|
||||
t.Run("get bookmarks of a channel", func(t *testing.T) {
|
||||
bookmarks, err := th.App.GetChannelBookmarks(th.BasicChannel.Id, 0)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bookmarks)
|
||||
assert.Len(t, bookmarks, 2)
|
||||
})
|
||||
|
||||
t.Run("get bookmarks of a channel after one is deleted (aka only return the changed bookmarks)", func(t *testing.T) {
|
||||
now := model.GetMillis()
|
||||
th.App.DeleteChannelBookmark(bookmark1.Id, "")
|
||||
|
||||
bookmarks, err := th.App.GetChannelBookmarks(th.BasicChannel.Id, 0)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bookmarks)
|
||||
assert.Len(t, bookmarks, 1)
|
||||
|
||||
bookmarks, err = th.App.GetChannelBookmarks(th.BasicChannel.Id, now)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bookmarks)
|
||||
assert.Len(t, bookmarks, 1)
|
||||
|
||||
deleted := false
|
||||
for _, b := range bookmarks {
|
||||
if b.DeleteAt > 0 {
|
||||
deleted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.Equal(t, deleted, true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpdateChannelBookmarkSortOrder(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
channelId := th.BasicChannel.Id
|
||||
th.Context.Session().UserId = th.BasicUser.Id // set the user for the session
|
||||
|
||||
bookmark0 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
DisplayName: "Bookmark 0",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
file := &model.FileInfo{
|
||||
Id: model.NewId(),
|
||||
CreatorId: model.BookmarkFileOwner,
|
||||
Path: "somepath",
|
||||
ThumbnailPath: "thumbpath",
|
||||
PreviewPath: "prevPath",
|
||||
Name: "test file",
|
||||
Extension: "png",
|
||||
MimeType: "images/png",
|
||||
Size: 873182,
|
||||
Width: 3076,
|
||||
Height: 2200,
|
||||
HasPreviewImage: true,
|
||||
}
|
||||
|
||||
bookmark1 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
DisplayName: "Bookmark 1",
|
||||
FileId: file.Id,
|
||||
Type: model.ChannelBookmarkFile,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
_, err := th.App.Srv().Store().FileInfo().Save(th.Context, file)
|
||||
require.NoError(t, err)
|
||||
defer th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, file.Id)
|
||||
|
||||
bookmark2 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
DisplayName: "Bookmark 2",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
}
|
||||
|
||||
bookmark3 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
DisplayName: "Bookmark 3",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
}
|
||||
|
||||
bookmark4 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
DisplayName: "Bookmark 4",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
}
|
||||
|
||||
bookmarkResp, appErr := th.App.CreateChannelBookmark(th.Context, bookmark0, "")
|
||||
require.Nil(t, appErr)
|
||||
require.NotNil(t, bookmarkResp)
|
||||
bookmark0 = bookmarkResp.ChannelBookmark.Clone()
|
||||
|
||||
bookmarkResp, appErr = th.App.CreateChannelBookmark(th.Context, bookmark1, "")
|
||||
require.Nil(t, appErr)
|
||||
require.NotNil(t, bookmarkResp)
|
||||
bookmark1 = bookmarkResp.ChannelBookmark.Clone()
|
||||
|
||||
bookmarkResp, appErr = th.App.CreateChannelBookmark(th.Context, bookmark2, "")
|
||||
require.Nil(t, appErr)
|
||||
require.NotNil(t, bookmarkResp)
|
||||
bookmark2 = bookmarkResp.ChannelBookmark.Clone()
|
||||
|
||||
bookmarkResp, appErr = th.App.CreateChannelBookmark(th.Context, bookmark3, "")
|
||||
require.Nil(t, appErr)
|
||||
require.NotNil(t, bookmarkResp)
|
||||
bookmark3 = bookmarkResp.ChannelBookmark.Clone()
|
||||
|
||||
bookmarkResp, appErr = th.App.CreateChannelBookmark(th.Context, bookmark4, "")
|
||||
require.Nil(t, appErr)
|
||||
require.NotNil(t, bookmarkResp)
|
||||
bookmark4 = bookmarkResp.ChannelBookmark.Clone()
|
||||
|
||||
t.Run("change order of bookmarks first to last", func(t *testing.T) {
|
||||
bookmarks, sortErr := th.App.UpdateChannelBookmarkSortOrder(bookmark0.Id, channelId, int64(4), "")
|
||||
require.Nil(t, sortErr)
|
||||
require.NotNil(t, bookmarks)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(4))
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks last to first", func(t *testing.T) {
|
||||
bookmarks, sortErr := th.App.UpdateChannelBookmarkSortOrder(bookmark0.Id, channelId, int64(0), "")
|
||||
require.Nil(t, sortErr)
|
||||
require.NotNil(t, bookmarks)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(4))
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks first to third", func(t *testing.T) {
|
||||
bookmarks, sortErr := th.App.UpdateChannelBookmarkSortOrder(bookmark0.Id, channelId, int64(2), "")
|
||||
require.Nil(t, sortErr)
|
||||
require.NotNil(t, bookmarks)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(4))
|
||||
|
||||
// now reset order
|
||||
th.App.UpdateChannelBookmarkSortOrder(bookmark0.Id, channelId, int64(0), "")
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks second to third", func(t *testing.T) {
|
||||
bookmarks, sortErr := th.App.UpdateChannelBookmarkSortOrder(bookmark1.Id, channelId, int64(2), "")
|
||||
require.Nil(t, sortErr)
|
||||
require.NotNil(t, bookmarks)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(4))
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks third to second", func(t *testing.T) {
|
||||
bookmarks, sortErr := th.App.UpdateChannelBookmarkSortOrder(bookmark1.Id, channelId, int64(1), "")
|
||||
require.Nil(t, sortErr)
|
||||
require.NotNil(t, bookmarks)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(4))
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks last to previous last", func(t *testing.T) {
|
||||
bookmarks, sortErr := th.App.UpdateChannelBookmarkSortOrder(bookmark4.Id, channelId, int64(3), "")
|
||||
require.Nil(t, sortErr)
|
||||
require.NotNil(t, bookmarks)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(4))
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks last to second", func(t *testing.T) {
|
||||
bookmarks, sortErr := th.App.UpdateChannelBookmarkSortOrder(bookmark3.Id, channelId, int64(1), "")
|
||||
require.Nil(t, sortErr)
|
||||
require.NotNil(t, bookmarks)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(4))
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks error when new index is out of bounds", func(t *testing.T) {
|
||||
_, appErr = th.App.UpdateChannelBookmarkSortOrder(bookmark3.Id, channelId, int64(-1), "")
|
||||
assert.Error(t, appErr)
|
||||
_, appErr = th.App.UpdateChannelBookmarkSortOrder(bookmark3.Id, channelId, int64(5), "")
|
||||
assert.Error(t, appErr)
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks error when bookmark not found", func(t *testing.T) {
|
||||
_, appErr = th.App.UpdateChannelBookmarkSortOrder(model.NewId(), channelId, int64(0), "")
|
||||
assert.Error(t, appErr)
|
||||
})
|
||||
}
|
||||
@@ -1716,6 +1716,7 @@ func TestPatchChannelModerationsForChannel(t *testing.T) {
|
||||
createReactions := model.ChannelModeratedPermissions[1]
|
||||
manageMembers := model.ChannelModeratedPermissions[2]
|
||||
channelMentions := model.ChannelModeratedPermissions[3]
|
||||
manageBookmarks := model.ChannelModeratedPermissions[4]
|
||||
|
||||
nonChannelModeratedPermission := model.PermissionCreateBot.Id
|
||||
|
||||
@@ -1809,6 +1810,26 @@ func TestPatchChannelModerationsForChannel(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Removing manage bookmarks from members role",
|
||||
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||
{
|
||||
Name: &manageBookmarks,
|
||||
Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(false)},
|
||||
},
|
||||
},
|
||||
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
|
||||
manageBookmarks: {
|
||||
Members: &model.ChannelModeratedRole{Value: false, Enabled: true},
|
||||
},
|
||||
},
|
||||
RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||
{
|
||||
Name: &manageBookmarks,
|
||||
Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(true)},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Removing create posts from guests role",
|
||||
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||
@@ -1881,6 +1902,18 @@ func TestPatchChannelModerationsForChannel(t *testing.T) {
|
||||
ShouldError: false,
|
||||
ShouldHaveNoChannelScheme: true,
|
||||
},
|
||||
{
|
||||
Name: "Removing manage bookmarks from guests role should not error",
|
||||
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||
{
|
||||
Name: &manageBookmarks,
|
||||
Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(false)},
|
||||
},
|
||||
},
|
||||
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{},
|
||||
ShouldError: false,
|
||||
ShouldHaveNoChannelScheme: true,
|
||||
},
|
||||
{
|
||||
Name: "Removing a permission that is not channel moderated should not error",
|
||||
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||
@@ -1981,6 +2014,12 @@ func TestPatchChannelModerationsForChannel(t *testing.T) {
|
||||
Members: model.NewBool(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: &manageBookmarks,
|
||||
Roles: &model.ChannelModeratedRolesPatch{
|
||||
Members: model.NewBool(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{},
|
||||
ShouldHaveNoChannelScheme: true,
|
||||
@@ -2039,7 +2078,7 @@ func TestPatchChannelModerationsForChannel(t *testing.T) {
|
||||
if permission, found := tc.PermissionsModeratedByPatch[moderation.Name]; found && permission.Guests != nil {
|
||||
require.Equal(t, moderation.Roles.Guests.Value, permission.Guests.Value)
|
||||
require.Equal(t, moderation.Roles.Guests.Enabled, permission.Guests.Enabled)
|
||||
} else if moderation.Name == manageMembers {
|
||||
} else if moderation.Name == manageMembers || moderation.Name == "manage_bookmarks" {
|
||||
require.Empty(t, moderation.Roles.Guests)
|
||||
} else {
|
||||
require.Equal(t, moderation.Roles.Guests.Value, true)
|
||||
|
||||
@@ -704,6 +704,7 @@ func (t *UploadFileTask) init(a *App) {
|
||||
t.fileinfo.CreatorId = t.UserId
|
||||
t.fileinfo.CreateAt = t.Timestamp.UnixNano() / int64(time.Millisecond)
|
||||
t.fileinfo.Path = t.pathPrefix() + t.Name
|
||||
t.fileinfo.ChannelId = t.ChannelId
|
||||
|
||||
t.limitedInput = &io.LimitedReader{
|
||||
R: t.Input,
|
||||
@@ -950,6 +951,12 @@ func (t *UploadFileTask) postprocessImage(file io.Reader) {
|
||||
}
|
||||
|
||||
func (t UploadFileTask) pathPrefix() string {
|
||||
if t.UserId == model.BookmarkFileOwner {
|
||||
return model.BookmarkFileOwner +
|
||||
"/teams/" + t.TeamId +
|
||||
"/channels/" + t.ChannelId +
|
||||
"/" + t.fileinfo.Id + "/"
|
||||
}
|
||||
return t.Timestamp.Format("20060102") +
|
||||
"/teams/" + t.TeamId +
|
||||
"/channels/" + t.ChannelId +
|
||||
@@ -1003,6 +1010,9 @@ func (a *App) DoUploadFileExpectModification(c request.CTX, now time.Time, rawTe
|
||||
info.CreateAt = now.UnixNano() / int64(time.Millisecond)
|
||||
|
||||
pathPrefix := now.Format("20060102") + "/teams/" + teamID + "/channels/" + channelID + "/users/" + userID + "/" + info.Id + "/"
|
||||
if userID == model.BookmarkFileOwner {
|
||||
pathPrefix = model.BookmarkFileOwner + "/teams/" + teamID + "/channels/" + channelID + "/" + info.Id + "/"
|
||||
}
|
||||
info.Path = pathPrefix + filename
|
||||
|
||||
if info.IsImage() && !info.IsSvg() {
|
||||
|
||||
@@ -96,6 +96,16 @@ func TestDoUploadFile(t *testing.T) {
|
||||
|
||||
value = fmt.Sprintf("20090305/teams/%v/channels/%v/users/%v/%v/%v", teamID, channelID, userID, info4.Id, filename)
|
||||
assert.Equal(t, value, info4.Path, "stored file at incorrect path")
|
||||
|
||||
info5, err := th.App.DoUploadFile(th.Context, time.Date(2008, 3, 5, 1, 2, 3, 4, time.Local), teamID, channelID, model.BookmarkFileOwner, filename, data)
|
||||
require.Nil(t, err, "DoUploadFile should succeed with valid data")
|
||||
defer func() {
|
||||
th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, info5.Id)
|
||||
th.App.RemoveFile(info3.Path)
|
||||
}()
|
||||
|
||||
value = fmt.Sprintf("%v/teams/%v/channels/%v/%v/%v", model.BookmarkFileOwner, teamID, channelID, info5.Id, filename)
|
||||
assert.Equal(t, value, info5.Path, "stored file at incorrect path")
|
||||
}
|
||||
|
||||
func TestUploadFile(t *testing.T) {
|
||||
|
||||
@@ -1994,6 +1994,28 @@ func (a *OpenTracingAppLayer) CreateChannel(c request.CTX, channel *model.Channe
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) CreateChannelBookmark(c request.CTX, newBookmark *model.ChannelBookmark, connectionId string) (*model.ChannelBookmarkWithFileInfo, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateChannelBookmark")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.CreateChannelBookmark(c, newBookmark, connectionId)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) CreateChannelScheme(c request.CTX, channel *model.Channel) (*model.Scheme, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateChannelScheme")
|
||||
@@ -3093,6 +3115,28 @@ func (a *OpenTracingAppLayer) DeleteChannel(c request.CTX, channel *model.Channe
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) DeleteChannelBookmark(bookmarkId string, connectionId string) (*model.ChannelBookmarkWithFileInfo, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.DeleteChannelBookmark")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.DeleteChannelBookmark(bookmarkId, connectionId)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) DeleteChannelScheme(c request.CTX, channel *model.Channel) (*model.Channel, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.DeleteChannelScheme")
|
||||
@@ -5138,6 +5182,28 @@ func (a *OpenTracingAppLayer) GetAuthorizedAppsForUser(userID string, page int,
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetBookmark(bookmarkId string, includeDeleted bool) (*model.ChannelBookmarkWithFileInfo, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetBookmark")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetBookmark(bookmarkId, includeDeleted)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetBot(rctx request.CTX, botUserId string, includeDeleted bool) (*model.Bot, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetBot")
|
||||
@@ -5248,6 +5314,28 @@ func (a *OpenTracingAppLayer) GetChannel(c request.CTX, channelID string) (*mode
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetChannelBookmarks(channelId string, since int64) ([]*model.ChannelBookmarkWithFileInfo, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetChannelBookmarks")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetChannelBookmarks(channelId, since)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetChannelByName(c request.CTX, channelName string, teamID string, includeDeleted bool) (*model.Channel, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetChannelByName")
|
||||
@@ -17562,6 +17650,50 @@ func (a *OpenTracingAppLayer) UpdateChannel(c request.CTX, channel *model.Channe
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) UpdateChannelBookmark(c request.CTX, updateBookmark *model.ChannelBookmarkWithFileInfo, connectionId string) (*model.UpdateChannelBookmarkResponse, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateChannelBookmark")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.UpdateChannelBookmark(c, updateBookmark, connectionId)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) UpdateChannelBookmarkSortOrder(bookmarkId string, channelId string, newIndex int64, connectionId string) ([]*model.ChannelBookmarkWithFileInfo, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateChannelBookmarkSortOrder")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.UpdateChannelBookmarkSortOrder(bookmarkId, channelId, newIndex, connectionId)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) UpdateChannelMemberNotifyProps(c request.CTX, data map[string]string, channelID string, userID string) (*model.ChannelMember, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateChannelMemberNotifyProps")
|
||||
|
||||
@@ -1156,6 +1156,31 @@ func (a *App) getAddOutgoingOAuthConnectionsPermissions() (permissionsMap, error
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (a *App) getAddChannelBookmarksPermissionsMigration() (permissionsMap, error) {
|
||||
transformations := []permissionTransformation{}
|
||||
|
||||
transformations = append(transformations, permissionTransformation{
|
||||
On: permissionOr(
|
||||
isRole(model.ChannelUserRoleId),
|
||||
isRole(model.ChannelAdminRoleId),
|
||||
isRole(model.TeamAdminRoleId),
|
||||
isRole(model.SystemAdminRoleId),
|
||||
),
|
||||
Add: []string{
|
||||
model.PermissionAddBookmarkPublicChannel.Id,
|
||||
model.PermissionEditBookmarkPublicChannel.Id,
|
||||
model.PermissionDeleteBookmarkPublicChannel.Id,
|
||||
model.PermissionOrderBookmarkPublicChannel.Id,
|
||||
model.PermissionAddBookmarkPrivateChannel.Id,
|
||||
model.PermissionEditBookmarkPrivateChannel.Id,
|
||||
model.PermissionDeleteBookmarkPrivateChannel.Id,
|
||||
model.PermissionOrderBookmarkPrivateChannel.Id,
|
||||
},
|
||||
})
|
||||
|
||||
return transformations, nil
|
||||
}
|
||||
|
||||
// DoPermissionsMigrations execute all the permissions migrations need by the current version.
|
||||
func (a *App) DoPermissionsMigrations() error {
|
||||
return a.Srv().doPermissionsMigrations()
|
||||
@@ -1202,6 +1227,7 @@ func (s *Server) doPermissionsMigrations() error {
|
||||
{Key: model.MigrationKeyAddReadChannelContentPermissions, Migration: a.getAddChannelReadContentPermissions},
|
||||
{Key: model.MigrationKeyAddIPFilteringPermissions, Migration: a.getAddIPFilterPermissionsMigration},
|
||||
{Key: model.MigrationKeyAddOutgoingOAuthConnectionsPermissions, Migration: a.getAddOutgoingOAuthConnectionsPermissions},
|
||||
{Key: model.MigrationKeyAddChannelBookmarksPermissions, Migration: a.getAddChannelBookmarksPermissionsMigration},
|
||||
}
|
||||
|
||||
roles, err := s.Store().Role().GetAll()
|
||||
|
||||
Ссылка в новой задаче
Block a user