github.com/gigforks/mattermost-server@v4.9.1-0.20180619094218-800d97fa55d0+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))
    62  	if err != nil {
    63  		c.Err = err
    64  		return
    65  	}
    66  
    67  	c.App.SetStatusOnline(c.Session.UserId, c.Session.Id, 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  	} else {
   238  		w.Header().Set(model.HEADER_ETAG_SERVER, post.Etag())
   239  		w.Write([]byte(c.App.PostWithProxyAddedToImageURLs(post).ToJson()))
   240  	}
   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  	if !c.App.SessionHasPermissionToPost(c.Session, c.Params.PostId, model.PERMISSION_DELETE_OTHERS_POSTS) {
   250  		c.SetPermissionError(model.PERMISSION_DELETE_OTHERS_POSTS)
   251  		return
   252  	}
   253  
   254  	if _, err := c.App.DeletePost(c.Params.PostId); err != nil {
   255  		c.Err = err
   256  		return
   257  	}
   258  
   259  	ReturnStatusOK(w)
   260  }
   261  
   262  func getPostThread(c *Context, w http.ResponseWriter, r *http.Request) {
   263  	c.RequirePostId()
   264  	if c.Err != nil {
   265  		return
   266  	}
   267  
   268  	var list *model.PostList
   269  	var err *model.AppError
   270  	if list, err = c.App.GetPostThread(c.Params.PostId); err != nil {
   271  		c.Err = err
   272  		return
   273  	}
   274  
   275  	var post *model.Post
   276  	if val, ok := list.Posts[c.Params.PostId]; ok {
   277  		post = val
   278  	} else {
   279  		c.SetInvalidUrlParam("post_id")
   280  		return
   281  	}
   282  
   283  	var channel *model.Channel
   284  	if channel, err = c.App.GetChannel(post.ChannelId); err != nil {
   285  		c.Err = err
   286  		return
   287  	}
   288  
   289  	if !c.App.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_READ_CHANNEL) {
   290  		if channel.Type == model.CHANNEL_OPEN {
   291  			if !c.App.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_READ_PUBLIC_CHANNEL) {
   292  				c.SetPermissionError(model.PERMISSION_READ_PUBLIC_CHANNEL)
   293  				return
   294  			}
   295  		} else {
   296  			c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
   297  			return
   298  		}
   299  	}
   300  
   301  	if c.HandleEtag(list.Etag(), "Get Post Thread", w, r) {
   302  		return
   303  	} else {
   304  		w.Header().Set(model.HEADER_ETAG_SERVER, list.Etag())
   305  		w.Write([]byte(c.App.PostListWithProxyAddedToImageURLs(list).ToJson()))
   306  	}
   307  }
   308  
   309  func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) {
   310  	c.RequireTeamId()
   311  	if c.Err != nil {
   312  		return
   313  	}
   314  
   315  	if !c.App.SessionHasPermissionToTeam(c.Session, c.Params.TeamId, model.PERMISSION_VIEW_TEAM) {
   316  		c.SetPermissionError(model.PERMISSION_VIEW_TEAM)
   317  		return
   318  	}
   319  
   320  	props := model.StringInterfaceFromJson(r.Body)
   321  	terms, ok := props["terms"].(string)
   322  	if !ok || len(terms) == 0 {
   323  		c.SetInvalidParam("terms")
   324  		return
   325  	}
   326  
   327  	isOrSearch, _ := props["is_or_search"].(bool)
   328  
   329  	startTime := time.Now()
   330  
   331  	posts, err := c.App.SearchPostsInTeam(terms, c.Session.UserId, c.Params.TeamId, isOrSearch)
   332  
   333  	elapsedTime := float64(time.Since(startTime)) / float64(time.Second)
   334  	metrics := c.App.Metrics
   335  	if metrics != nil {
   336  		metrics.IncrementPostsSearchCounter()
   337  		metrics.ObservePostsSearchDuration(elapsedTime)
   338  	}
   339  
   340  	if err != nil {
   341  		c.Err = err
   342  		return
   343  	}
   344  
   345  	w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
   346  	w.Write([]byte(c.App.PostListWithProxyAddedToImageURLs(posts).ToJson()))
   347  }
   348  
   349  func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
   350  	c.RequirePostId()
   351  	if c.Err != nil {
   352  		return
   353  	}
   354  
   355  	post := model.PostFromJson(r.Body)
   356  
   357  	if post == nil {
   358  		c.SetInvalidParam("post")
   359  		return
   360  	}
   361  
   362  	if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_POST) {
   363  		c.SetPermissionError(model.PERMISSION_EDIT_POST)
   364  		return
   365  	}
   366  
   367  	if !c.App.SessionHasPermissionToPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) {
   368  		c.SetPermissionError(model.PERMISSION_EDIT_OTHERS_POSTS)
   369  		return
   370  	}
   371  
   372  	post.Id = c.Params.PostId
   373  
   374  	rpost, err := c.App.UpdatePost(c.App.PostWithProxyRemovedFromImageURLs(post), false)
   375  	if err != nil {
   376  		c.Err = err
   377  		return
   378  	}
   379  
   380  	w.Write([]byte(c.App.PostWithProxyAddedToImageURLs(rpost).ToJson()))
   381  }
   382  
   383  func patchPost(c *Context, w http.ResponseWriter, r *http.Request) {
   384  	c.RequirePostId()
   385  	if c.Err != nil {
   386  		return
   387  	}
   388  
   389  	post := model.PostPatchFromJson(r.Body)
   390  
   391  	if post == nil {
   392  		c.SetInvalidParam("post")
   393  		return
   394  	}
   395  
   396  	if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_POST) {
   397  		c.SetPermissionError(model.PERMISSION_EDIT_POST)
   398  		return
   399  	}
   400  
   401  	if !c.App.SessionHasPermissionToPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) {
   402  		c.SetPermissionError(model.PERMISSION_EDIT_OTHERS_POSTS)
   403  		return
   404  	}
   405  
   406  	patchedPost, err := c.App.PatchPost(c.Params.PostId, c.App.PostPatchWithProxyRemovedFromImageURLs(post))
   407  	if err != nil {
   408  		c.Err = err
   409  		return
   410  	}
   411  
   412  	w.Write([]byte(c.App.PostWithProxyAddedToImageURLs(patchedPost).ToJson()))
   413  }
   414  
   415  func saveIsPinnedPost(c *Context, w http.ResponseWriter, r *http.Request, isPinned bool) {
   416  	c.RequirePostId()
   417  	if c.Err != nil {
   418  		return
   419  	}
   420  
   421  	if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_READ_CHANNEL) {
   422  		c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
   423  		return
   424  	}
   425  
   426  	patch := &model.PostPatch{}
   427  	patch.IsPinned = model.NewBool(isPinned)
   428  
   429  	_, err := c.App.PatchPost(c.Params.PostId, patch)
   430  	if err != nil {
   431  		c.Err = err
   432  		return
   433  	}
   434  
   435  	ReturnStatusOK(w)
   436  }
   437  
   438  func pinPost(c *Context, w http.ResponseWriter, r *http.Request) {
   439  	saveIsPinnedPost(c, w, r, true)
   440  }
   441  
   442  func unpinPost(c *Context, w http.ResponseWriter, r *http.Request) {
   443  	saveIsPinnedPost(c, w, r, false)
   444  }
   445  
   446  func getFileInfosForPost(c *Context, w http.ResponseWriter, r *http.Request) {
   447  	c.RequirePostId()
   448  	if c.Err != nil {
   449  		return
   450  	}
   451  
   452  	if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_READ_CHANNEL) {
   453  		c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
   454  		return
   455  	}
   456  
   457  	if infos, err := c.App.GetFileInfosForPost(c.Params.PostId, false); err != nil {
   458  		c.Err = err
   459  		return
   460  	} else if c.HandleEtag(model.GetEtagForFileInfos(infos), "Get File Infos For Post", w, r) {
   461  		return
   462  	} else {
   463  		w.Header().Set("Cache-Control", "max-age=2592000, public")
   464  		w.Header().Set(model.HEADER_ETAG_SERVER, model.GetEtagForFileInfos(infos))
   465  		w.Write([]byte(model.FileInfosToJson(infos)))
   466  	}
   467  }
   468  
   469  func doPostAction(c *Context, w http.ResponseWriter, r *http.Request) {
   470  	c.RequirePostId().RequireActionId()
   471  	if c.Err != nil {
   472  		return
   473  	}
   474  
   475  	if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_READ_CHANNEL) {
   476  		c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
   477  		return
   478  	}
   479  
   480  	if err := c.App.DoPostAction(c.Params.PostId, c.Params.ActionId, c.Session.UserId); err != nil {
   481  		c.Err = err
   482  		return
   483  	}
   484  
   485  	ReturnStatusOK(w)
   486  }