github.com/gigforks/mattermost-server@v4.9.1-0.20180619094218-800d97fa55d0+incompatible/api4/post.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package api4 5 6 import ( 7 "encoding/json" 8 "net/http" 9 "strconv" 10 "time" 11 12 "github.com/mattermost/mattermost-server/model" 13 ) 14 15 func (api *API) InitPost() { 16 api.BaseRoutes.Posts.Handle("", api.ApiSessionRequired(createPost)).Methods("POST") 17 api.BaseRoutes.Post.Handle("", api.ApiSessionRequired(getPost)).Methods("GET") 18 api.BaseRoutes.Post.Handle("", api.ApiSessionRequired(deletePost)).Methods("DELETE") 19 api.BaseRoutes.Posts.Handle("/ephemeral", api.ApiSessionRequired(createEphemeralPost)).Methods("POST") 20 api.BaseRoutes.Post.Handle("/thread", api.ApiSessionRequired(getPostThread)).Methods("GET") 21 api.BaseRoutes.Post.Handle("/files/info", api.ApiSessionRequired(getFileInfosForPost)).Methods("GET") 22 api.BaseRoutes.PostsForChannel.Handle("", api.ApiSessionRequired(getPostsForChannel)).Methods("GET") 23 api.BaseRoutes.PostsForUser.Handle("/flagged", api.ApiSessionRequired(getFlaggedPostsForUser)).Methods("GET") 24 25 api.BaseRoutes.Team.Handle("/posts/search", api.ApiSessionRequired(searchPosts)).Methods("POST") 26 api.BaseRoutes.Post.Handle("", api.ApiSessionRequired(updatePost)).Methods("PUT") 27 api.BaseRoutes.Post.Handle("/patch", api.ApiSessionRequired(patchPost)).Methods("PUT") 28 api.BaseRoutes.Post.Handle("/actions/{action_id:[A-Za-z0-9]+}", api.ApiSessionRequired(doPostAction)).Methods("POST") 29 api.BaseRoutes.Post.Handle("/pin", api.ApiSessionRequired(pinPost)).Methods("POST") 30 api.BaseRoutes.Post.Handle("/unpin", api.ApiSessionRequired(unpinPost)).Methods("POST") 31 } 32 33 func createPost(c *Context, w http.ResponseWriter, r *http.Request) { 34 post := model.PostFromJson(r.Body) 35 if post == nil { 36 c.SetInvalidParam("post") 37 return 38 } 39 40 post.UserId = c.Session.UserId 41 42 hasPermission := false 43 if c.App.SessionHasPermissionToChannel(c.Session, post.ChannelId, model.PERMISSION_CREATE_POST) { 44 hasPermission = true 45 } else if channel, err := c.App.GetChannel(post.ChannelId); err == nil { 46 // Temporary permission check method until advanced permissions, please do not copy 47 if channel.Type == model.CHANNEL_OPEN && c.App.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_CREATE_POST_PUBLIC) { 48 hasPermission = true 49 } 50 } 51 52 if !hasPermission { 53 c.SetPermissionError(model.PERMISSION_CREATE_POST) 54 return 55 } 56 57 if post.CreateAt != 0 && !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { 58 post.CreateAt = 0 59 } 60 61 rp, err := c.App.CreatePostAsUser(c.App.PostWithProxyRemovedFromImageURLs(post)) 62 if err != nil { 63 c.Err = err 64 return 65 } 66 67 c.App.SetStatusOnline(c.Session.UserId, c.Session.Id, false) 68 c.App.UpdateLastActivityAtIfNeeded(c.Session) 69 70 w.WriteHeader(http.StatusCreated) 71 w.Write([]byte(c.App.PostWithProxyAddedToImageURLs(rp).ToJson())) 72 } 73 74 func createEphemeralPost(c *Context, w http.ResponseWriter, r *http.Request) { 75 ephRequest := model.PostEphemeral{} 76 77 json.NewDecoder(r.Body).Decode(&ephRequest) 78 if ephRequest.UserID == "" { 79 c.SetInvalidParam("user_id") 80 return 81 } 82 83 if ephRequest.Post == nil { 84 c.SetInvalidParam("post") 85 return 86 } 87 88 ephRequest.Post.UserId = c.Session.UserId 89 ephRequest.Post.CreateAt = model.GetMillis() 90 91 if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_CREATE_POST_EPHEMERAL) { 92 c.SetPermissionError(model.PERMISSION_CREATE_POST_EPHEMERAL) 93 return 94 } 95 96 rp := c.App.SendEphemeralPost(ephRequest.UserID, c.App.PostWithProxyRemovedFromImageURLs(ephRequest.Post)) 97 98 w.WriteHeader(http.StatusCreated) 99 w.Write([]byte(c.App.PostWithProxyAddedToImageURLs(rp).ToJson())) 100 } 101 102 func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { 103 c.RequireChannelId() 104 if c.Err != nil { 105 return 106 } 107 108 afterPost := r.URL.Query().Get("after") 109 beforePost := r.URL.Query().Get("before") 110 sinceString := r.URL.Query().Get("since") 111 112 var since int64 113 var parseError error 114 115 if len(sinceString) > 0 { 116 since, parseError = strconv.ParseInt(sinceString, 10, 64) 117 if parseError != nil { 118 c.SetInvalidParam("since") 119 return 120 } 121 } 122 123 if !c.App.SessionHasPermissionToChannel(c.Session, c.Params.ChannelId, model.PERMISSION_READ_CHANNEL) { 124 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 125 return 126 } 127 128 var list *model.PostList 129 var err *model.AppError 130 etag := "" 131 132 if since > 0 { 133 list, err = c.App.GetPostsSince(c.Params.ChannelId, since) 134 } else if len(afterPost) > 0 { 135 etag = c.App.GetPostsEtag(c.Params.ChannelId) 136 137 if c.HandleEtag(etag, "Get Posts After", w, r) { 138 return 139 } 140 141 list, err = c.App.GetPostsAfterPost(c.Params.ChannelId, afterPost, c.Params.Page, c.Params.PerPage) 142 } else if len(beforePost) > 0 { 143 etag = c.App.GetPostsEtag(c.Params.ChannelId) 144 145 if c.HandleEtag(etag, "Get Posts Before", w, r) { 146 return 147 } 148 149 list, err = c.App.GetPostsBeforePost(c.Params.ChannelId, beforePost, c.Params.Page, c.Params.PerPage) 150 } else { 151 etag = c.App.GetPostsEtag(c.Params.ChannelId) 152 153 if c.HandleEtag(etag, "Get Posts", w, r) { 154 return 155 } 156 157 list, err = c.App.GetPostsPage(c.Params.ChannelId, c.Params.Page, c.Params.PerPage) 158 } 159 160 if err != nil { 161 c.Err = err 162 return 163 } 164 165 if len(etag) > 0 { 166 w.Header().Set(model.HEADER_ETAG_SERVER, etag) 167 } 168 w.Write([]byte(c.App.PostListWithProxyAddedToImageURLs(list).ToJson())) 169 } 170 171 func getFlaggedPostsForUser(c *Context, w http.ResponseWriter, r *http.Request) { 172 c.RequireUserId() 173 if c.Err != nil { 174 return 175 } 176 177 if !c.App.SessionHasPermissionToUser(c.Session, c.Params.UserId) { 178 c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS) 179 return 180 } 181 182 channelId := r.URL.Query().Get("channel_id") 183 teamId := r.URL.Query().Get("team_id") 184 185 var posts *model.PostList 186 var err *model.AppError 187 188 if len(channelId) > 0 { 189 posts, err = c.App.GetFlaggedPostsForChannel(c.Params.UserId, channelId, c.Params.Page, c.Params.PerPage) 190 } else if len(teamId) > 0 { 191 posts, err = c.App.GetFlaggedPostsForTeam(c.Params.UserId, teamId, c.Params.Page, c.Params.PerPage) 192 } else { 193 posts, err = c.App.GetFlaggedPosts(c.Params.UserId, c.Params.Page, c.Params.PerPage) 194 } 195 196 if err != nil { 197 c.Err = err 198 return 199 } 200 201 w.Write([]byte(c.App.PostListWithProxyAddedToImageURLs(posts).ToJson())) 202 } 203 204 func getPost(c *Context, w http.ResponseWriter, r *http.Request) { 205 c.RequirePostId() 206 if c.Err != nil { 207 return 208 } 209 210 var post *model.Post 211 var err *model.AppError 212 if post, err = c.App.GetSinglePost(c.Params.PostId); err != nil { 213 c.Err = err 214 return 215 } 216 217 var channel *model.Channel 218 if channel, err = c.App.GetChannel(post.ChannelId); err != nil { 219 c.Err = err 220 return 221 } 222 223 if !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_READ_CHANNEL) { 224 if channel.Type == model.CHANNEL_OPEN { 225 if !c.App.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_READ_PUBLIC_CHANNEL) { 226 c.SetPermissionError(model.PERMISSION_READ_PUBLIC_CHANNEL) 227 return 228 } 229 } else { 230 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 231 return 232 } 233 } 234 235 if c.HandleEtag(post.Etag(), "Get Post", w, r) { 236 return 237 } else { 238 w.Header().Set(model.HEADER_ETAG_SERVER, post.Etag()) 239 w.Write([]byte(c.App.PostWithProxyAddedToImageURLs(post).ToJson())) 240 } 241 } 242 243 func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { 244 c.RequirePostId() 245 if c.Err != nil { 246 return 247 } 248 249 if !c.App.SessionHasPermissionToPost(c.Session, c.Params.PostId, model.PERMISSION_DELETE_OTHERS_POSTS) { 250 c.SetPermissionError(model.PERMISSION_DELETE_OTHERS_POSTS) 251 return 252 } 253 254 if _, err := c.App.DeletePost(c.Params.PostId); err != nil { 255 c.Err = err 256 return 257 } 258 259 ReturnStatusOK(w) 260 } 261 262 func getPostThread(c *Context, w http.ResponseWriter, r *http.Request) { 263 c.RequirePostId() 264 if c.Err != nil { 265 return 266 } 267 268 var list *model.PostList 269 var err *model.AppError 270 if list, err = c.App.GetPostThread(c.Params.PostId); err != nil { 271 c.Err = err 272 return 273 } 274 275 var post *model.Post 276 if val, ok := list.Posts[c.Params.PostId]; ok { 277 post = val 278 } else { 279 c.SetInvalidUrlParam("post_id") 280 return 281 } 282 283 var channel *model.Channel 284 if channel, err = c.App.GetChannel(post.ChannelId); err != nil { 285 c.Err = err 286 return 287 } 288 289 if !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_READ_CHANNEL) { 290 if channel.Type == model.CHANNEL_OPEN { 291 if !c.App.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_READ_PUBLIC_CHANNEL) { 292 c.SetPermissionError(model.PERMISSION_READ_PUBLIC_CHANNEL) 293 return 294 } 295 } else { 296 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 297 return 298 } 299 } 300 301 if c.HandleEtag(list.Etag(), "Get Post Thread", w, r) { 302 return 303 } else { 304 w.Header().Set(model.HEADER_ETAG_SERVER, list.Etag()) 305 w.Write([]byte(c.App.PostListWithProxyAddedToImageURLs(list).ToJson())) 306 } 307 } 308 309 func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) { 310 c.RequireTeamId() 311 if c.Err != nil { 312 return 313 } 314 315 if !c.App.SessionHasPermissionToTeam(c.Session, c.Params.TeamId, model.PERMISSION_VIEW_TEAM) { 316 c.SetPermissionError(model.PERMISSION_VIEW_TEAM) 317 return 318 } 319 320 props := model.StringInterfaceFromJson(r.Body) 321 terms, ok := props["terms"].(string) 322 if !ok || len(terms) == 0 { 323 c.SetInvalidParam("terms") 324 return 325 } 326 327 isOrSearch, _ := props["is_or_search"].(bool) 328 329 startTime := time.Now() 330 331 posts, err := c.App.SearchPostsInTeam(terms, c.Session.UserId, c.Params.TeamId, isOrSearch) 332 333 elapsedTime := float64(time.Since(startTime)) / float64(time.Second) 334 metrics := c.App.Metrics 335 if metrics != nil { 336 metrics.IncrementPostsSearchCounter() 337 metrics.ObservePostsSearchDuration(elapsedTime) 338 } 339 340 if err != nil { 341 c.Err = err 342 return 343 } 344 345 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 346 w.Write([]byte(c.App.PostListWithProxyAddedToImageURLs(posts).ToJson())) 347 } 348 349 func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { 350 c.RequirePostId() 351 if c.Err != nil { 352 return 353 } 354 355 post := model.PostFromJson(r.Body) 356 357 if post == nil { 358 c.SetInvalidParam("post") 359 return 360 } 361 362 if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_POST) { 363 c.SetPermissionError(model.PERMISSION_EDIT_POST) 364 return 365 } 366 367 if !c.App.SessionHasPermissionToPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) { 368 c.SetPermissionError(model.PERMISSION_EDIT_OTHERS_POSTS) 369 return 370 } 371 372 post.Id = c.Params.PostId 373 374 rpost, err := c.App.UpdatePost(c.App.PostWithProxyRemovedFromImageURLs(post), false) 375 if err != nil { 376 c.Err = err 377 return 378 } 379 380 w.Write([]byte(c.App.PostWithProxyAddedToImageURLs(rpost).ToJson())) 381 } 382 383 func patchPost(c *Context, w http.ResponseWriter, r *http.Request) { 384 c.RequirePostId() 385 if c.Err != nil { 386 return 387 } 388 389 post := model.PostPatchFromJson(r.Body) 390 391 if post == nil { 392 c.SetInvalidParam("post") 393 return 394 } 395 396 if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_POST) { 397 c.SetPermissionError(model.PERMISSION_EDIT_POST) 398 return 399 } 400 401 if !c.App.SessionHasPermissionToPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) { 402 c.SetPermissionError(model.PERMISSION_EDIT_OTHERS_POSTS) 403 return 404 } 405 406 patchedPost, err := c.App.PatchPost(c.Params.PostId, c.App.PostPatchWithProxyRemovedFromImageURLs(post)) 407 if err != nil { 408 c.Err = err 409 return 410 } 411 412 w.Write([]byte(c.App.PostWithProxyAddedToImageURLs(patchedPost).ToJson())) 413 } 414 415 func saveIsPinnedPost(c *Context, w http.ResponseWriter, r *http.Request, isPinned bool) { 416 c.RequirePostId() 417 if c.Err != nil { 418 return 419 } 420 421 if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_READ_CHANNEL) { 422 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 423 return 424 } 425 426 patch := &model.PostPatch{} 427 patch.IsPinned = model.NewBool(isPinned) 428 429 _, err := c.App.PatchPost(c.Params.PostId, patch) 430 if err != nil { 431 c.Err = err 432 return 433 } 434 435 ReturnStatusOK(w) 436 } 437 438 func pinPost(c *Context, w http.ResponseWriter, r *http.Request) { 439 saveIsPinnedPost(c, w, r, true) 440 } 441 442 func unpinPost(c *Context, w http.ResponseWriter, r *http.Request) { 443 saveIsPinnedPost(c, w, r, false) 444 } 445 446 func getFileInfosForPost(c *Context, w http.ResponseWriter, r *http.Request) { 447 c.RequirePostId() 448 if c.Err != nil { 449 return 450 } 451 452 if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_READ_CHANNEL) { 453 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 454 return 455 } 456 457 if infos, err := c.App.GetFileInfosForPost(c.Params.PostId, false); err != nil { 458 c.Err = err 459 return 460 } else if c.HandleEtag(model.GetEtagForFileInfos(infos), "Get File Infos For Post", w, r) { 461 return 462 } else { 463 w.Header().Set("Cache-Control", "max-age=2592000, public") 464 w.Header().Set(model.HEADER_ETAG_SERVER, model.GetEtagForFileInfos(infos)) 465 w.Write([]byte(model.FileInfosToJson(infos))) 466 } 467 } 468 469 func doPostAction(c *Context, w http.ResponseWriter, r *http.Request) { 470 c.RequirePostId().RequireActionId() 471 if c.Err != nil { 472 return 473 } 474 475 if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_READ_CHANNEL) { 476 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 477 return 478 } 479 480 if err := c.App.DoPostAction(c.Params.PostId, c.Params.ActionId, c.Session.UserId); err != nil { 481 c.Err = err 482 return 483 } 484 485 ReturnStatusOK(w) 486 }