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  }