github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/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 "github.com/mattermost/mattermost-server/v5/plugin" 12 ) 13 14 func (a *App) SaveReactionForPost(reaction *model.Reaction) (*model.Reaction, *model.AppError) { 15 post, err := a.GetSinglePost(reaction.PostId) 16 if err != nil { 17 return nil, err 18 } 19 20 channel, err := a.GetChannel(post.ChannelId) 21 if err != nil { 22 return nil, err 23 } 24 25 if channel.DeleteAt > 0 { 26 return nil, model.NewAppError("deleteReactionForPost", "api.reaction.save.archived_channel.app_error", nil, "", http.StatusForbidden) 27 } 28 29 if a.Srv().License() != nil && *a.Config().TeamSettings.ExperimentalTownSquareIsReadOnly && channel.Name == model.DEFAULT_CHANNEL { 30 var user *model.User 31 user, err = a.GetUser(reaction.UserId) 32 if err != nil { 33 return nil, err 34 } 35 36 if !a.RolesGrantPermission(user.GetRoles(), model.PERMISSION_MANAGE_SYSTEM.Id) { 37 return nil, model.NewAppError("saveReactionForPost", "api.reaction.town_square_read_only", nil, "", http.StatusForbidden) 38 } 39 } 40 41 reaction, nErr := a.Srv().Store.Reaction().Save(reaction) 42 if nErr != nil { 43 var appErr *model.AppError 44 switch { 45 case errors.As(nErr, &appErr): 46 return nil, appErr 47 default: 48 return nil, model.NewAppError("SaveReactionForPost", "app.reaction.save.save.app_error", nil, nErr.Error(), http.StatusInternalServerError) 49 } 50 } 51 52 // The post is always modified since the UpdateAt always changes 53 a.invalidateCacheForChannelPosts(post.ChannelId) 54 55 if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil { 56 a.Srv().Go(func() { 57 pluginContext := a.PluginContext() 58 pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { 59 hooks.ReactionHasBeenAdded(pluginContext, reaction) 60 return true 61 }, plugin.ReactionHasBeenAddedId) 62 }) 63 } 64 65 a.Srv().Go(func() { 66 a.sendReactionEvent(model.WEBSOCKET_EVENT_REACTION_ADDED, reaction, post) 67 }) 68 69 return reaction, nil 70 } 71 72 func (a *App) GetReactionsForPost(postId string) ([]*model.Reaction, *model.AppError) { 73 reactions, err := a.Srv().Store.Reaction().GetForPost(postId, true) 74 if err != nil { 75 return nil, model.NewAppError("GetReactionsForPost", "app.reaction.get_for_post.app_error", nil, err.Error(), http.StatusInternalServerError) 76 } 77 return reactions, nil 78 } 79 80 func (a *App) GetBulkReactionsForPosts(postIds []string) (map[string][]*model.Reaction, *model.AppError) { 81 reactions := make(map[string][]*model.Reaction) 82 83 allReactions, err := a.Srv().Store.Reaction().BulkGetForPosts(postIds) 84 if err != nil { 85 return nil, model.NewAppError("GetBulkReactionsForPosts", "app.reaction.bulk_get_for_post_ids.app_error", nil, err.Error(), http.StatusInternalServerError) 86 } 87 88 for _, reaction := range allReactions { 89 reactionsForPost := reactions[reaction.PostId] 90 reactionsForPost = append(reactionsForPost, reaction) 91 92 reactions[reaction.PostId] = reactionsForPost 93 } 94 95 reactions = populateEmptyReactions(postIds, reactions) 96 return reactions, nil 97 } 98 99 func populateEmptyReactions(postIds []string, reactions map[string][]*model.Reaction) map[string][]*model.Reaction { 100 for _, postId := range postIds { 101 if _, present := reactions[postId]; !present { 102 reactions[postId] = []*model.Reaction{} 103 } 104 } 105 return reactions 106 } 107 108 func (a *App) DeleteReactionForPost(reaction *model.Reaction) *model.AppError { 109 post, err := a.GetSinglePost(reaction.PostId) 110 if err != nil { 111 return err 112 } 113 114 channel, err := a.GetChannel(post.ChannelId) 115 if err != nil { 116 return err 117 } 118 119 if channel.DeleteAt > 0 { 120 return model.NewAppError("DeleteReactionForPost", "api.reaction.delete.archived_channel.app_error", nil, "", http.StatusForbidden) 121 } 122 123 if a.Srv().License() != nil && *a.Config().TeamSettings.ExperimentalTownSquareIsReadOnly && channel.Name == model.DEFAULT_CHANNEL { 124 user, err := a.GetUser(reaction.UserId) 125 if err != nil { 126 return err 127 } 128 129 if !a.RolesGrantPermission(user.GetRoles(), model.PERMISSION_MANAGE_SYSTEM.Id) { 130 return model.NewAppError("DeleteReactionForPost", "api.reaction.town_square_read_only", nil, "", http.StatusForbidden) 131 } 132 } 133 134 if _, err := a.Srv().Store.Reaction().Delete(reaction); err != nil { 135 return model.NewAppError("DeleteReactionForPost", "app.reaction.delete_all_with_emoji_name.get_reactions.app_error", nil, err.Error(), http.StatusInternalServerError) 136 } 137 138 // The post is always modified since the UpdateAt always changes 139 a.invalidateCacheForChannelPosts(post.ChannelId) 140 141 if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil { 142 a.Srv().Go(func() { 143 pluginContext := a.PluginContext() 144 pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { 145 hooks.ReactionHasBeenRemoved(pluginContext, reaction) 146 return true 147 }, plugin.ReactionHasBeenRemovedId) 148 }) 149 } 150 151 a.Srv().Go(func() { 152 a.sendReactionEvent(model.WEBSOCKET_EVENT_REACTION_REMOVED, reaction, post) 153 }) 154 155 return nil 156 } 157 158 func (a *App) sendReactionEvent(event string, reaction *model.Reaction, post *model.Post) { 159 // send out that a reaction has been added/removed 160 message := model.NewWebSocketEvent(event, "", post.ChannelId, "", nil) 161 message.Add("reaction", reaction.ToJson()) 162 a.Publish(message) 163 }