github.com/xzl8028/xenia-server@v0.0.0-20190809101854-18450a97da63/api4/post.go (about)

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