github.com/spreadshirt/mattermost-server@v5.3.2-0.20180927191755-a257d501df3d+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), !c.Session.IsMobileApp()) 62 if err != nil { 63 c.Err = err 64 return 65 } 66 67 c.App.SetStatusOnline(c.Session.UserId, 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 } 238 239 w.Header().Set(model.HEADER_ETAG_SERVER, post.Etag()) 240 w.Write([]byte(c.App.PostWithProxyAddedToImageURLs(post).ToJson())) 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 post, err := c.App.GetSinglePost(c.Params.PostId) 250 if err != nil { 251 c.SetPermissionError(model.PERMISSION_DELETE_POST) 252 return 253 } 254 255 if c.Session.UserId == post.UserId { 256 if !c.App.SessionHasPermissionToChannel(c.Session, post.ChannelId, model.PERMISSION_DELETE_POST) { 257 c.SetPermissionError(model.PERMISSION_DELETE_POST) 258 return 259 } 260 } else { 261 if !c.App.SessionHasPermissionToChannel(c.Session, post.ChannelId, model.PERMISSION_DELETE_OTHERS_POSTS) { 262 c.SetPermissionError(model.PERMISSION_DELETE_OTHERS_POSTS) 263 return 264 } 265 } 266 267 if _, err := c.App.DeletePost(c.Params.PostId, c.Session.UserId); err != nil { 268 c.Err = err 269 return 270 } 271 272 ReturnStatusOK(w) 273 } 274 275 func getPostThread(c *Context, w http.ResponseWriter, r *http.Request) { 276 c.RequirePostId() 277 if c.Err != nil { 278 return 279 } 280 281 var list *model.PostList 282 var err *model.AppError 283 if list, err = c.App.GetPostThread(c.Params.PostId); err != nil { 284 c.Err = err 285 return 286 } 287 288 var post *model.Post 289 if val, ok := list.Posts[c.Params.PostId]; ok { 290 post = val 291 } else { 292 c.SetInvalidUrlParam("post_id") 293 return 294 } 295 296 var channel *model.Channel 297 if channel, err = c.App.GetChannel(post.ChannelId); err != nil { 298 c.Err = err 299 return 300 } 301 302 if !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_READ_CHANNEL) { 303 if channel.Type == model.CHANNEL_OPEN { 304 if !c.App.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_READ_PUBLIC_CHANNEL) { 305 c.SetPermissionError(model.PERMISSION_READ_PUBLIC_CHANNEL) 306 return 307 } 308 } else { 309 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 310 return 311 } 312 } 313 314 if c.HandleEtag(list.Etag(), "Get Post Thread", w, r) { 315 return 316 } 317 318 w.Header().Set(model.HEADER_ETAG_SERVER, list.Etag()) 319 w.Write([]byte(c.App.PostListWithProxyAddedToImageURLs(list).ToJson())) 320 } 321 322 func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) { 323 c.RequireTeamId() 324 if c.Err != nil { 325 return 326 } 327 328 if !c.App.SessionHasPermissionToTeam(c.Session, c.Params.TeamId, model.PERMISSION_VIEW_TEAM) { 329 c.SetPermissionError(model.PERMISSION_VIEW_TEAM) 330 return 331 } 332 333 params := model.SearchParameterFromJson(r.Body) 334 335 if params.Terms == nil || len(*params.Terms) == 0 { 336 c.SetInvalidParam("terms") 337 return 338 } 339 terms := *params.Terms 340 341 timeZoneOffset := 0 342 if params.TimeZoneOffset != nil { 343 timeZoneOffset = *params.TimeZoneOffset 344 } 345 346 isOrSearch := false 347 if params.IsOrSearch != nil { 348 isOrSearch = *params.IsOrSearch 349 } 350 351 page := 0 352 if params.Page != nil { 353 page = *params.Page 354 } 355 356 perPage := 60 357 if params.PerPage != nil { 358 perPage = *params.PerPage 359 } 360 361 includeDeletedChannels := false 362 if params.IncludeDeletedChannels != nil { 363 includeDeletedChannels = *params.IncludeDeletedChannels 364 } 365 366 startTime := time.Now() 367 368 results, err := c.App.SearchPostsInTeam(terms, c.Session.UserId, c.Params.TeamId, isOrSearch, includeDeletedChannels, int(timeZoneOffset), page, perPage) 369 370 elapsedTime := float64(time.Since(startTime)) / float64(time.Second) 371 metrics := c.App.Metrics 372 if metrics != nil { 373 metrics.IncrementPostsSearchCounter() 374 metrics.ObservePostsSearchDuration(elapsedTime) 375 } 376 377 if err != nil { 378 c.Err = err 379 return 380 } 381 382 results = model.MakePostSearchResults(c.App.PostListWithProxyAddedToImageURLs(results.PostList), results.Matches) 383 384 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 385 w.Write([]byte(results.ToJson())) 386 } 387 388 func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { 389 c.RequirePostId() 390 if c.Err != nil { 391 return 392 } 393 394 post := model.PostFromJson(r.Body) 395 396 if post == nil { 397 c.SetInvalidParam("post") 398 return 399 } 400 401 // The post being updated in the payload must be the same one as indicated in the URL. 402 if post.Id != c.Params.PostId { 403 c.SetInvalidParam("post_id") 404 return 405 } 406 407 if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_POST) { 408 c.SetPermissionError(model.PERMISSION_EDIT_POST) 409 return 410 } 411 412 originalPost, err := c.App.GetSinglePost(c.Params.PostId) 413 if err != nil { 414 c.SetPermissionError(model.PERMISSION_EDIT_POST) 415 return 416 } 417 418 if c.Session.UserId != originalPost.UserId { 419 if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) { 420 c.SetPermissionError(model.PERMISSION_EDIT_OTHERS_POSTS) 421 return 422 } 423 } 424 425 post.Id = c.Params.PostId 426 427 rpost, err := c.App.UpdatePost(c.App.PostWithProxyRemovedFromImageURLs(post), false) 428 if err != nil { 429 c.Err = err 430 return 431 } 432 433 w.Write([]byte(c.App.PostWithProxyAddedToImageURLs(rpost).ToJson())) 434 } 435 436 func patchPost(c *Context, w http.ResponseWriter, r *http.Request) { 437 c.RequirePostId() 438 if c.Err != nil { 439 return 440 } 441 442 post := model.PostPatchFromJson(r.Body) 443 444 if post == nil { 445 c.SetInvalidParam("post") 446 return 447 } 448 449 if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_POST) { 450 c.SetPermissionError(model.PERMISSION_EDIT_POST) 451 return 452 } 453 454 originalPost, err := c.App.GetSinglePost(c.Params.PostId) 455 if err != nil { 456 c.SetPermissionError(model.PERMISSION_EDIT_POST) 457 return 458 } 459 460 if c.Session.UserId != originalPost.UserId { 461 if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) { 462 c.SetPermissionError(model.PERMISSION_EDIT_OTHERS_POSTS) 463 return 464 } 465 } 466 467 patchedPost, err := c.App.PatchPost(c.Params.PostId, c.App.PostPatchWithProxyRemovedFromImageURLs(post)) 468 if err != nil { 469 c.Err = err 470 return 471 } 472 473 w.Write([]byte(c.App.PostWithProxyAddedToImageURLs(patchedPost).ToJson())) 474 } 475 476 func saveIsPinnedPost(c *Context, w http.ResponseWriter, r *http.Request, isPinned bool) { 477 c.RequirePostId() 478 if c.Err != nil { 479 return 480 } 481 482 if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_READ_CHANNEL) { 483 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 484 return 485 } 486 487 patch := &model.PostPatch{} 488 patch.IsPinned = model.NewBool(isPinned) 489 490 _, err := c.App.PatchPost(c.Params.PostId, patch) 491 if err != nil { 492 c.Err = err 493 return 494 } 495 496 ReturnStatusOK(w) 497 } 498 499 func pinPost(c *Context, w http.ResponseWriter, r *http.Request) { 500 saveIsPinnedPost(c, w, r, true) 501 } 502 503 func unpinPost(c *Context, w http.ResponseWriter, r *http.Request) { 504 saveIsPinnedPost(c, w, r, false) 505 } 506 507 func getFileInfosForPost(c *Context, w http.ResponseWriter, r *http.Request) { 508 c.RequirePostId() 509 if c.Err != nil { 510 return 511 } 512 513 if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_READ_CHANNEL) { 514 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 515 return 516 } 517 518 infos, err := c.App.GetFileInfosForPost(c.Params.PostId, false) 519 if err != nil { 520 c.Err = err 521 return 522 } 523 524 if c.HandleEtag(model.GetEtagForFileInfos(infos), "Get File Infos For Post", w, r) { 525 return 526 } 527 528 w.Header().Set("Cache-Control", "max-age=2592000, public") 529 w.Header().Set(model.HEADER_ETAG_SERVER, model.GetEtagForFileInfos(infos)) 530 w.Write([]byte(model.FileInfosToJson(infos))) 531 } 532 533 func doPostAction(c *Context, w http.ResponseWriter, r *http.Request) { 534 c.RequirePostId().RequireActionId() 535 if c.Err != nil { 536 return 537 } 538 539 if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_READ_CHANNEL) { 540 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 541 return 542 } 543 544 actionRequest := model.DoPostActionRequestFromJson(r.Body) 545 if actionRequest == nil { 546 actionRequest = &model.DoPostActionRequest{} 547 } 548 549 if err := c.App.DoPostAction(c.Params.PostId, c.Params.ActionId, c.Session.UserId, actionRequest.SelectedOption); err != nil { 550 c.Err = err 551 return 552 } 553 554 ReturnStatusOK(w) 555 }