github.com/mad-app/mattermost-server@v5.11.1+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("/pin", api.ApiSessionRequired(pinPost)).Methods("POST") 29 api.BaseRoutes.Post.Handle("/unpin", api.ApiSessionRequired(unpinPost)).Methods("POST") 30 } 31 32 func createPost(c *Context, w http.ResponseWriter, r *http.Request) { 33 post := model.PostFromJson(r.Body) 34 if post == nil { 35 c.SetInvalidParam("post") 36 return 37 } 38 39 post.UserId = c.App.Session.UserId 40 41 hasPermission := false 42 if c.App.SessionHasPermissionToChannel(c.App.Session, post.ChannelId, model.PERMISSION_CREATE_POST) { 43 hasPermission = true 44 } else if channel, err := c.App.GetChannel(post.ChannelId); err == nil { 45 // Temporary permission check method until advanced permissions, please do not copy 46 if channel.Type == model.CHANNEL_OPEN && c.App.SessionHasPermissionToTeam(c.App.Session, channel.TeamId, model.PERMISSION_CREATE_POST_PUBLIC) { 47 hasPermission = true 48 } 49 } 50 51 if !hasPermission { 52 c.SetPermissionError(model.PERMISSION_CREATE_POST) 53 return 54 } 55 56 if post.CreateAt != 0 && !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) { 57 post.CreateAt = 0 58 } 59 60 rp, err := c.App.CreatePostAsUser(c.App.PostWithProxyRemovedFromImageURLs(post), c.App.Session.Id) 61 if err != nil { 62 c.Err = err 63 return 64 } 65 66 c.App.SetStatusOnline(c.App.Session.UserId, false) 67 c.App.UpdateLastActivityAtIfNeeded(c.App.Session) 68 69 w.WriteHeader(http.StatusCreated) 70 71 // Note that rp has already had PreparePostForClient called on it by App.CreatePost 72 w.Write([]byte(rp.ToJson())) 73 } 74 75 func createEphemeralPost(c *Context, w http.ResponseWriter, r *http.Request) { 76 ephRequest := model.PostEphemeral{} 77 78 json.NewDecoder(r.Body).Decode(&ephRequest) 79 if ephRequest.UserID == "" { 80 c.SetInvalidParam("user_id") 81 return 82 } 83 84 if ephRequest.Post == nil { 85 c.SetInvalidParam("post") 86 return 87 } 88 89 ephRequest.Post.UserId = c.App.Session.UserId 90 ephRequest.Post.CreateAt = model.GetMillis() 91 92 if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_CREATE_POST_EPHEMERAL) { 93 c.SetPermissionError(model.PERMISSION_CREATE_POST_EPHEMERAL) 94 return 95 } 96 97 rp := c.App.SendEphemeralPost(ephRequest.UserID, c.App.PostWithProxyRemovedFromImageURLs(ephRequest.Post)) 98 99 w.WriteHeader(http.StatusCreated) 100 rp = model.AddPostActionCookies(rp, c.App.PostActionCookieSecret()) 101 rp = c.App.PreparePostForClient(rp, true) 102 w.Write([]byte(rp.ToJson())) 103 } 104 105 func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { 106 c.RequireChannelId() 107 if c.Err != nil { 108 return 109 } 110 111 afterPost := r.URL.Query().Get("after") 112 beforePost := r.URL.Query().Get("before") 113 sinceString := r.URL.Query().Get("since") 114 115 var since int64 116 var parseError error 117 118 if len(sinceString) > 0 { 119 since, parseError = strconv.ParseInt(sinceString, 10, 64) 120 if parseError != nil { 121 c.SetInvalidParam("since") 122 return 123 } 124 } 125 126 if !c.App.SessionHasPermissionToChannel(c.App.Session, c.Params.ChannelId, model.PERMISSION_READ_CHANNEL) { 127 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 128 return 129 } 130 131 var list *model.PostList 132 var err *model.AppError 133 etag := "" 134 135 if since > 0 { 136 list, err = c.App.GetPostsSince(c.Params.ChannelId, since) 137 } else if len(afterPost) > 0 { 138 etag = c.App.GetPostsEtag(c.Params.ChannelId) 139 140 if c.HandleEtag(etag, "Get Posts After", w, r) { 141 return 142 } 143 144 list, err = c.App.GetPostsAfterPost(c.Params.ChannelId, afterPost, c.Params.Page, c.Params.PerPage) 145 } else if len(beforePost) > 0 { 146 etag = c.App.GetPostsEtag(c.Params.ChannelId) 147 148 if c.HandleEtag(etag, "Get Posts Before", w, r) { 149 return 150 } 151 152 list, err = c.App.GetPostsBeforePost(c.Params.ChannelId, beforePost, c.Params.Page, c.Params.PerPage) 153 } else { 154 etag = c.App.GetPostsEtag(c.Params.ChannelId) 155 156 if c.HandleEtag(etag, "Get Posts", w, r) { 157 return 158 } 159 160 list, err = c.App.GetPostsPage(c.Params.ChannelId, c.Params.Page, c.Params.PerPage) 161 } 162 163 if err != nil { 164 c.Err = err 165 return 166 } 167 168 if len(etag) > 0 { 169 w.Header().Set(model.HEADER_ETAG_SERVER, etag) 170 } 171 172 w.Write([]byte(c.App.PreparePostListForClient(list).ToJson())) 173 } 174 175 func getFlaggedPostsForUser(c *Context, w http.ResponseWriter, r *http.Request) { 176 c.RequireUserId() 177 if c.Err != nil { 178 return 179 } 180 181 if !c.App.SessionHasPermissionToUser(c.App.Session, c.Params.UserId) { 182 c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS) 183 return 184 } 185 186 channelId := r.URL.Query().Get("channel_id") 187 teamId := r.URL.Query().Get("team_id") 188 189 var posts *model.PostList 190 var err *model.AppError 191 192 if len(channelId) > 0 { 193 posts, err = c.App.GetFlaggedPostsForChannel(c.Params.UserId, channelId, c.Params.Page, c.Params.PerPage) 194 } else if len(teamId) > 0 { 195 posts, err = c.App.GetFlaggedPostsForTeam(c.Params.UserId, teamId, c.Params.Page, c.Params.PerPage) 196 } else { 197 posts, err = c.App.GetFlaggedPosts(c.Params.UserId, c.Params.Page, c.Params.PerPage) 198 } 199 200 pl := model.NewPostList() 201 channelReadPermission := make(map[string]bool) 202 203 for _, post := range posts.Posts { 204 allowed, ok := channelReadPermission[post.ChannelId] 205 206 if !ok { 207 allowed = false 208 209 if c.App.SessionHasPermissionToChannel(c.App.Session, post.ChannelId, model.PERMISSION_READ_CHANNEL) { 210 allowed = true 211 } 212 213 channelReadPermission[post.ChannelId] = allowed 214 } 215 216 if !allowed { 217 continue 218 } 219 220 pl.AddPost(post) 221 pl.AddOrder(post.Id) 222 } 223 224 pl.SortByCreateAt() 225 226 if err != nil { 227 c.Err = err 228 return 229 } 230 231 w.Write([]byte(c.App.PreparePostListForClient(pl).ToJson())) 232 } 233 234 func getPost(c *Context, w http.ResponseWriter, r *http.Request) { 235 c.RequirePostId() 236 if c.Err != nil { 237 return 238 } 239 240 post, err := c.App.GetSinglePost(c.Params.PostId) 241 if err != nil { 242 c.Err = err 243 return 244 } 245 246 channel, err := c.App.GetChannel(post.ChannelId) 247 if err != nil { 248 c.Err = err 249 return 250 } 251 252 if !c.App.SessionHasPermissionToChannel(c.App.Session, channel.Id, model.PERMISSION_READ_CHANNEL) { 253 if channel.Type == model.CHANNEL_OPEN { 254 if !c.App.SessionHasPermissionToTeam(c.App.Session, channel.TeamId, model.PERMISSION_READ_PUBLIC_CHANNEL) { 255 c.SetPermissionError(model.PERMISSION_READ_PUBLIC_CHANNEL) 256 return 257 } 258 } else { 259 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 260 return 261 } 262 } 263 264 post = c.App.PreparePostForClient(post, false) 265 266 if c.HandleEtag(post.Etag(), "Get Post", w, r) { 267 return 268 } 269 270 w.Header().Set(model.HEADER_ETAG_SERVER, post.Etag()) 271 w.Write([]byte(post.ToJson())) 272 } 273 274 func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { 275 c.RequirePostId() 276 if c.Err != nil { 277 return 278 } 279 280 post, err := c.App.GetSinglePost(c.Params.PostId) 281 if err != nil { 282 c.SetPermissionError(model.PERMISSION_DELETE_POST) 283 return 284 } 285 286 if c.App.Session.UserId == post.UserId { 287 if !c.App.SessionHasPermissionToChannel(c.App.Session, post.ChannelId, model.PERMISSION_DELETE_POST) { 288 c.SetPermissionError(model.PERMISSION_DELETE_POST) 289 return 290 } 291 } else { 292 if !c.App.SessionHasPermissionToChannel(c.App.Session, post.ChannelId, model.PERMISSION_DELETE_OTHERS_POSTS) { 293 c.SetPermissionError(model.PERMISSION_DELETE_OTHERS_POSTS) 294 return 295 } 296 } 297 298 if _, err := c.App.DeletePost(c.Params.PostId, c.App.Session.UserId); err != nil { 299 c.Err = err 300 return 301 } 302 303 ReturnStatusOK(w) 304 } 305 306 func getPostThread(c *Context, w http.ResponseWriter, r *http.Request) { 307 c.RequirePostId() 308 if c.Err != nil { 309 return 310 } 311 312 list, err := c.App.GetPostThread(c.Params.PostId) 313 if err != nil { 314 c.Err = err 315 return 316 } 317 318 post, ok := list.Posts[c.Params.PostId] 319 if !ok { 320 c.SetInvalidUrlParam("post_id") 321 return 322 } 323 324 channel, err := c.App.GetChannel(post.ChannelId) 325 if err != nil { 326 c.Err = err 327 return 328 } 329 330 if !c.App.SessionHasPermissionToChannel(c.App.Session, channel.Id, model.PERMISSION_READ_CHANNEL) { 331 if channel.Type == model.CHANNEL_OPEN { 332 if !c.App.SessionHasPermissionToTeam(c.App.Session, channel.TeamId, model.PERMISSION_READ_PUBLIC_CHANNEL) { 333 c.SetPermissionError(model.PERMISSION_READ_PUBLIC_CHANNEL) 334 return 335 } 336 } else { 337 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 338 return 339 } 340 } 341 342 if c.HandleEtag(list.Etag(), "Get Post Thread", w, r) { 343 return 344 } 345 346 clientPostList := c.App.PreparePostListForClient(list) 347 348 w.Header().Set(model.HEADER_ETAG_SERVER, clientPostList.Etag()) 349 350 w.Write([]byte(clientPostList.ToJson())) 351 } 352 353 func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) { 354 c.RequireTeamId() 355 if c.Err != nil { 356 return 357 } 358 359 if !c.App.SessionHasPermissionToTeam(c.App.Session, c.Params.TeamId, model.PERMISSION_VIEW_TEAM) { 360 c.SetPermissionError(model.PERMISSION_VIEW_TEAM) 361 return 362 } 363 364 params := model.SearchParameterFromJson(r.Body) 365 366 if params.Terms == nil || len(*params.Terms) == 0 { 367 c.SetInvalidParam("terms") 368 return 369 } 370 terms := *params.Terms 371 372 timeZoneOffset := 0 373 if params.TimeZoneOffset != nil { 374 timeZoneOffset = *params.TimeZoneOffset 375 } 376 377 isOrSearch := false 378 if params.IsOrSearch != nil { 379 isOrSearch = *params.IsOrSearch 380 } 381 382 page := 0 383 if params.Page != nil { 384 page = *params.Page 385 } 386 387 perPage := 60 388 if params.PerPage != nil { 389 perPage = *params.PerPage 390 } 391 392 includeDeletedChannels := false 393 if params.IncludeDeletedChannels != nil { 394 includeDeletedChannels = *params.IncludeDeletedChannels 395 } 396 397 startTime := time.Now() 398 399 results, err := c.App.SearchPostsInTeamForUser(terms, c.App.Session.UserId, c.Params.TeamId, isOrSearch, includeDeletedChannels, int(timeZoneOffset), page, perPage) 400 401 elapsedTime := float64(time.Since(startTime)) / float64(time.Second) 402 metrics := c.App.Metrics 403 if metrics != nil { 404 metrics.IncrementPostsSearchCounter() 405 metrics.ObservePostsSearchDuration(elapsedTime) 406 } 407 408 if err != nil { 409 c.Err = err 410 return 411 } 412 413 clientPostList := c.App.PreparePostListForClient(results.PostList) 414 415 results = model.MakePostSearchResults(clientPostList, results.Matches) 416 417 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 418 w.Write([]byte(results.ToJson())) 419 } 420 421 func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { 422 c.RequirePostId() 423 if c.Err != nil { 424 return 425 } 426 427 post := model.PostFromJson(r.Body) 428 429 if post == nil { 430 c.SetInvalidParam("post") 431 return 432 } 433 434 // The post being updated in the payload must be the same one as indicated in the URL. 435 if post.Id != c.Params.PostId { 436 c.SetInvalidParam("id") 437 return 438 } 439 440 // Updating the file_ids of a post is not a supported operation and will be ignored 441 post.FileIds = nil 442 443 if !c.App.SessionHasPermissionToChannelByPost(c.App.Session, c.Params.PostId, model.PERMISSION_EDIT_POST) { 444 c.SetPermissionError(model.PERMISSION_EDIT_POST) 445 return 446 } 447 448 originalPost, err := c.App.GetSinglePost(c.Params.PostId) 449 if err != nil { 450 c.SetPermissionError(model.PERMISSION_EDIT_POST) 451 return 452 } 453 454 if c.App.Session.UserId != originalPost.UserId { 455 if !c.App.SessionHasPermissionToChannelByPost(c.App.Session, c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) { 456 c.SetPermissionError(model.PERMISSION_EDIT_OTHERS_POSTS) 457 return 458 } 459 } 460 461 post.Id = c.Params.PostId 462 463 rpost, err := c.App.UpdatePost(c.App.PostWithProxyRemovedFromImageURLs(post), false) 464 if err != nil { 465 c.Err = err 466 return 467 } 468 469 w.Write([]byte(rpost.ToJson())) 470 } 471 472 func patchPost(c *Context, w http.ResponseWriter, r *http.Request) { 473 c.RequirePostId() 474 if c.Err != nil { 475 return 476 } 477 478 post := model.PostPatchFromJson(r.Body) 479 480 if post == nil { 481 c.SetInvalidParam("post") 482 return 483 } 484 485 // Updating the file_ids of a post is not a supported operation and will be ignored 486 post.FileIds = nil 487 488 if !c.App.SessionHasPermissionToChannelByPost(c.App.Session, c.Params.PostId, model.PERMISSION_EDIT_POST) { 489 c.SetPermissionError(model.PERMISSION_EDIT_POST) 490 return 491 } 492 493 originalPost, err := c.App.GetSinglePost(c.Params.PostId) 494 if err != nil { 495 c.SetPermissionError(model.PERMISSION_EDIT_POST) 496 return 497 } 498 499 if c.App.Session.UserId != originalPost.UserId { 500 if !c.App.SessionHasPermissionToChannelByPost(c.App.Session, c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) { 501 c.SetPermissionError(model.PERMISSION_EDIT_OTHERS_POSTS) 502 return 503 } 504 } 505 506 patchedPost, err := c.App.PatchPost(c.Params.PostId, c.App.PostPatchWithProxyRemovedFromImageURLs(post)) 507 if err != nil { 508 c.Err = err 509 return 510 } 511 512 w.Write([]byte(patchedPost.ToJson())) 513 } 514 515 func saveIsPinnedPost(c *Context, w http.ResponseWriter, r *http.Request, isPinned bool) { 516 c.RequirePostId() 517 if c.Err != nil { 518 return 519 } 520 521 if !c.App.SessionHasPermissionToChannelByPost(c.App.Session, c.Params.PostId, model.PERMISSION_READ_CHANNEL) { 522 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 523 return 524 } 525 526 // Restrict pinning if the experimental read-only-town-square setting is on. 527 user, err := c.App.GetUser(c.App.Session.UserId) 528 if err != nil { 529 c.Err = err 530 return 531 } 532 533 post, err := c.App.GetSinglePost(c.Params.PostId) 534 if err != nil { 535 c.Err = err 536 return 537 } 538 539 channel, err := c.App.GetChannel(post.ChannelId) 540 if err != nil { 541 c.Err = err 542 return 543 } 544 545 if c.App.License() != nil && 546 *c.App.Config().TeamSettings.ExperimentalTownSquareIsReadOnly && 547 channel.Name == model.DEFAULT_CHANNEL && 548 !c.App.RolesGrantPermission(user.GetRoles(), model.PERMISSION_MANAGE_SYSTEM.Id) { 549 c.Err = model.NewAppError("saveIsPinnedPost", "api.post.save_is_pinned_post.town_square_read_only", nil, "", http.StatusForbidden) 550 return 551 } 552 553 patch := &model.PostPatch{} 554 patch.IsPinned = model.NewBool(isPinned) 555 556 _, err = c.App.PatchPost(c.Params.PostId, patch) 557 if err != nil { 558 c.Err = err 559 return 560 } 561 562 ReturnStatusOK(w) 563 } 564 565 func pinPost(c *Context, w http.ResponseWriter, r *http.Request) { 566 saveIsPinnedPost(c, w, r, true) 567 } 568 569 func unpinPost(c *Context, w http.ResponseWriter, r *http.Request) { 570 saveIsPinnedPost(c, w, r, false) 571 } 572 573 func getFileInfosForPost(c *Context, w http.ResponseWriter, r *http.Request) { 574 c.RequirePostId() 575 if c.Err != nil { 576 return 577 } 578 579 if !c.App.SessionHasPermissionToChannelByPost(c.App.Session, c.Params.PostId, model.PERMISSION_READ_CHANNEL) { 580 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 581 return 582 } 583 584 infos, err := c.App.GetFileInfosForPostWithMigration(c.Params.PostId) 585 if err != nil { 586 c.Err = err 587 return 588 } 589 590 if c.HandleEtag(model.GetEtagForFileInfos(infos), "Get File Infos For Post", w, r) { 591 return 592 } 593 594 w.Header().Set("Cache-Control", "max-age=2592000, public") 595 w.Header().Set(model.HEADER_ETAG_SERVER, model.GetEtagForFileInfos(infos)) 596 w.Write([]byte(model.FileInfosToJson(infos))) 597 }