github.com/spreadshirt/mattermost-server@v5.3.2-0.20180927191755-a257d501df3d+incompatible/api4/post.go (about)

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