github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/reaction.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package app 5 6 import ( 7 "errors" 8 "net/http" 9 10 "github.com/mattermost/mattermost-server/v5/model" 11 ) 12 13 func (a *App) SaveReactionForPost(reaction *model.Reaction) (*model.Reaction, *model.AppError) { 14 post, err := a.GetSinglePost(reaction.PostId) 15 if err != nil { 16 return nil, err 17 } 18 19 channel, err := a.GetChannel(post.ChannelId) 20 if err != nil { 21 return nil, err 22 } 23 24 if channel.DeleteAt > 0 { 25 return nil, model.NewAppError("deleteReactionForPost", "api.reaction.save.archived_channel.app_error", nil, "", http.StatusForbidden) 26 } 27 28 if a.Srv().License() != nil && *a.Config().TeamSettings.ExperimentalTownSquareIsReadOnly && channel.Name == model.DEFAULT_CHANNEL { 29 var user *model.User 30 user, err = a.GetUser(reaction.UserId) 31 if err != nil { 32 return nil, err 33 } 34 35 if !a.RolesGrantPermission(user.GetRoles(), model.PERMISSION_MANAGE_SYSTEM.Id) { 36 return nil, model.NewAppError("saveReactionForPost", "api.reaction.town_square_read_only", nil, "", http.StatusForbidden) 37 } 38 } 39 40 reaction, nErr := a.Srv().Store.Reaction().Save(reaction) 41 if nErr != nil { 42 var appErr *model.AppError 43 switch { 44 case errors.As(nErr, &appErr): 45 return nil, appErr 46 default: 47 return nil, model.NewAppError("SaveReactionForPost", "app.reaction.save.save.app_error", nil, nErr.Error(), http.StatusInternalServerError) 48 } 49 } 50 51 // The post is always modified since the UpdateAt always changes 52 a.invalidateCacheForChannelPosts(post.ChannelId) 53 54 a.Srv().Go(func() { 55 a.sendReactionEvent(model.WEBSOCKET_EVENT_REACTION_ADDED, reaction, post, true) 56 }) 57 58 return reaction, nil 59 } 60 61 func (a *App) GetReactionsForPost(postId string) ([]*model.Reaction, *model.AppError) { 62 reactions, err := a.Srv().Store.Reaction().GetForPost(postId, true) 63 if err != nil { 64 return nil, model.NewAppError("GetReactionsForPost", "app.reaction.get_for_post.app_error", nil, err.Error(), http.StatusInternalServerError) 65 } 66 return reactions, nil 67 } 68 69 func (a *App) GetBulkReactionsForPosts(postIds []string) (map[string][]*model.Reaction, *model.AppError) { 70 reactions := make(map[string][]*model.Reaction) 71 72 allReactions, err := a.Srv().Store.Reaction().BulkGetForPosts(postIds) 73 if err != nil { 74 return nil, model.NewAppError("GetBulkReactionsForPosts", "app.reaction.bulk_get_for_post_ids.app_error", nil, err.Error(), http.StatusInternalServerError) 75 } 76 77 for _, reaction := range allReactions { 78 reactionsForPost := reactions[reaction.PostId] 79 reactionsForPost = append(reactionsForPost, reaction) 80 81 reactions[reaction.PostId] = reactionsForPost 82 } 83 84 reactions = populateEmptyReactions(postIds, reactions) 85 return reactions, nil 86 } 87 88 func populateEmptyReactions(postIds []string, reactions map[string][]*model.Reaction) map[string][]*model.Reaction { 89 for _, postId := range postIds { 90 if _, present := reactions[postId]; !present { 91 reactions[postId] = []*model.Reaction{} 92 } 93 } 94 return reactions 95 } 96 97 func (a *App) DeleteReactionForPost(reaction *model.Reaction) *model.AppError { 98 post, err := a.GetSinglePost(reaction.PostId) 99 if err != nil { 100 return err 101 } 102 103 channel, err := a.GetChannel(post.ChannelId) 104 if err != nil { 105 return err 106 } 107 108 if channel.DeleteAt > 0 { 109 return model.NewAppError("DeleteReactionForPost", "api.reaction.delete.archived_channel.app_error", nil, "", http.StatusForbidden) 110 } 111 112 if a.Srv().License() != nil && *a.Config().TeamSettings.ExperimentalTownSquareIsReadOnly && channel.Name == model.DEFAULT_CHANNEL { 113 user, err := a.GetUser(reaction.UserId) 114 if err != nil { 115 return err 116 } 117 118 if !a.RolesGrantPermission(user.GetRoles(), model.PERMISSION_MANAGE_SYSTEM.Id) { 119 return model.NewAppError("DeleteReactionForPost", "api.reaction.town_square_read_only", nil, "", http.StatusForbidden) 120 } 121 } 122 123 hasReactions := true 124 if reactions, _ := a.GetReactionsForPost(post.Id); len(reactions) <= 1 { 125 hasReactions = false 126 } 127 128 if _, err := a.Srv().Store.Reaction().Delete(reaction); err != nil { 129 return model.NewAppError("DeleteReactionForPost", "app.reaction.delete_all_with_emoji_name.get_reactions.app_error", nil, err.Error(), http.StatusInternalServerError) 130 } 131 132 // The post is always modified since the UpdateAt always changes 133 a.invalidateCacheForChannelPosts(post.ChannelId) 134 135 a.Srv().Go(func() { 136 a.sendReactionEvent(model.WEBSOCKET_EVENT_REACTION_REMOVED, reaction, post, hasReactions) 137 }) 138 139 return nil 140 } 141 142 func (a *App) sendReactionEvent(event string, reaction *model.Reaction, post *model.Post, hasReactions bool) { 143 // send out that a reaction has been added/removed 144 message := model.NewWebSocketEvent(event, "", post.ChannelId, "", nil) 145 message.Add("reaction", reaction.ToJson()) 146 a.Publish(message) 147 }