Mono repo -> Master (#22553)
Combines the following repositories into one: https://github.com/mattermost/mattermost-server https://github.com/mattermost/mattermost-webapp https://github.com/mattermost/focalboard https://github.com/mattermost/mattermost-plugin-playbooks
Этот коммит содержится в:
265
server/channels/app/post_helpers.go
Обычный файл
265
server/channels/app/post_helpers.go
Обычный файл
@@ -0,0 +1,265 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
)
|
||||
|
||||
type filterPostOptions struct {
|
||||
assumeSortedCreatedAt bool
|
||||
}
|
||||
|
||||
type accessibleBounds struct {
|
||||
start int
|
||||
end int
|
||||
}
|
||||
|
||||
func (b accessibleBounds) allAccessible(lenPosts int) bool {
|
||||
return b.start == allAccessibleBounds(lenPosts).start && b.end == allAccessibleBounds(lenPosts).end
|
||||
}
|
||||
|
||||
func (b accessibleBounds) noAccessible() bool {
|
||||
return b.start == noAccessibleBounds.start && b.end == noAccessibleBounds.end
|
||||
}
|
||||
|
||||
// assumes checking was already performed that at least one post is inaccessible
|
||||
func (b accessibleBounds) getInaccessibleRange(listLength int) (int, int) {
|
||||
var start, end int
|
||||
if b.start == 0 {
|
||||
start = b.end + 1
|
||||
end = listLength - 1
|
||||
} else {
|
||||
start = 0
|
||||
end = b.start - 1
|
||||
}
|
||||
return start, end
|
||||
}
|
||||
|
||||
var noAccessibleBounds = accessibleBounds{start: -1, end: -1}
|
||||
var allAccessibleBounds = func(lenPosts int) accessibleBounds { return accessibleBounds{start: 0, end: lenPosts - 1} }
|
||||
|
||||
// getTimeSortedPostAccessibleBounds returns what the boundaries are for accessible posts.
|
||||
// It assumes that CreateAt time for posts is monotonically increasing or decreasing.
|
||||
// It could be either because posts can be returned in ascending or descending time order.
|
||||
// Special values (which can be checked with methods `allAccessible` and `allInaccessible`)
|
||||
// denote if all or none of the posts are accessible.
|
||||
func getTimeSortedPostAccessibleBounds(earliestAccessibleTime int64, lenPosts int, getCreateAt func(int) int64) accessibleBounds {
|
||||
if lenPosts == 0 {
|
||||
return allAccessibleBounds(lenPosts)
|
||||
}
|
||||
if lenPosts == 1 {
|
||||
if getCreateAt(0) >= earliestAccessibleTime {
|
||||
return allAccessibleBounds(lenPosts)
|
||||
}
|
||||
return noAccessibleBounds
|
||||
}
|
||||
|
||||
ascending := getCreateAt(0) < getCreateAt(lenPosts-1)
|
||||
|
||||
idx := sort.Search(lenPosts, func(i int) bool {
|
||||
if ascending {
|
||||
// Ascending order automatically picks the left most post(at idx),
|
||||
// in case multiple posts at idx, idx+1, idx+2... have the same time.
|
||||
return getCreateAt(i) >= earliestAccessibleTime
|
||||
}
|
||||
// Special case(subtracting 1) for descending order to include the right most post(at idx+k),
|
||||
// in case multiple posts at idx, idx+1, idx+2...idx+k have the same time.
|
||||
return getCreateAt(i) <= earliestAccessibleTime-1
|
||||
})
|
||||
|
||||
if ascending {
|
||||
if idx == lenPosts {
|
||||
return noAccessibleBounds
|
||||
}
|
||||
return accessibleBounds{start: idx, end: lenPosts - 1}
|
||||
}
|
||||
|
||||
if idx == 0 {
|
||||
return noAccessibleBounds
|
||||
}
|
||||
return accessibleBounds{start: 0, end: idx - 1}
|
||||
}
|
||||
|
||||
// linearFilterPostList make no assumptions about ordering, go through posts one by one
|
||||
// this is the slower fallback that is still safe if we can not
|
||||
// assume posts are ordered by CreatedAt
|
||||
func linearFilterPostList(postList *model.PostList, earliestAccessibleTime int64) {
|
||||
// filter Posts
|
||||
posts := postList.Posts
|
||||
order := postList.Order
|
||||
|
||||
n := 0
|
||||
for i, postId := range order {
|
||||
if createAt := posts[postId].CreateAt; createAt >= earliestAccessibleTime {
|
||||
order[n] = order[i]
|
||||
n++
|
||||
} else {
|
||||
if createAt > postList.FirstInaccessiblePostTime {
|
||||
postList.FirstInaccessiblePostTime = createAt
|
||||
}
|
||||
delete(posts, postId)
|
||||
}
|
||||
}
|
||||
postList.Order = order[:n]
|
||||
|
||||
// it can happen that some post list results don't have all posts in the Order field.
|
||||
// for example GetPosts in the CollapsedThreads = false path, parents are not added
|
||||
// to Order
|
||||
for postId := range posts {
|
||||
if createAt := posts[postId].CreateAt; createAt < earliestAccessibleTime {
|
||||
if createAt > postList.FirstInaccessiblePostTime {
|
||||
postList.FirstInaccessiblePostTime = createAt
|
||||
}
|
||||
delete(posts, postId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// linearFilterPostsSlice make no assumptions about ordering, go through posts one by one
|
||||
// this is the slower fallback that is still safe if we can not
|
||||
// assume posts are ordered by CreatedAt
|
||||
func linearFilterPostsSlice(posts []*model.Post, earliestAccessibleTime int64) ([]*model.Post, int64) {
|
||||
var firstInaccessiblePostTime int64 = 0
|
||||
n := 0
|
||||
for i := range posts {
|
||||
if createAt := posts[i].CreateAt; createAt >= earliestAccessibleTime {
|
||||
posts[n] = posts[i]
|
||||
n++
|
||||
} else {
|
||||
if createAt > firstInaccessiblePostTime {
|
||||
firstInaccessiblePostTime = createAt
|
||||
}
|
||||
}
|
||||
}
|
||||
return posts[:n], firstInaccessiblePostTime
|
||||
}
|
||||
|
||||
// filterInaccessiblePosts filters out the posts, past the cloud limit
|
||||
func (a *App) filterInaccessiblePosts(postList *model.PostList, options filterPostOptions) *model.AppError {
|
||||
if postList == nil || postList.Posts == nil || len(postList.Posts) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
lastAccessiblePostTime, appErr := a.GetLastAccessiblePostTime()
|
||||
if appErr != nil {
|
||||
return model.NewAppError("filterInaccessiblePosts", "app.last_accessible_post.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
||||
}
|
||||
if lastAccessiblePostTime == 0 {
|
||||
// No need to filter, all posts are accessible
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(postList.Posts) == len(postList.Order) && options.assumeSortedCreatedAt {
|
||||
lenPosts := len(postList.Posts)
|
||||
getCreateAt := func(i int) int64 { return postList.Posts[postList.Order[i]].CreateAt }
|
||||
|
||||
bounds := getTimeSortedPostAccessibleBounds(lastAccessiblePostTime, lenPosts, getCreateAt)
|
||||
|
||||
if bounds.allAccessible(lenPosts) {
|
||||
return nil
|
||||
}
|
||||
if bounds.noAccessible() {
|
||||
if lenPosts > 0 {
|
||||
firstPostCreatedAt := postList.Posts[postList.Order[0]].CreateAt
|
||||
lastPostCreatedAt := postList.Posts[postList.Order[len(postList.Order)-1]].CreateAt
|
||||
postList.FirstInaccessiblePostTime = max(firstPostCreatedAt, lastPostCreatedAt)
|
||||
}
|
||||
postList.Posts = map[string]*model.Post{}
|
||||
postList.Order = []string{}
|
||||
return nil
|
||||
}
|
||||
startInaccessibleIndex, endInaccessibleIndex := bounds.getInaccessibleRange(len(postList.Order))
|
||||
startInaccessibleCreatedAt := postList.Posts[postList.Order[startInaccessibleIndex]].CreateAt
|
||||
endInaccessibleCreatedAt := postList.Posts[postList.Order[endInaccessibleIndex]].CreateAt
|
||||
postList.FirstInaccessiblePostTime = max(startInaccessibleCreatedAt, endInaccessibleCreatedAt)
|
||||
|
||||
posts := postList.Posts
|
||||
order := postList.Order
|
||||
accessibleCount := bounds.end - bounds.start + 1
|
||||
inaccessibleCount := lenPosts - accessibleCount
|
||||
// Linearly cover shorter route to traverse posts map
|
||||
if inaccessibleCount < accessibleCount {
|
||||
for i := 0; i < bounds.start; i++ {
|
||||
delete(posts, order[i])
|
||||
}
|
||||
for i := bounds.end + 1; i < lenPosts; i++ {
|
||||
delete(posts, order[i])
|
||||
}
|
||||
} else {
|
||||
accessiblePosts := make(map[string]*model.Post, accessibleCount)
|
||||
for i := bounds.start; i <= bounds.end; i++ {
|
||||
accessiblePosts[order[i]] = posts[order[i]]
|
||||
}
|
||||
postList.Posts = accessiblePosts
|
||||
}
|
||||
|
||||
postList.Order = postList.Order[bounds.start : bounds.end+1]
|
||||
} else {
|
||||
linearFilterPostList(postList, lastAccessiblePostTime)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isInaccessiblePost indicates if the post is past the cloud plan's limit.
|
||||
func (a *App) isInaccessiblePost(post *model.Post) (int64, *model.AppError) {
|
||||
if post == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
pl := &model.PostList{
|
||||
Order: []string{post.Id},
|
||||
Posts: map[string]*model.Post{post.Id: post},
|
||||
}
|
||||
|
||||
return pl.FirstInaccessiblePostTime, a.filterInaccessiblePosts(pl, filterPostOptions{assumeSortedCreatedAt: true})
|
||||
}
|
||||
|
||||
// getFilteredAccessiblePosts returns accessible posts filtered as per the cloud plan's limit and also indicates if there were any inaccessible posts
|
||||
func (a *App) getFilteredAccessiblePosts(posts []*model.Post, options filterPostOptions) ([]*model.Post, int64, *model.AppError) {
|
||||
if len(posts) == 0 {
|
||||
return posts, 0, nil
|
||||
}
|
||||
|
||||
filteredPosts := []*model.Post{}
|
||||
lastAccessiblePostTime, appErr := a.GetLastAccessiblePostTime()
|
||||
if appErr != nil {
|
||||
return filteredPosts, 0, model.NewAppError("getFilteredAccessiblePosts", "app.last_accessible_post.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
||||
} else if lastAccessiblePostTime == 0 {
|
||||
// No need to filter, all posts are accessible
|
||||
return posts, 0, nil
|
||||
}
|
||||
|
||||
if options.assumeSortedCreatedAt {
|
||||
lenPosts := len(posts)
|
||||
getCreateAt := func(i int) int64 { return posts[i].CreateAt }
|
||||
bounds := getTimeSortedPostAccessibleBounds(lastAccessiblePostTime, lenPosts, getCreateAt)
|
||||
if bounds.allAccessible(lenPosts) {
|
||||
return posts, 0, nil
|
||||
}
|
||||
if bounds.noAccessible() {
|
||||
var firstInaccessiblePostTime int64 = 0
|
||||
if lenPosts > 0 {
|
||||
firstPostCreatedAt := posts[0].CreateAt
|
||||
lastPostCreatedAt := posts[len(posts)-1].CreateAt
|
||||
firstInaccessiblePostTime = max(firstPostCreatedAt, lastPostCreatedAt)
|
||||
}
|
||||
return filteredPosts, firstInaccessiblePostTime, nil
|
||||
}
|
||||
|
||||
startInaccessibleIndex, endInaccessibleIndex := bounds.getInaccessibleRange(len(posts))
|
||||
firstPostCreatedAt := posts[startInaccessibleIndex].CreateAt
|
||||
lastPostCreatedAt := posts[endInaccessibleIndex].CreateAt
|
||||
firstInaccessiblePostTime := max(firstPostCreatedAt, lastPostCreatedAt)
|
||||
filteredPosts = posts[bounds.start : bounds.end+1]
|
||||
return filteredPosts, firstInaccessiblePostTime, nil
|
||||
}
|
||||
|
||||
filteredPosts, firstInaccessiblePostTime := linearFilterPostsSlice(posts, lastAccessiblePostTime)
|
||||
return filteredPosts, firstInaccessiblePostTime, nil
|
||||
}
|
||||
Ссылка в новой задаче
Block a user