github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/api4/post.go (about) 1 // Copyright (c) 2015-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/masterhung0112/hk_server/v5/app" 13 "github.com/masterhung0112/hk_server/v5/audit" 14 "github.com/masterhung0112/hk_server/v5/model" 15 "github.com/masterhung0112/hk_server/v5/shared/mlog" 16 ) 17 18 func (api *API) InitPost() { 19 api.BaseRoutes.Posts.Handle("", api.ApiSessionRequired(createPost)).Methods("POST") 20 api.BaseRoutes.Post.Handle("", api.ApiSessionRequired(getPost)).Methods("GET") 21 api.BaseRoutes.Post.Handle("", api.ApiSessionRequired(deletePost)).Methods("DELETE") 22 api.BaseRoutes.Posts.Handle("/ephemeral", api.ApiSessionRequired(createEphemeralPost)).Methods("POST") 23 api.BaseRoutes.Post.Handle("/thread", api.ApiSessionRequired(getPostThread)).Methods("GET") 24 api.BaseRoutes.Post.Handle("/files/info", api.ApiSessionRequired(getFileInfosForPost)).Methods("GET") 25 api.BaseRoutes.PostsForChannel.Handle("", api.ApiSessionRequired(getPostsForChannel)).Methods("GET") 26 api.BaseRoutes.PostsForUser.Handle("/flagged", api.ApiSessionRequired(getFlaggedPostsForUser)).Methods("GET") 27 28 api.BaseRoutes.ChannelForUser.Handle("/posts/unread", api.ApiSessionRequired(getPostsForChannelAroundLastUnread)).Methods("GET") 29 30 api.BaseRoutes.Team.Handle("/posts/search", api.ApiSessionRequiredDisableWhenBusy(searchPosts)).Methods("POST") 31 api.BaseRoutes.Post.Handle("", api.ApiSessionRequired(updatePost)).Methods("PUT") 32 api.BaseRoutes.Post.Handle("/patch", api.ApiSessionRequired(patchPost)).Methods("PUT") 33 api.BaseRoutes.PostForUser.Handle("/set_unread", api.ApiSessionRequired(setPostUnread)).Methods("POST") 34 api.BaseRoutes.Post.Handle("/pin", api.ApiSessionRequired(pinPost)).Methods("POST") 35 api.BaseRoutes.Post.Handle("/unpin", api.ApiSessionRequired(unpinPost)).Methods("POST") 36 } 37 38 func createPost(c *Context, w http.ResponseWriter, r *http.Request) { 39 post := model.PostFromJson(r.Body) 40 if post == nil { 41 c.SetInvalidParam("post") 42 return 43 } 44 45 post.UserId = c.AppContext.Session().UserId 46 47 auditRec := c.MakeAuditRecord("createPost", audit.Fail) 48 defer c.LogAuditRecWithLevel(auditRec, app.LevelContent) 49 auditRec.AddMeta("post", post) 50 51 hasPermission := false 52 if c.App.SessionHasPermissionToChannel(*c.AppContext.Session(), post.ChannelId, model.PERMISSION_CREATE_POST) { 53 hasPermission = true 54 } else if channel, err := c.App.GetChannel(post.ChannelId); err == nil { 55 // Temporary permission check method until advanced permissions, please do not copy 56 if channel.Type == model.CHANNEL_OPEN && c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PERMISSION_CREATE_POST_PUBLIC) { 57 hasPermission = true 58 } 59 } 60 61 if !hasPermission { 62 c.SetPermissionError(model.PERMISSION_CREATE_POST) 63 return 64 } 65 66 if post.CreateAt != 0 && !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_MANAGE_SYSTEM) { 67 post.CreateAt = 0 68 } 69 70 setOnline := r.URL.Query().Get("set_online") 71 setOnlineBool := true // By default, always set online. 72 var err2 error 73 if setOnline != "" { 74 setOnlineBool, err2 = strconv.ParseBool(setOnline) 75 if err2 != nil { 76 mlog.Warn("Failed to parse set_online URL query parameter from createPost request", mlog.Err(err2)) 77 setOnlineBool = true // Set online nevertheless. 78 } 79 } 80 81 rp, err := c.App.CreatePostAsUser(c.AppContext, c.App.PostWithProxyRemovedFromImageURLs(post), c.AppContext.Session().Id, setOnlineBool) 82 if err != nil { 83 c.Err = err 84 return 85 } 86 auditRec.Success() 87 auditRec.AddMeta("post", rp) // overwrite meta 88 89 if setOnlineBool { 90 c.App.SetStatusOnline(c.AppContext.Session().UserId, false) 91 } 92 93 c.App.UpdateLastActivityAtIfNeeded(*c.AppContext.Session()) 94 c.ExtendSessionExpiryIfNeeded(w, r) 95 96 w.WriteHeader(http.StatusCreated) 97 98 // Note that rp has already had PreparePostForClient called on it by App.CreatePost 99 w.Write([]byte(rp.ToJson())) 100 } 101 102 func createEphemeralPost(c *Context, w http.ResponseWriter, r *http.Request) { 103 ephRequest := model.PostEphemeral{} 104 105 json.NewDecoder(r.Body).Decode(&ephRequest) 106 if ephRequest.UserID == "" { 107 c.SetInvalidParam("user_id") 108 return 109 } 110 111 if ephRequest.Post == nil { 112 c.SetInvalidParam("post") 113 return 114 } 115 116 ephRequest.Post.UserId = c.AppContext.Session().UserId 117 ephRequest.Post.CreateAt = model.GetMillis() 118 119 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_CREATE_POST_EPHEMERAL) { 120 c.SetPermissionError(model.PERMISSION_CREATE_POST_EPHEMERAL) 121 return 122 } 123 124 rp := c.App.SendEphemeralPost(ephRequest.UserID, c.App.PostWithProxyRemovedFromImageURLs(ephRequest.Post)) 125 126 w.WriteHeader(http.StatusCreated) 127 rp = model.AddPostActionCookies(rp, c.App.PostActionCookieSecret()) 128 rp = c.App.PreparePostForClient(rp, true, false) 129 w.Write([]byte(rp.ToJson())) 130 } 131 132 func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { 133 c.RequireChannelId() 134 if c.Err != nil { 135 return 136 } 137 138 afterPost := r.URL.Query().Get("after") 139 if afterPost != "" && !model.IsValidId(afterPost) { 140 c.SetInvalidParam("after") 141 return 142 } 143 144 beforePost := r.URL.Query().Get("before") 145 if beforePost != "" && !model.IsValidId(beforePost) { 146 c.SetInvalidParam("before") 147 return 148 } 149 150 sinceString := r.URL.Query().Get("since") 151 var since int64 152 var parseError error 153 if sinceString != "" { 154 since, parseError = strconv.ParseInt(sinceString, 10, 64) 155 if parseError != nil { 156 c.SetInvalidParam("since") 157 return 158 } 159 } 160 skipFetchThreads := r.URL.Query().Get("skipFetchThreads") == "true" 161 collapsedThreads := r.URL.Query().Get("collapsedThreads") == "true" 162 collapsedThreadsExtended := r.URL.Query().Get("collapsedThreadsExtended") == "true" 163 channelId := c.Params.ChannelId 164 page := c.Params.Page 165 perPage := c.Params.PerPage 166 167 if !c.App.SessionHasPermissionToChannel(*c.AppContext.Session(), channelId, model.PERMISSION_READ_CHANNEL) { 168 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 169 return 170 } 171 172 var list *model.PostList 173 var err *model.AppError 174 etag := "" 175 176 if since > 0 { 177 list, err = c.App.GetPostsSince(model.GetPostsSinceOptions{ChannelId: channelId, Time: since, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended, UserId: c.AppContext.Session().UserId}) 178 } else if afterPost != "" { 179 etag = c.App.GetPostsEtag(channelId, collapsedThreads) 180 181 if c.HandleEtag(etag, "Get Posts After", w, r) { 182 return 183 } 184 185 list, err = c.App.GetPostsAfterPost(model.GetPostsOptions{ChannelId: channelId, PostId: afterPost, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, UserId: c.AppContext.Session().UserId}) 186 } else if beforePost != "" { 187 etag = c.App.GetPostsEtag(channelId, collapsedThreads) 188 189 if c.HandleEtag(etag, "Get Posts Before", w, r) { 190 return 191 } 192 193 list, err = c.App.GetPostsBeforePost(model.GetPostsOptions{ChannelId: channelId, PostId: beforePost, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended, UserId: c.AppContext.Session().UserId}) 194 } else { 195 etag = c.App.GetPostsEtag(channelId, collapsedThreads) 196 197 if c.HandleEtag(etag, "Get Posts", w, r) { 198 return 199 } 200 201 list, err = c.App.GetPostsPage(model.GetPostsOptions{ChannelId: channelId, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended, UserId: c.AppContext.Session().UserId}) 202 } 203 204 if err != nil { 205 c.Err = err 206 return 207 } 208 209 if etag != "" { 210 w.Header().Set(model.HEADER_ETAG_SERVER, etag) 211 } 212 213 c.App.AddCursorIdsForPostList(list, afterPost, beforePost, since, page, perPage, collapsedThreads) 214 clientPostList := c.App.PreparePostListForClient(list) 215 216 w.Write([]byte(clientPostList.ToJson())) 217 } 218 219 func getPostsForChannelAroundLastUnread(c *Context, w http.ResponseWriter, r *http.Request) { 220 c.RequireUserId().RequireChannelId() 221 if c.Err != nil { 222 return 223 } 224 225 userId := c.Params.UserId 226 if !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), userId) { 227 c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS) 228 return 229 } 230 231 channelId := c.Params.ChannelId 232 if !c.App.SessionHasPermissionToChannel(*c.AppContext.Session(), channelId, model.PERMISSION_READ_CHANNEL) { 233 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 234 return 235 } 236 237 if c.Params.LimitAfter == 0 { 238 c.SetInvalidUrlParam("limit_after") 239 return 240 } 241 242 skipFetchThreads := r.URL.Query().Get("skipFetchThreads") == "true" 243 collapsedThreads := r.URL.Query().Get("collapsedThreads") == "true" 244 collapsedThreadsExtended := r.URL.Query().Get("collapsedThreadsExtended") == "true" 245 246 postList, err := c.App.GetPostsForChannelAroundLastUnread(channelId, userId, c.Params.LimitBefore, c.Params.LimitAfter, skipFetchThreads, collapsedThreads, collapsedThreadsExtended) 247 if err != nil { 248 c.Err = err 249 return 250 } 251 252 etag := "" 253 if len(postList.Order) == 0 { 254 etag = c.App.GetPostsEtag(channelId, collapsedThreads) 255 256 if c.HandleEtag(etag, "Get Posts", w, r) { 257 return 258 } 259 260 postList, err = c.App.GetPostsPage(model.GetPostsOptions{ChannelId: channelId, Page: app.PageDefault, PerPage: c.Params.LimitBefore, SkipFetchThreads: skipFetchThreads, CollapsedThreads: collapsedThreads, CollapsedThreadsExtended: collapsedThreadsExtended, UserId: c.AppContext.Session().UserId}) 261 if err != nil { 262 c.Err = err 263 return 264 } 265 } 266 267 postList.NextPostId = c.App.GetNextPostIdFromPostList(postList, collapsedThreads) 268 postList.PrevPostId = c.App.GetPrevPostIdFromPostList(postList, collapsedThreads) 269 270 clientPostList := c.App.PreparePostListForClient(postList) 271 272 if etag != "" { 273 w.Header().Set(model.HEADER_ETAG_SERVER, etag) 274 } 275 w.Write([]byte(clientPostList.ToJson())) 276 } 277 278 func getFlaggedPostsForUser(c *Context, w http.ResponseWriter, r *http.Request) { 279 c.RequireUserId() 280 if c.Err != nil { 281 return 282 } 283 284 if !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), c.Params.UserId) { 285 c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS) 286 return 287 } 288 289 channelId := r.URL.Query().Get("channel_id") 290 teamId := r.URL.Query().Get("team_id") 291 292 var posts *model.PostList 293 var err *model.AppError 294 295 if channelId != "" { 296 posts, err = c.App.GetFlaggedPostsForChannel(c.Params.UserId, channelId, c.Params.Page, c.Params.PerPage) 297 } else if teamId != "" { 298 posts, err = c.App.GetFlaggedPostsForTeam(c.Params.UserId, teamId, c.Params.Page, c.Params.PerPage) 299 } else { 300 posts, err = c.App.GetFlaggedPosts(c.Params.UserId, c.Params.Page, c.Params.PerPage) 301 } 302 if err != nil { 303 c.Err = err 304 return 305 } 306 307 pl := model.NewPostList() 308 channelReadPermission := make(map[string]bool) 309 310 for _, post := range posts.Posts { 311 allowed, ok := channelReadPermission[post.ChannelId] 312 313 if !ok { 314 allowed = false 315 316 if c.App.SessionHasPermissionToChannel(*c.AppContext.Session(), post.ChannelId, model.PERMISSION_READ_CHANNEL) { 317 allowed = true 318 } 319 320 channelReadPermission[post.ChannelId] = allowed 321 } 322 323 if !allowed { 324 continue 325 } 326 327 pl.AddPost(post) 328 pl.AddOrder(post.Id) 329 } 330 331 pl.SortByCreateAt() 332 w.Write([]byte(c.App.PreparePostListForClient(pl).ToJson())) 333 } 334 335 func getPost(c *Context, w http.ResponseWriter, r *http.Request) { 336 c.RequirePostId() 337 if c.Err != nil { 338 return 339 } 340 341 post, err := c.App.GetSinglePost(c.Params.PostId) 342 if err != nil { 343 c.Err = err 344 return 345 } 346 347 channel, err := c.App.GetChannel(post.ChannelId) 348 if err != nil { 349 c.Err = err 350 return 351 } 352 353 if !c.App.SessionHasPermissionToChannel(*c.AppContext.Session(), channel.Id, model.PERMISSION_READ_CHANNEL) { 354 if channel.Type == model.CHANNEL_OPEN { 355 if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PERMISSION_READ_PUBLIC_CHANNEL) { 356 c.SetPermissionError(model.PERMISSION_READ_PUBLIC_CHANNEL) 357 return 358 } 359 } else { 360 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 361 return 362 } 363 } 364 365 post = c.App.PreparePostForClient(post, false, false) 366 367 if c.HandleEtag(post.Etag(), "Get Post", w, r) { 368 return 369 } 370 371 w.Header().Set(model.HEADER_ETAG_SERVER, post.Etag()) 372 w.Write([]byte(post.ToJson())) 373 } 374 375 func deletePost(c *Context, w http.ResponseWriter, _ *http.Request) { 376 c.RequirePostId() 377 if c.Err != nil { 378 return 379 } 380 381 auditRec := c.MakeAuditRecord("deletePost", audit.Fail) 382 defer c.LogAuditRecWithLevel(auditRec, app.LevelContent) 383 auditRec.AddMeta("post_id", c.Params.PostId) 384 385 post, err := c.App.GetSinglePost(c.Params.PostId) 386 if err != nil { 387 c.SetPermissionError(model.PERMISSION_DELETE_POST) 388 return 389 } 390 auditRec.AddMeta("post", post) 391 392 if c.AppContext.Session().UserId == post.UserId { 393 if !c.App.SessionHasPermissionToChannel(*c.AppContext.Session(), post.ChannelId, model.PERMISSION_DELETE_POST) { 394 c.SetPermissionError(model.PERMISSION_DELETE_POST) 395 return 396 } 397 } else { 398 if !c.App.SessionHasPermissionToChannel(*c.AppContext.Session(), post.ChannelId, model.PERMISSION_DELETE_OTHERS_POSTS) { 399 c.SetPermissionError(model.PERMISSION_DELETE_OTHERS_POSTS) 400 return 401 } 402 } 403 404 if _, err := c.App.DeletePost(c.Params.PostId, c.AppContext.Session().UserId); err != nil { 405 c.Err = err 406 return 407 } 408 409 auditRec.Success() 410 ReturnStatusOK(w) 411 } 412 413 func getPostThread(c *Context, w http.ResponseWriter, r *http.Request) { 414 c.RequirePostId() 415 if c.Err != nil { 416 return 417 } 418 skipFetchThreads := r.URL.Query().Get("skipFetchThreads") == "true" 419 collapsedThreads := r.URL.Query().Get("collapsedThreads") == "true" 420 collapsedThreadsExtended := r.URL.Query().Get("collapsedThreadsExtended") == "true" 421 list, err := c.App.GetPostThread(c.Params.PostId, skipFetchThreads, collapsedThreads, collapsedThreadsExtended, c.AppContext.Session().UserId) 422 if err != nil { 423 c.Err = err 424 return 425 } 426 427 post, ok := list.Posts[c.Params.PostId] 428 if !ok { 429 c.SetInvalidUrlParam("post_id") 430 return 431 } 432 433 channel, err := c.App.GetChannel(post.ChannelId) 434 if err != nil { 435 c.Err = err 436 return 437 } 438 439 if !c.App.SessionHasPermissionToChannel(*c.AppContext.Session(), channel.Id, model.PERMISSION_READ_CHANNEL) { 440 if channel.Type == model.CHANNEL_OPEN { 441 if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PERMISSION_READ_PUBLIC_CHANNEL) { 442 c.SetPermissionError(model.PERMISSION_READ_PUBLIC_CHANNEL) 443 return 444 } 445 } else { 446 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 447 return 448 } 449 } 450 451 if c.HandleEtag(list.Etag(), "Get Post Thread", w, r) { 452 return 453 } 454 455 clientPostList := c.App.PreparePostListForClient(list) 456 457 w.Header().Set(model.HEADER_ETAG_SERVER, clientPostList.Etag()) 458 459 w.Write([]byte(clientPostList.ToJson())) 460 } 461 462 func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) { 463 c.RequireTeamId() 464 if c.Err != nil { 465 return 466 } 467 468 if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), c.Params.TeamId, model.PERMISSION_VIEW_TEAM) { 469 c.SetPermissionError(model.PERMISSION_VIEW_TEAM) 470 return 471 } 472 473 params, jsonErr := model.SearchParameterFromJson(r.Body) 474 if jsonErr != nil { 475 c.Err = model.NewAppError("searchPosts", "api.post.search_posts.invalid_body.app_error", nil, jsonErr.Error(), http.StatusBadRequest) 476 return 477 } 478 479 if params.Terms == nil || *params.Terms == "" { 480 c.SetInvalidParam("terms") 481 return 482 } 483 terms := *params.Terms 484 485 timeZoneOffset := 0 486 if params.TimeZoneOffset != nil { 487 timeZoneOffset = *params.TimeZoneOffset 488 } 489 490 isOrSearch := false 491 if params.IsOrSearch != nil { 492 isOrSearch = *params.IsOrSearch 493 } 494 495 page := 0 496 if params.Page != nil { 497 page = *params.Page 498 } 499 500 perPage := 60 501 if params.PerPage != nil { 502 perPage = *params.PerPage 503 } 504 505 includeDeletedChannels := false 506 if params.IncludeDeletedChannels != nil { 507 includeDeletedChannels = *params.IncludeDeletedChannels 508 } 509 510 startTime := time.Now() 511 512 results, err := c.App.SearchPostsInTeamForUser(c.AppContext, terms, c.AppContext.Session().UserId, c.Params.TeamId, isOrSearch, includeDeletedChannels, timeZoneOffset, page, perPage) 513 514 elapsedTime := float64(time.Since(startTime)) / float64(time.Second) 515 metrics := c.App.Metrics() 516 if metrics != nil { 517 metrics.IncrementPostsSearchCounter() 518 metrics.ObservePostsSearchDuration(elapsedTime) 519 } 520 521 if err != nil { 522 c.Err = err 523 return 524 } 525 526 clientPostList := c.App.PreparePostListForClient(results.PostList) 527 528 results = model.MakePostSearchResults(clientPostList, results.Matches) 529 530 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 531 w.Write([]byte(results.ToJson())) 532 } 533 534 func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { 535 c.RequirePostId() 536 if c.Err != nil { 537 return 538 } 539 540 post := model.PostFromJson(r.Body) 541 542 if post == nil { 543 c.SetInvalidParam("post") 544 return 545 } 546 547 auditRec := c.MakeAuditRecord("updatePost", audit.Fail) 548 defer c.LogAuditRecWithLevel(auditRec, app.LevelContent) 549 550 // The post being updated in the payload must be the same one as indicated in the URL. 551 if post.Id != c.Params.PostId { 552 c.SetInvalidParam("id") 553 return 554 } 555 556 if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, model.PERMISSION_EDIT_POST) { 557 c.SetPermissionError(model.PERMISSION_EDIT_POST) 558 return 559 } 560 561 originalPost, err := c.App.GetSinglePost(c.Params.PostId) 562 if err != nil { 563 c.SetPermissionError(model.PERMISSION_EDIT_POST) 564 return 565 } 566 auditRec.AddMeta("post", originalPost) 567 568 // Updating the file_ids of a post is not a supported operation and will be ignored 569 post.FileIds = originalPost.FileIds 570 571 if c.AppContext.Session().UserId != originalPost.UserId { 572 if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) { 573 c.SetPermissionError(model.PERMISSION_EDIT_OTHERS_POSTS) 574 return 575 } 576 } 577 578 post.Id = c.Params.PostId 579 580 rpost, err := c.App.UpdatePost(c.AppContext, c.App.PostWithProxyRemovedFromImageURLs(post), false) 581 if err != nil { 582 c.Err = err 583 return 584 } 585 586 auditRec.Success() 587 auditRec.AddMeta("update", rpost) 588 589 w.Write([]byte(rpost.ToJson())) 590 } 591 592 func patchPost(c *Context, w http.ResponseWriter, r *http.Request) { 593 c.RequirePostId() 594 if c.Err != nil { 595 return 596 } 597 598 post := model.PostPatchFromJson(r.Body) 599 600 if post == nil { 601 c.SetInvalidParam("post") 602 return 603 } 604 605 auditRec := c.MakeAuditRecord("patchPost", audit.Fail) 606 defer c.LogAuditRecWithLevel(auditRec, app.LevelContent) 607 608 // Updating the file_ids of a post is not a supported operation and will be ignored 609 post.FileIds = nil 610 611 originalPost, err := c.App.GetSinglePost(c.Params.PostId) 612 if err != nil { 613 c.SetPermissionError(model.PERMISSION_EDIT_POST) 614 return 615 } 616 auditRec.AddMeta("post", originalPost) 617 618 var permission *model.Permission 619 if c.AppContext.Session().UserId == originalPost.UserId { 620 permission = model.PERMISSION_EDIT_POST 621 } else { 622 permission = model.PERMISSION_EDIT_OTHERS_POSTS 623 } 624 625 if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, permission) { 626 c.SetPermissionError(permission) 627 return 628 } 629 630 patchedPost, err := c.App.PatchPost(c.AppContext, c.Params.PostId, c.App.PostPatchWithProxyRemovedFromImageURLs(post)) 631 if err != nil { 632 c.Err = err 633 return 634 } 635 636 auditRec.Success() 637 auditRec.AddMeta("patch", patchedPost) 638 639 w.Write([]byte(patchedPost.ToJson())) 640 } 641 642 func setPostUnread(c *Context, w http.ResponseWriter, r *http.Request) { 643 c.RequirePostId().RequireUserId() 644 if c.Err != nil { 645 return 646 } 647 648 props := model.MapBoolFromJson(r.Body) 649 collapsedThreadsSupported := props["collapsed_threads_supported"] 650 651 if c.AppContext.Session().UserId != c.Params.UserId && !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), c.Params.UserId) { 652 c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS) 653 return 654 } 655 if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, model.PERMISSION_READ_CHANNEL) { 656 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 657 return 658 } 659 660 state, err := c.App.MarkChannelAsUnreadFromPost(c.Params.PostId, c.Params.UserId, collapsedThreadsSupported, false) 661 if err != nil { 662 c.Err = err 663 return 664 } 665 w.Write([]byte(state.ToJson())) 666 } 667 668 func saveIsPinnedPost(c *Context, w http.ResponseWriter, isPinned bool) { 669 c.RequirePostId() 670 if c.Err != nil { 671 return 672 } 673 674 auditRec := c.MakeAuditRecord("saveIsPinnedPost", audit.Fail) 675 defer c.LogAuditRecWithLevel(auditRec, app.LevelContent) 676 677 if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, model.PERMISSION_READ_CHANNEL) { 678 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 679 return 680 } 681 682 // Restrict pinning if the experimental read-only-town-square setting is on. 683 user, err := c.App.GetUser(c.AppContext.Session().UserId) 684 if err != nil { 685 c.Err = err 686 return 687 } 688 689 post, err := c.App.GetSinglePost(c.Params.PostId) 690 if err != nil { 691 c.Err = err 692 return 693 } 694 auditRec.AddMeta("post", post) 695 696 channel, err := c.App.GetChannel(post.ChannelId) 697 if err != nil { 698 c.Err = err 699 return 700 } 701 702 if c.App.Srv().License() != nil && 703 *c.App.Config().TeamSettings.ExperimentalTownSquareIsReadOnly && 704 channel.Name == model.DEFAULT_CHANNEL && 705 !c.App.RolesGrantPermission(user.GetRoles(), model.PERMISSION_MANAGE_SYSTEM.Id) { 706 c.Err = model.NewAppError("saveIsPinnedPost", "api.post.save_is_pinned_post.town_square_read_only", nil, "", http.StatusForbidden) 707 return 708 } 709 710 patch := &model.PostPatch{} 711 patch.IsPinned = model.NewBool(isPinned) 712 713 patchedPost, err := c.App.PatchPost(c.AppContext, c.Params.PostId, patch) 714 if err != nil { 715 c.Err = err 716 return 717 } 718 auditRec.AddMeta("patch", patchedPost) 719 720 auditRec.Success() 721 ReturnStatusOK(w) 722 } 723 724 func pinPost(c *Context, w http.ResponseWriter, _ *http.Request) { 725 saveIsPinnedPost(c, w, true) 726 } 727 728 func unpinPost(c *Context, w http.ResponseWriter, _ *http.Request) { 729 saveIsPinnedPost(c, w, false) 730 } 731 732 func getFileInfosForPost(c *Context, w http.ResponseWriter, r *http.Request) { 733 c.RequirePostId() 734 if c.Err != nil { 735 return 736 } 737 738 if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, model.PERMISSION_READ_CHANNEL) { 739 c.SetPermissionError(model.PERMISSION_READ_CHANNEL) 740 return 741 } 742 743 infos, err := c.App.GetFileInfosForPostWithMigration(c.Params.PostId) 744 if err != nil { 745 c.Err = err 746 return 747 } 748 749 if c.HandleEtag(model.GetEtagForFileInfos(infos), "Get File Infos For Post", w, r) { 750 return 751 } 752 753 w.Header().Set("Cache-Control", "max-age=2592000, private") 754 w.Header().Set(model.HEADER_ETAG_SERVER, model.GetEtagForFileInfos(infos)) 755 w.Write([]byte(model.FileInfosToJson(infos))) 756 }