diff --git a/.circleci/config.yml b/.circleci/config.yml
index e61ee7343b..1f9739e5d8 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -66,14 +66,37 @@ jobs:
sleep 5
done
if [[ -f "$FILE_DIST" ]]; then
+ echo "Precompiled version of web app found"
mkdir dist && tar -xf $FILE_DIST -C dist --strip-components=1
else
- npm ci --silent && make build
+ echo "Building web app from source"
+ make dist
fi
- persist_to_workspace:
root: /mnt/ramdisk
paths:
- mattermost-webapp
+ setup-focalboard-product:
+ working_directory: /mnt/ramdisk/
+ docker:
+ - image: cimg/go:1.18-node
+ resource_class: large
+ steps:
+ - attach_workspace:
+ at: /mnt/ramdisk
+ - run: |
+ cd focalboard
+ echo "Building Boards product for web app"
+ # make prebuild build-product # TODO figure out how to get this to run without bypassing the Makefile
+ make prebuild
+ cd mattermost-plugin/webapp
+ npm run build:product
+ cd ../..
+ - persist_to_workspace:
+ root: /mnt/ramdisk
+ paths:
+ - focalboard/mattermost-plugin/webapp/dist
+
check-app-layers:
docker:
- image: cimg/go:1.18
@@ -99,7 +122,7 @@ jobs:
command: |
cd mattermost-server
make modules-tidy
- if [[ -n $(git status --porcelain) ]]; then echo "Please tidy up the Go modules using make modules-tidy"; exit 1; fi
+ if [[ -n $(git status --porcelain) ]]; then echo "Please tidy up the Go modules using make modules-tidy"; exit 1; fi
check-store-layers:
docker:
- image: cimg/go:1.18
@@ -460,6 +483,14 @@ workflows:
ignore:
- master
- /^release-.*/
+ - setup-focalboard-product:
+ filters:
+ branches:
+ ignore:
+ - master
+ - /^release-.*/
+ requires:
+ - setup-multi-product-repositories
- check-go-mod-tidy:
requires:
- setup-multi-product-repositories
@@ -503,6 +534,7 @@ workflows:
- check-migrations
- build-api-spec
- setup-mattermost-webapp
+ - setup-focalboard-product
- build-focalboard:
requires:
- check-go-mod-tidy
@@ -532,7 +564,8 @@ workflows:
- check-mattermost-vet
- check-migrations
- build-api-spec
- - setup-mattermost-webapp
+ - setup-mattermost-webapp
+ - setup-focalboard-product
- test:
name: test-postgres-normal
dbdriver: postgres
@@ -549,7 +582,8 @@ workflows:
- check-mattermost-vet
- check-migrations
- build-api-spec
- - setup-mattermost-webapp
+ - setup-mattermost-webapp
+ - setup-focalboard-product
- test:
name: test-postgres-binary
dbdriver: postgres
@@ -566,7 +600,8 @@ workflows:
- check-mattermost-vet
- check-migrations
- build-api-spec
- - setup-mattermost-webapp
+ - setup-mattermost-webapp
+ - setup-focalboard-product
- test-schema:
requires:
- check-go-mod-tidy
@@ -580,6 +615,7 @@ workflows:
- check-migrations
- build-api-spec
- setup-mattermost-webapp
+ - setup-focalboard-product
- upload-artifacts-to-s3:
context: mattermost-ci-pr-builds-s3
requires:
@@ -605,6 +641,14 @@ workflows:
- master
- /^release-.*/
- cloud
+ - setup-focalboard-product:
+ filters:
+ branches:
+ ignore:
+ - master
+ - /^release-.*/
+ requires:
+ - setup-multi-product-repositories
- check-go-mod-tidy:
requires:
- setup-multi-product-repositories
@@ -652,6 +696,7 @@ workflows:
- check-migrations
- build-api-spec
- setup-mattermost-webapp
+ - setup-focalboard-product
- build-focalboard:
requires:
- check-go-mod-tidy
diff --git a/api4/apitestlib.go b/api4/apitestlib.go
index 9b5e53abfc..4606541277 100644
--- a/api4/apitestlib.go
+++ b/api4/apitestlib.go
@@ -71,6 +71,7 @@ type TestHelper struct {
IncludeCacheLayer bool
+ LogBuffer *mlog.Buffer
TestLogger *mlog.Logger
}
@@ -119,11 +120,16 @@ func setupTestHelper(dbStore store.Store, searchEngine *searchengine.Broker, ent
options = append(options, app.StoreOverride(dbStore))
}
+ buffer := &mlog.Buffer{}
+
testLogger, _ := mlog.NewLogger()
logCfg, _ := config.MloggerConfigFromLoggerConfig(&memoryConfig.LogSettings, nil, config.GetLogFileLocation)
if errCfg := testLogger.ConfigureTargets(logCfg, nil); errCfg != nil {
panic("failed to configure test logger: " + errCfg.Error())
}
+ if errW := mlog.AddWriterTarget(testLogger, buffer, true, mlog.StdAll...); errW != nil {
+ panic("failed to add writer target to test logger: " + errW.Error())
+ }
// lock logger config so server init cannot override it during testing.
testLogger.LockConfiguration()
options = append(options, app.SetLogger(testLogger))
@@ -140,6 +146,7 @@ func setupTestHelper(dbStore store.Store, searchEngine *searchengine.Broker, ent
IncludeCacheLayer: includeCache,
Context: request.EmptyContext(testLogger),
TestLogger: testLogger,
+ LogBuffer: buffer,
}
th.Context.SetLogger(testLogger)
diff --git a/api4/cloud_test.go b/api4/cloud_test.go
index 2092342c89..15ff7b1831 100644
--- a/api4/cloud_test.go
+++ b/api4/cloud_test.go
@@ -515,8 +515,9 @@ func TestGetCloudProducts(t *testing.T) {
SKU: "sku",
PriceID: "price_id",
Family: "family",
- RecurringInterval: "recurring_interval",
+ RecurringInterval: "monthly",
BillingScheme: "billing_scheme",
+ CrossSellsTo: "",
},
{
ID: "prod_test2",
@@ -526,8 +527,9 @@ func TestGetCloudProducts(t *testing.T) {
SKU: "sku2",
PriceID: "price_id2",
Family: "family2",
- RecurringInterval: "recurring_interval2",
+ RecurringInterval: "monthly",
BillingScheme: "billing_scheme2",
+ CrossSellsTo: "prod_test3",
},
{
ID: "prod_test3",
@@ -537,29 +539,36 @@ func TestGetCloudProducts(t *testing.T) {
SKU: "sku3",
PriceID: "price_id3",
Family: "family3",
- RecurringInterval: "recurring_interval3",
+ RecurringInterval: "yearly",
BillingScheme: "billing_scheme3",
+ CrossSellsTo: "prod_test2",
},
}
sanitizedProducts := []*model.Product{
{
- ID: "prod_test1",
- Name: "name",
- PricePerSeat: 10,
- SKU: "sku",
+ ID: "prod_test1",
+ Name: "name",
+ PricePerSeat: 10,
+ SKU: "sku",
+ RecurringInterval: "monthly",
+ CrossSellsTo: "",
},
{
- ID: "prod_test2",
- Name: "name2",
- PricePerSeat: 100,
- SKU: "sku2",
+ ID: "prod_test2",
+ Name: "name2",
+ PricePerSeat: 100,
+ SKU: "sku2",
+ RecurringInterval: "monthly",
+ CrossSellsTo: "prod_test3",
},
{
- ID: "prod_test3",
- Name: "name3",
- PricePerSeat: 1000,
- SKU: "sku3",
+ ID: "prod_test3",
+ Name: "name3",
+ PricePerSeat: 1000,
+ SKU: "sku3",
+ RecurringInterval: "yearly",
+ CrossSellsTo: "prod_test2",
},
}
t.Run("get products for admins", func(t *testing.T) {
@@ -615,8 +624,9 @@ func TestGetCloudProducts(t *testing.T) {
require.Equal(t, returnedProducts[0].Description, "")
require.Equal(t, returnedProducts[0].PriceID, "")
require.Equal(t, returnedProducts[0].Family, model.SubscriptionFamily(""))
- require.Equal(t, returnedProducts[0].RecurringInterval, model.RecurringInterval(""))
+ require.Equal(t, returnedProducts[0].RecurringInterval, model.RecurringInterval("monthly"))
require.Equal(t, returnedProducts[0].BillingScheme, model.BillingScheme(""))
+ require.Equal(t, returnedProducts[0].CrossSellsTo, "")
require.Equal(t, returnedProducts[1].ID, "prod_test2")
require.Equal(t, returnedProducts[1].Name, "name2")
@@ -625,8 +635,9 @@ func TestGetCloudProducts(t *testing.T) {
require.Equal(t, returnedProducts[1].Description, "")
require.Equal(t, returnedProducts[1].PriceID, "")
require.Equal(t, returnedProducts[1].Family, model.SubscriptionFamily(""))
- require.Equal(t, returnedProducts[1].RecurringInterval, model.RecurringInterval(""))
+ require.Equal(t, returnedProducts[1].RecurringInterval, model.RecurringInterval("monthly"))
require.Equal(t, returnedProducts[1].BillingScheme, model.BillingScheme(""))
+ require.Equal(t, returnedProducts[1].CrossSellsTo, "prod_test3")
require.Equal(t, returnedProducts[2].ID, "prod_test3")
require.Equal(t, returnedProducts[2].Name, "name3")
@@ -635,7 +646,8 @@ func TestGetCloudProducts(t *testing.T) {
require.Equal(t, returnedProducts[2].Description, "")
require.Equal(t, returnedProducts[2].PriceID, "")
require.Equal(t, returnedProducts[2].Family, model.SubscriptionFamily(""))
- require.Equal(t, returnedProducts[2].RecurringInterval, model.RecurringInterval(""))
+ require.Equal(t, returnedProducts[2].RecurringInterval, model.RecurringInterval("yearly"))
require.Equal(t, returnedProducts[2].BillingScheme, model.BillingScheme(""))
+ require.Equal(t, returnedProducts[2].CrossSellsTo, "prod_test2")
})
}
diff --git a/api4/group.go b/api4/group.go
index abcde27459..da17d31ce3 100644
--- a/api4/group.go
+++ b/api4/group.go
@@ -105,9 +105,15 @@ func getGroup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ restrictions, appErr := c.App.GetViewUsersRestrictions(c.AppContext.Session().UserId)
+ if appErr != nil {
+ c.Err = appErr
+ return
+ }
+
group, appErr := c.App.GetGroup(c.Params.GroupId, &model.GetGroupOpts{
IncludeMemberCount: c.Params.IncludeMemberCount,
- })
+ }, restrictions)
if appErr != nil {
c.Err = appErr
return
@@ -206,7 +212,7 @@ func patchGroup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- group, appErr := c.App.GetGroup(c.Params.GroupId, nil)
+ group, appErr := c.App.GetGroup(c.Params.GroupId, nil, nil)
if appErr != nil {
c.Err = appErr
return
@@ -321,7 +327,7 @@ func linkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- group, appErr := c.App.GetGroup(c.Params.GroupId, nil)
+ group, appErr := c.App.GetGroup(c.Params.GroupId, nil, nil)
if appErr != nil {
c.Err = appErr
return
@@ -657,7 +663,7 @@ func getGroupMembers(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- group, appErr := c.App.GetGroup(c.Params.GroupId, nil)
+ group, appErr := c.App.GetGroup(c.Params.GroupId, nil, nil)
if appErr != nil {
c.Err = appErr
return
@@ -675,7 +681,13 @@ func getGroupMembers(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- members, count, appErr := c.App.GetGroupMemberUsersPage(c.Params.GroupId, c.Params.Page, c.Params.PerPage)
+ restrictions, appErr := c.App.GetViewUsersRestrictions(c.AppContext.Session().UserId)
+ if appErr != nil {
+ c.Err = appErr
+ return
+ }
+
+ members, count, appErr := c.App.GetGroupMemberUsersPage(c.Params.GroupId, c.Params.Page, c.Params.PerPage, restrictions)
if appErr != nil {
c.Err = appErr
return
@@ -718,7 +730,7 @@ func getGroupStats(c *Context, w http.ResponseWriter, r *http.Request) {
}
groupID := c.Params.GroupId
- count, appErr := c.App.GetGroupMemberCount(groupID)
+ count, appErr := c.App.GetGroupMemberCount(groupID, nil)
if appErr != nil {
c.Err = appErr
return
@@ -1011,12 +1023,33 @@ func getGroups(c *Context, w http.ResponseWriter, r *http.Request) {
opts.Since = since
}
- groups, appErr := c.App.GetGroups(c.Params.Page, c.Params.PerPage, opts)
+ restrictions, appErr := c.App.GetViewUsersRestrictions(c.AppContext.Session().UserId)
if appErr != nil {
c.Err = appErr
return
}
+ var (
+ groups = []*model.Group{}
+ canSee bool = true
+ )
+
+ if opts.FilterHasMember != "" {
+ canSee, appErr = c.App.UserCanSeeOtherUser(c.AppContext.Session().UserId, opts.FilterHasMember)
+ if appErr != nil {
+ c.Err = appErr
+ return
+ }
+ }
+
+ if canSee {
+ groups, appErr = c.App.GetGroups(c.Params.Page, c.Params.PerPage, opts, restrictions)
+ if appErr != nil {
+ c.Err = appErr
+ return
+ }
+ }
+
var (
b []byte
err error
@@ -1055,7 +1088,7 @@ func deleteGroup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- group, err := c.App.GetGroup(c.Params.GroupId, nil)
+ group, err := c.App.GetGroup(c.Params.GroupId, nil, nil)
if err != nil {
c.Err = err
return
@@ -1103,7 +1136,7 @@ func addGroupMembers(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- group, appErr := c.App.GetGroup(c.Params.GroupId, nil)
+ group, appErr := c.App.GetGroup(c.Params.GroupId, nil, nil)
if appErr != nil {
c.Err = appErr
return
@@ -1162,7 +1195,7 @@ func deleteGroupMembers(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- group, appErr := c.App.GetGroup(c.Params.GroupId, nil)
+ group, appErr := c.App.GetGroup(c.Params.GroupId, nil, nil)
if appErr != nil {
c.Err = appErr
return
diff --git a/api4/group_test.go b/api4/group_test.go
index b6f155ad1c..bbe4fa33c4 100644
--- a/api4/group_test.go
+++ b/api4/group_test.go
@@ -1518,7 +1518,7 @@ func TestAddMembersToGroup(t *testing.T) {
assert.Len(t, groupMembers, 2)
- count, countErr := th.App.GetGroupMemberCount(group.Id)
+ count, countErr := th.App.GetGroupMemberCount(group.Id, nil)
assert.Nil(t, countErr)
assert.Equal(t, count, int64(2))
diff --git a/api4/team_local.go b/api4/team_local.go
index 8f35695e56..ff9b4da4b3 100644
--- a/api4/team_local.go
+++ b/api4/team_local.go
@@ -160,10 +160,10 @@ func localInviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request)
if len(goodEmails) > 0 {
var invitesWithErrors2 []*model.EmailInviteWithError
if len(channels) > 0 {
- invitesWithErrors2, err = c.App.Srv().EmailService.SendInviteEmailsToTeamAndChannels(team, channels, "Administrator", "mmctl "+model.NewId(), nil, goodEmails, *c.App.Config().ServiceSettings.SiteURL, nil, memberInvite.Message, true)
+ invitesWithErrors2, err = c.App.Srv().EmailService.SendInviteEmailsToTeamAndChannels(team, channels, "Administrator", "mmctl "+model.NewId(), nil, goodEmails, *c.App.Config().ServiceSettings.SiteURL, nil, memberInvite.Message, true, true, false)
invitesWithErrors = append(invitesWithErrors, invitesWithErrors2...)
} else {
- err = c.App.Srv().EmailService.SendInviteEmails(team, "Administrator", "mmctl "+model.NewId(), goodEmails, *c.App.Config().ServiceSettings.SiteURL, nil, false)
+ err = c.App.Srv().EmailService.SendInviteEmails(team, "Administrator", "mmctl "+model.NewId(), goodEmails, *c.App.Config().ServiceSettings.SiteURL, nil, false, true, false)
}
if err != nil {
@@ -200,7 +200,7 @@ func localInviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request)
c.Err = model.NewAppError("localInviteUsersToTeam", "api.team.invite_members.invalid_email.app_error", map[string]any{"Addresses": s}, "", http.StatusBadRequest)
return
}
- err := c.App.Srv().EmailService.SendInviteEmails(team, "Administrator", "mmctl "+model.NewId(), emailList, *c.App.Config().ServiceSettings.SiteURL, nil, false)
+ err := c.App.Srv().EmailService.SendInviteEmails(team, "Administrator", "mmctl "+model.NewId(), emailList, *c.App.Config().ServiceSettings.SiteURL, nil, false, true, false)
if err != nil {
switch {
case errors.Is(err, email.NoRateLimiterError):
diff --git a/api4/user.go b/api4/user.go
index 423edc755c..69a7c6214a 100644
--- a/api4/user.go
+++ b/api4/user.go
@@ -829,7 +829,7 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- profiles, _, appErr = c.App.GetGroupMemberUsersPage(inGroupId, c.Params.Page, c.Params.PerPage)
+ profiles, _, appErr = c.App.GetGroupMemberUsersPage(inGroupId, c.Params.Page, c.Params.PerPage, userGetOptions.ViewRestrictions)
if appErr != nil {
c.Err = appErr
return
@@ -842,7 +842,7 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- profiles, appErr = c.App.GetUsersNotInGroupPage(notInGroupId, c.Params.Page, c.Params.PerPage)
+ profiles, appErr = c.App.GetUsersNotInGroupPage(notInGroupId, c.Params.Page, c.Params.PerPage, userGetOptions.ViewRestrictions)
if appErr != nil {
c.Err = appErr
return
@@ -876,7 +876,7 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) {
}
func requireGroupAccess(c *web.Context, groupID string) *model.AppError {
- group, err := c.App.GetGroup(groupID, nil)
+ group, err := c.App.GetGroup(groupID, nil, nil)
if err != nil {
return err
}
diff --git a/api4/user_test.go b/api4/user_test.go
index 7f937fa7fc..80bdea263e 100644
--- a/api4/user_test.go
+++ b/api4/user_test.go
@@ -2717,13 +2717,26 @@ func TestGetUsersInGroup(t *testing.T) {
})
assert.Nil(t, appErr)
+ cid := model.NewId()
+ customGroup, appErr := th.App.CreateGroup(&model.Group{
+ DisplayName: "dn-foo_" + cid,
+ Name: model.NewString("name" + cid),
+ Source: model.GroupSourceCustom,
+ Description: "description_" + cid,
+ RemoteId: model.NewString(model.NewId()),
+ })
+ assert.Nil(t, appErr)
+
+ user1, err := th.App.CreateUser(th.Context, &model.User{Email: th.GenerateTestEmail(), Nickname: "test user1", Password: "test-password-1", Username: "test-user-1", Roles: model.SystemUserRoleId})
+ assert.Nil(t, err)
+
t.Run("Requires ldap license", func(t *testing.T) {
_, response, err := th.SystemAdminClient.GetUsersInGroup(group.Id, 0, 60, "")
require.Error(t, err)
CheckForbiddenStatus(t, response)
})
- th.App.Srv().SetLicense(model.NewTestLicense("ldap"))
+ th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuProfessional))
t.Run("Requires manage system permission to access users in group", func(t *testing.T) {
th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
@@ -2732,8 +2745,6 @@ func TestGetUsersInGroup(t *testing.T) {
CheckForbiddenStatus(t, response)
})
- user1, err := th.App.CreateUser(th.Context, &model.User{Email: th.GenerateTestEmail(), Nickname: "test user1", Password: "test-password-1", Username: "test-user-1", Roles: model.SystemUserRoleId})
- assert.Nil(t, err)
_, err = th.App.UpsertGroupMember(group.Id, user1.Id)
assert.Nil(t, err)
@@ -2748,6 +2759,26 @@ func TestGetUsersInGroup(t *testing.T) {
require.NoError(t, err)
assert.Empty(t, users)
})
+
+ _, err = th.App.UpsertGroupMember(customGroup.Id, user1.Id)
+ assert.Nil(t, err)
+
+ t.Run("Returns users in custom group when called by regular user", func(t *testing.T) {
+ th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
+ users, _, err := th.Client.GetUsersInGroup(customGroup.Id, 0, 60, "")
+ require.NoError(t, err)
+ assert.Equal(t, users[0].Id, user1.Id)
+ })
+
+ t.Run("Returns no users in custom group when called by guest user", func(t *testing.T) {
+ th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
+ th.App.DemoteUserToGuest(th.Context, th.BasicUser)
+
+ users, _, err := th.Client.GetUsersInGroup(customGroup.Id, 0, 60, "")
+ require.NoError(t, err)
+ assert.Equal(t, len(users), 0)
+ })
+
}
func TestUpdateUserMfa(t *testing.T) {
diff --git a/api4/websocket.go b/api4/websocket.go
index 540188be8a..5f1cb2cdd3 100644
--- a/api4/websocket.go
+++ b/api4/websocket.go
@@ -32,7 +32,7 @@ func connectWebSocket(c *Context, w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
- c.Err = model.NewAppError("connect", "api.web_socket.connect.upgrade.app_error", nil, err.Error(), http.StatusInternalServerError)
+ c.Err = model.NewAppError("connect", "api.web_socket.connect.upgrade.app_error", nil, err.Error(), http.StatusBadRequest)
return
}
diff --git a/api4/websocket_test.go b/api4/websocket_test.go
index 28729b936a..7d35c12578 100644
--- a/api4/websocket_test.go
+++ b/api4/websocket_test.go
@@ -15,6 +15,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/v6/model"
+ "github.com/mattermost/mattermost-server/v6/shared/mlog"
+ "github.com/mattermost/mattermost-server/v6/testlib"
)
func TestWebSocketTrailingSlash(t *testing.T) {
@@ -417,3 +419,15 @@ func TestWebSocketStatuses(t *testing.T) {
WebSocketClient.Close()
}
+
+func TestWebSocketUpgrade(t *testing.T) {
+ th := Setup(t)
+ defer th.TearDown()
+
+ url := fmt.Sprintf("http://localhost:%v", th.App.Srv().ListenAddr.Port) + model.APIURLSuffix + "/websocket"
+ resp, err := http.Get(url)
+ require.NoError(t, err)
+ require.Equal(t, resp.StatusCode, http.StatusBadRequest)
+ require.NoError(t, th.TestLogger.Flush())
+ testlib.AssertLog(t, th.LogBuffer, mlog.LvlDebug.Name, "Failed to upgrade websocket connection.")
+}
diff --git a/app/app_iface.go b/app/app_iface.go
index aed132ba5f..a113169c5c 100644
--- a/app/app_iface.go
+++ b/app/app_iface.go
@@ -639,16 +639,16 @@ type AppIface interface {
GetFlaggedPostsForChannel(userID, channelID string, offset int, limit int) (*model.PostList, *model.AppError)
GetFlaggedPostsForTeam(userID, teamID string, offset int, limit int) (*model.PostList, *model.AppError)
GetGlobalRetentionPolicy() (*model.GlobalRetentionPolicy, *model.AppError)
- GetGroup(id string, opts *model.GetGroupOpts) (*model.Group, *model.AppError)
+ GetGroup(id string, opts *model.GetGroupOpts, viewRestrictions *model.ViewUsersRestrictions) (*model.Group, *model.AppError)
GetGroupByName(name string, opts model.GroupSearchOpts) (*model.Group, *model.AppError)
GetGroupByRemoteID(remoteID string, groupSource model.GroupSource) (*model.Group, *model.AppError)
GetGroupChannel(c request.CTX, userIDs []string) (*model.Channel, *model.AppError)
- GetGroupMemberCount(groupID string) (int64, *model.AppError)
+ GetGroupMemberCount(groupID string, viewRestrictions *model.ViewUsersRestrictions) (int64, *model.AppError)
GetGroupMemberUsers(groupID string) ([]*model.User, *model.AppError)
- GetGroupMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, int, *model.AppError)
+ GetGroupMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, int, *model.AppError)
GetGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) (*model.GroupSyncable, *model.AppError)
GetGroupSyncables(groupID string, syncableType model.GroupSyncableType) ([]*model.GroupSyncable, *model.AppError)
- GetGroups(page, perPage int, opts model.GroupSearchOpts) ([]*model.Group, *model.AppError)
+ GetGroups(page, perPage int, opts model.GroupSearchOpts, viewRestrictions *model.ViewUsersRestrictions) ([]*model.Group, *model.AppError)
GetGroupsAssociatedToChannelsByTeam(teamID string, opts model.GroupSearchOpts) (map[string][]*model.GroupWithSchemeAdmin, *model.AppError)
GetGroupsByChannel(channelID string, opts model.GroupSearchOpts) ([]*model.GroupWithSchemeAdmin, int, *model.AppError)
GetGroupsByIDs(groupIDs []string) ([]*model.Group, *model.AppError)
@@ -831,7 +831,7 @@ type AppIface interface {
GetUsersNotInChannel(teamID string, channelID string, groupConstrained bool, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError)
GetUsersNotInChannelMap(teamID string, channelID string, groupConstrained bool, offset int, limit int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) (map[string]*model.User, *model.AppError)
GetUsersNotInChannelPage(teamID string, channelID string, groupConstrained bool, page int, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError)
- GetUsersNotInGroupPage(groupID string, page int, perPage int) ([]*model.User, *model.AppError)
+ GetUsersNotInGroupPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError)
GetUsersNotInTeam(teamID string, groupConstrained bool, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError)
GetUsersNotInTeamEtag(teamID string, restrictionsHash string) string
GetUsersNotInTeamPage(teamID string, groupConstrained bool, page int, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError)
@@ -1143,6 +1143,7 @@ type AppIface interface {
UpsertGroupSyncable(groupSyncable *model.GroupSyncable) (*model.GroupSyncable, *model.AppError)
UserAlreadyNotifiedOnRequiredFeature(user string, feature model.MattermostPaidFeature) bool
UserCanSeeOtherUser(userID string, otherUserId string) (bool, *model.AppError)
+ UserIsFirstAdmin(user *model.User) bool
VerifyEmailFromToken(c request.CTX, userSuppliedTokenString string) *model.AppError
VerifyUserEmail(userID, email string) *model.AppError
ViewChannel(c request.CTX, view *model.ChannelView, userID string, currentSessionId string, collapsedThreadsSupported bool) (map[string]int64, *model.AppError)
diff --git a/app/email/email.go b/app/email/email.go
index d9f3882c61..ca1d7752e5 100644
--- a/app/email/email.go
+++ b/app/email/email.go
@@ -410,7 +410,17 @@ func (es *Service) SendMfaChangeEmail(email string, activated bool, locale, site
return nil
}
-func (es *Service) SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, errorWhenNotSent bool) error {
+func (es *Service) SendInviteEmails(
+ team *model.Team,
+ senderName string,
+ senderUserId string,
+ invites []string,
+ siteURL string,
+ reminderData *model.TeamInviteReminderData,
+ errorWhenNotSent bool,
+ isSystemAdmin bool,
+ isFirstAdmin bool,
+) error {
if es.perHourEmailRateLimiter == nil {
return NoRateLimiterError
}
@@ -466,7 +476,8 @@ func (es *Service) SendInviteEmails(team *model.Team, senderName string, senderU
mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
continue
}
- data.Props["ButtonURL"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(tokenData), url.QueryEscape(token.Token))
+
+ data.Props["ButtonURL"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s&sbr=%s", siteURL, url.QueryEscape(tokenData), url.QueryEscape(token.Token), es.GetTrackFlowStartedByRole(isFirstAdmin, isSystemAdmin))
body, err := es.templatesContainer.RenderToString("invite_body", data)
if err != nil {
@@ -484,7 +495,19 @@ func (es *Service) SendInviteEmails(team *model.Team, senderName string, senderU
return nil
}
-func (es *Service) SendGuestInviteEmails(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, message string, errorWhenNotSent bool) error {
+func (es *Service) SendGuestInviteEmails(
+ team *model.Team,
+ channels []*model.Channel,
+ senderName string,
+ senderUserId string,
+ senderProfileImage []byte,
+ invites []string,
+ siteURL string,
+ message string,
+ errorWhenNotSent bool,
+ isSystemAdmin bool,
+ isFirstAdmin bool,
+) error {
if es.perHourEmailRateLimiter == nil {
return NoRateLimiterError
}
@@ -545,7 +568,8 @@ func (es *Service) SendGuestInviteEmails(team *model.Team, channels []*model.Cha
mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
continue
}
- data.Props["ButtonURL"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(tokenData), url.QueryEscape(token.Token))
+
+ data.Props["ButtonURL"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s&sbr=%s", siteURL, url.QueryEscape(tokenData), url.QueryEscape(token.Token), es.GetTrackFlowStartedByRole(isFirstAdmin, isSystemAdmin))
if !*es.config().EmailSettings.SendEmailNotifications {
mlog.Info("sending invitation ", mlog.String("to", invite), mlog.String("link", data.Props["ButtonURL"].(string)))
@@ -597,6 +621,8 @@ func (es *Service) SendInviteEmailsToTeamAndChannels(
reminderData *model.TeamInviteReminderData,
message string,
errorWhenNotSent bool,
+ isSystemAdmin bool,
+ isFirstAdmin bool,
) ([]*model.EmailInviteWithError, error) {
if es.perHourEmailRateLimiter == nil {
return nil, NoRateLimiterError
@@ -694,7 +720,8 @@ func (es *Service) SendInviteEmailsToTeamAndChannels(
mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
continue
}
- data.Props["ButtonURL"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(tokenData), url.QueryEscape(token.Token))
+
+ data.Props["ButtonURL"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s&sbr=%s", siteURL, url.QueryEscape(tokenData), url.QueryEscape(token.Token), es.GetTrackFlowStartedByRole(isFirstAdmin, isSystemAdmin))
senderPhoto := ""
embeddedFiles := make(map[string]io.Reader)
diff --git a/app/email/email_test.go b/app/email/email_test.go
index 5742a67c73..0fe3b237e0 100644
--- a/app/email/email_test.go
+++ b/app/email/email_test.go
@@ -76,7 +76,7 @@ func TestSendInviteEmails(t *testing.T) {
t.Run("SendInviteEmails", func(t *testing.T) {
mail.DeleteMailBox(emailTo)
- err := th.service.SendInviteEmails(th.BasicTeam, "test-user", th.BasicUser.Id, []string{emailTo}, "http://testserver", nil, false)
+ err := th.service.SendInviteEmails(th.BasicTeam, "test-user", th.BasicUser.Id, []string{emailTo}, "http://testserver", nil, false, false, false)
require.NoError(t, err)
verifyMailbox(t)
@@ -93,10 +93,10 @@ func TestSendInviteEmails(t *testing.T) {
*cfg.EmailSettings.SMTPPort = originalPort
})
- err := th.service.SendInviteEmails(th.BasicTeam, "test-user", th.BasicUser.Id, []string{emailTo}, "http://testserver", nil, true)
+ err := th.service.SendInviteEmails(th.BasicTeam, "test-user", th.BasicUser.Id, []string{emailTo}, "http://testserver", nil, true, false, false)
require.Error(t, err)
- err = th.service.SendInviteEmails(th.BasicTeam, "test-user", th.BasicUser.Id, []string{emailTo}, "http://testserver", nil, false)
+ err = th.service.SendInviteEmails(th.BasicTeam, "test-user", th.BasicUser.Id, []string{emailTo}, "http://testserver", nil, false, false, false)
require.NoError(t, err)
})
@@ -113,6 +113,8 @@ func TestSendInviteEmails(t *testing.T) {
"http://testserver",
"hello world",
false,
+ false,
+ false,
)
require.NoError(t, err)
@@ -140,6 +142,8 @@ func TestSendInviteEmails(t *testing.T) {
"http://testserver",
"hello world",
false,
+ false,
+ false,
)
require.NoError(t, err)
@@ -153,6 +157,8 @@ func TestSendInviteEmails(t *testing.T) {
"http://testserver",
"hello world",
true,
+ false,
+ false,
)
require.Error(t, err)
@@ -172,6 +178,8 @@ func TestSendInviteEmails(t *testing.T) {
"http://testserver",
message,
false,
+ false,
+ false,
)
require.NoError(t, err)
@@ -180,6 +188,66 @@ func TestSendInviteEmails(t *testing.T) {
require.Contains(t, email.Body.HTML, "sanitized message")
require.Contains(t, email.Body.Text, "sanitized message")
})
+
+ t.Run("SendInviteEmails should contain button URL with 'started by role' param for system user", func(t *testing.T) {
+ mail.DeleteMailBox(emailTo)
+
+ err := th.service.SendInviteEmails(
+ th.BasicTeam,
+ "test-user",
+ th.BasicUser.Id,
+ []string{emailTo},
+ "http://testserver",
+ nil,
+ false,
+ false,
+ false,
+ )
+ require.NoError(t, err)
+
+ email := retrieveEmail(t)
+ require.Contains(t, email.Body.HTML, "&sbr=su")
+ })
+
+ t.Run("SendInviteEmails should contain button URL with 'started by role' param for system admin", func(t *testing.T) {
+ mail.DeleteMailBox(emailTo)
+
+ err := th.service.SendInviteEmails(
+ th.BasicTeam,
+ "test-user",
+ th.BasicUser.Id,
+ []string{emailTo},
+ "http://testserver",
+ nil,
+ false,
+ true,
+ false,
+ )
+ require.NoError(t, err)
+
+ email := retrieveEmail(t)
+ require.Contains(t, email.Body.HTML, "&sbr=sa")
+ })
+
+ t.Run("SendInviteEmails should contain button URL with 'started by role' param for first system admin", func(t *testing.T) {
+ mail.DeleteMailBox(emailTo)
+
+ err := th.service.SendInviteEmails(
+ th.BasicTeam,
+ "test-user",
+ th.BasicUser.Id,
+ []string{emailTo},
+ "http://testserver",
+ nil,
+ false,
+ true,
+ true,
+ )
+ require.NoError(t, err)
+
+ email := retrieveEmail(t)
+ require.Contains(t, email.Body.HTML, "&sbr=fa")
+ })
}
func TestSendCloudUpgradedEmail(t *testing.T) {
diff --git a/app/email/mocks/ServiceInterface.go b/app/email/mocks/ServiceInterface.go
index dbcabb1146..08cddbe4ad 100644
--- a/app/email/mocks/ServiceInterface.go
+++ b/app/email/mocks/ServiceInterface.go
@@ -293,13 +293,13 @@ func (_m *ServiceInterface) SendEmailChangeVerifyEmail(newUserEmail string, loca
return r0
}
-// SendGuestInviteEmails provides a mock function with given fields: team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, message, errorWhenNotSent
-func (_m *ServiceInterface) SendGuestInviteEmails(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, message string, errorWhenNotSent bool) error {
- ret := _m.Called(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, message, errorWhenNotSent)
+// SendGuestInviteEmails provides a mock function with given fields: team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, message, errorWhenNotSent, isSystemAdmin, isFirstAdmin
+func (_m *ServiceInterface) SendGuestInviteEmails(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, message string, errorWhenNotSent bool, isSystemAdmin bool, isFirstAdmin bool) error {
+ ret := _m.Called(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, message, errorWhenNotSent, isSystemAdmin, isFirstAdmin)
var r0 error
- if rf, ok := ret.Get(0).(func(*model.Team, []*model.Channel, string, string, []byte, []string, string, string, bool) error); ok {
- r0 = rf(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, message, errorWhenNotSent)
+ if rf, ok := ret.Get(0).(func(*model.Team, []*model.Channel, string, string, []byte, []string, string, string, bool, bool, bool) error); ok {
+ r0 = rf(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, message, errorWhenNotSent, isSystemAdmin, isFirstAdmin)
} else {
r0 = ret.Error(0)
}
@@ -307,13 +307,13 @@ func (_m *ServiceInterface) SendGuestInviteEmails(team *model.Team, channels []*
return r0
}
-// SendInviteEmails provides a mock function with given fields: team, senderName, senderUserId, invites, siteURL, reminderData, errorWhenNotSent
-func (_m *ServiceInterface) SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, errorWhenNotSent bool) error {
- ret := _m.Called(team, senderName, senderUserId, invites, siteURL, reminderData, errorWhenNotSent)
+// SendInviteEmails provides a mock function with given fields: team, senderName, senderUserId, invites, siteURL, reminderData, errorWhenNotSent, isSystemAdmin, isFirstAdmin
+func (_m *ServiceInterface) SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, errorWhenNotSent bool, isSystemAdmin bool, isFirstAdmin bool) error {
+ ret := _m.Called(team, senderName, senderUserId, invites, siteURL, reminderData, errorWhenNotSent, isSystemAdmin, isFirstAdmin)
var r0 error
- if rf, ok := ret.Get(0).(func(*model.Team, string, string, []string, string, *model.TeamInviteReminderData, bool) error); ok {
- r0 = rf(team, senderName, senderUserId, invites, siteURL, reminderData, errorWhenNotSent)
+ if rf, ok := ret.Get(0).(func(*model.Team, string, string, []string, string, *model.TeamInviteReminderData, bool, bool, bool) error); ok {
+ r0 = rf(team, senderName, senderUserId, invites, siteURL, reminderData, errorWhenNotSent, isSystemAdmin, isFirstAdmin)
} else {
r0 = ret.Error(0)
}
@@ -321,13 +321,13 @@ func (_m *ServiceInterface) SendInviteEmails(team *model.Team, senderName string
return r0
}
-// SendInviteEmailsToTeamAndChannels provides a mock function with given fields: team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent
-func (_m *ServiceInterface) SendInviteEmailsToTeamAndChannels(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, message string, errorWhenNotSent bool) ([]*model.EmailInviteWithError, error) {
- ret := _m.Called(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent)
+// SendInviteEmailsToTeamAndChannels provides a mock function with given fields: team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent, isSystemAdmin, isFirstAdmin
+func (_m *ServiceInterface) SendInviteEmailsToTeamAndChannels(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, message string, errorWhenNotSent bool, isSystemAdmin bool, isFirstAdmin bool) ([]*model.EmailInviteWithError, error) {
+ ret := _m.Called(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent, isSystemAdmin, isFirstAdmin)
var r0 []*model.EmailInviteWithError
- if rf, ok := ret.Get(0).(func(*model.Team, []*model.Channel, string, string, []byte, []string, string, *model.TeamInviteReminderData, string, bool) []*model.EmailInviteWithError); ok {
- r0 = rf(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent)
+ if rf, ok := ret.Get(0).(func(*model.Team, []*model.Channel, string, string, []byte, []string, string, *model.TeamInviteReminderData, string, bool, bool, bool) []*model.EmailInviteWithError); ok {
+ r0 = rf(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent, isSystemAdmin, isFirstAdmin)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.EmailInviteWithError)
@@ -335,8 +335,8 @@ func (_m *ServiceInterface) SendInviteEmailsToTeamAndChannels(team *model.Team,
}
var r1 error
- if rf, ok := ret.Get(1).(func(*model.Team, []*model.Channel, string, string, []byte, []string, string, *model.TeamInviteReminderData, string, bool) error); ok {
- r1 = rf(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent)
+ if rf, ok := ret.Get(1).(func(*model.Team, []*model.Channel, string, string, []byte, []string, string, *model.TeamInviteReminderData, string, bool, bool, bool) error); ok {
+ r1 = rf(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent, isSystemAdmin, isFirstAdmin)
} else {
r1 = ret.Error(1)
}
diff --git a/app/email/service.go b/app/email/service.go
index 135991692e..0f9158e343 100644
--- a/app/email/service.go
+++ b/app/email/service.go
@@ -135,9 +135,9 @@ type ServiceInterface interface {
SendUserAccessTokenAddedEmail(email, locale, siteURL string) error
SendPasswordResetEmail(email string, token *model.Token, locale, siteURL string) (bool, error)
SendMfaChangeEmail(email string, activated bool, locale, siteURL string) error
- SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, errorWhenNotSent bool) error
- SendGuestInviteEmails(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, message string, errorWhenNotSent bool) error
- SendInviteEmailsToTeamAndChannels(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, message string, errorWhenNotSent bool) ([]*model.EmailInviteWithError, error)
+ SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, errorWhenNotSent bool, isSystemAdmin bool, isFirstAdmin bool) error
+ SendGuestInviteEmails(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, message string, errorWhenNotSent bool, isSystemAdmin bool, isFirstAdmin bool) error
+ SendInviteEmailsToTeamAndChannels(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, message string, errorWhenNotSent bool, isSystemAdmin bool, isFirstAdmin bool) ([]*model.EmailInviteWithError, error)
SendDeactivateAccountEmail(email string, locale, siteURL string) error
SendNotificationMail(to, subject, htmlBody string) error
SendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader, messageID string, inReplyTo string, references string) error
diff --git a/app/email/utils.go b/app/email/utils.go
index 623e0c6969..b203a4f62e 100644
--- a/app/email/utils.go
+++ b/app/email/utils.go
@@ -34,3 +34,15 @@ func (es *Service) mailServiceConfig(replyToAddress string) *mail.SMTPConfig {
}
return &cfg
}
+
+func (es *Service) GetTrackFlowStartedByRole(isFirstAdmin bool, isSystemAdmin bool) string {
+ trackFlowStartedByRole := "su"
+
+ if isFirstAdmin {
+ trackFlowStartedByRole = "fa"
+ } else if isSystemAdmin {
+ trackFlowStartedByRole = "sa"
+ }
+
+ return trackFlowStartedByRole
+}
diff --git a/app/group.go b/app/group.go
index beead51982..209c9e28c4 100644
--- a/app/group.go
+++ b/app/group.go
@@ -13,7 +13,7 @@ import (
"github.com/mattermost/mattermost-server/v6/store"
)
-func (a *App) GetGroup(id string, opts *model.GetGroupOpts) (*model.Group, *model.AppError) {
+func (a *App) GetGroup(id string, opts *model.GetGroupOpts, viewRestrictions *model.ViewUsersRestrictions) (*model.Group, *model.AppError) {
group, err := a.Srv().Store().Group().Get(id)
if err != nil {
var nfErr *store.ErrNotFound
@@ -26,7 +26,7 @@ func (a *App) GetGroup(id string, opts *model.GetGroupOpts) (*model.Group, *mode
}
if opts != nil && opts.IncludeMemberCount {
- memberCount, err := a.Srv().Store().Group().GetMemberCount(id)
+ memberCount, err := a.Srv().Store().Group().GetMemberCountWithRestrictions(id, viewRestrictions)
if err != nil {
return nil, model.NewAppError("GetGroup", "app.member_count", nil, "", http.StatusInternalServerError).Wrap(err)
}
@@ -147,7 +147,6 @@ func (a *App) CreateGroupWithUserIds(group *model.GroupWithUserIds) (*model.Grou
messageWs := model.NewWebSocketEvent(model.WebsocketEventReceivedGroup, "", "", "", nil, "")
count, err := a.Srv().Store().Group().GetMemberCount(newGroup.Id)
-
if err != nil {
return nil, model.NewAppError("CreateGroupWithUserIds", "app.group.id.app_error", nil, "", http.StatusBadRequest).Wrap(err)
}
@@ -218,8 +217,8 @@ func (a *App) DeleteGroup(groupID string) (*model.Group, *model.AppError) {
return deletedGroup, nil
}
-func (a *App) GetGroupMemberCount(groupID string) (int64, *model.AppError) {
- count, err := a.Srv().Store().Group().GetMemberCount(groupID)
+func (a *App) GetGroupMemberCount(groupID string, viewRestrictions *model.ViewUsersRestrictions) (int64, *model.AppError) {
+ count, err := a.Srv().Store().Group().GetMemberCountWithRestrictions(groupID, viewRestrictions)
if err != nil {
return 0, model.NewAppError("GetGroupMemberCount", "app.select_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
@@ -236,20 +235,21 @@ func (a *App) GetGroupMemberUsers(groupID string) ([]*model.User, *model.AppErro
return users, nil
}
-func (a *App) GetGroupMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, int, *model.AppError) {
- members, err := a.Srv().Store().Group().GetMemberUsersPage(groupID, page, perPage)
+func (a *App) GetGroupMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, int, *model.AppError) {
+ members, err := a.Srv().Store().Group().GetMemberUsersPage(groupID, page, perPage, viewRestrictions)
if err != nil {
return nil, 0, model.NewAppError("GetGroupMemberUsersPage", "app.select_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
- count, appErr := a.GetGroupMemberCount(groupID)
+ count, appErr := a.GetGroupMemberCount(groupID, viewRestrictions)
if appErr != nil {
return nil, 0, appErr
}
return a.sanitizeProfiles(members, false), int(count), nil
}
-func (a *App) GetUsersNotInGroupPage(groupID string, page int, perPage int) ([]*model.User, *model.AppError) {
- members, err := a.Srv().Store().Group().GetNonMemberUsersPage(groupID, page, perPage)
+
+func (a *App) GetUsersNotInGroupPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
+ members, err := a.Srv().Store().Group().GetNonMemberUsersPage(groupID, page, perPage, viewRestrictions)
if err != nil {
return nil, model.NewAppError("GetUsersNotInGroupPage", "app.select_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
@@ -580,8 +580,8 @@ func (a *App) GetGroupsAssociatedToChannelsByTeam(teamID string, opts model.Grou
return groupsAssociatedByChannelId, nil
}
-func (a *App) GetGroups(page, perPage int, opts model.GroupSearchOpts) ([]*model.Group, *model.AppError) {
- groups, err := a.Srv().Store().Group().GetGroups(page, perPage, opts)
+func (a *App) GetGroups(page, perPage int, opts model.GroupSearchOpts, viewRestrictions *model.ViewUsersRestrictions) ([]*model.Group, *model.AppError) {
+ groups, err := a.Srv().Store().Group().GetGroups(page, perPage, opts, viewRestrictions)
if err != nil {
return nil, model.NewAppError("GetGroups", "app.select_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
diff --git a/app/group_test.go b/app/group_test.go
index 9a1084cfec..3225d881b7 100644
--- a/app/group_test.go
+++ b/app/group_test.go
@@ -17,19 +17,19 @@ func TestGetGroup(t *testing.T) {
defer th.TearDown()
group := th.CreateGroup()
- group, err := th.App.GetGroup(group.Id, nil)
+ group, err := th.App.GetGroup(group.Id, nil, nil)
require.Nil(t, err)
require.NotNil(t, group)
- nilGroup, err := th.App.GetGroup(model.NewId(), nil)
+ nilGroup, err := th.App.GetGroup(model.NewId(), nil, nil)
require.NotNil(t, err)
require.Nil(t, nilGroup)
- group, err = th.App.GetGroup(group.Id, &model.GetGroupOpts{IncludeMemberCount: false})
+ group, err = th.App.GetGroup(group.Id, &model.GetGroupOpts{IncludeMemberCount: false}, nil)
require.Nil(t, err)
require.Nil(t, group.MemberCount)
- group, err = th.App.GetGroup(group.Id, &model.GetGroupOpts{IncludeMemberCount: true})
+ group, err = th.App.GetGroup(group.Id, &model.GetGroupOpts{IncludeMemberCount: true}, nil)
require.Nil(t, err)
require.NotNil(t, group.MemberCount)
}
@@ -369,7 +369,7 @@ func TestGetGroups(t *testing.T) {
defer th.TearDown()
group := th.CreateGroup()
- groups, err := th.App.GetGroups(0, 60, model.GroupSearchOpts{})
+ groups, err := th.App.GetGroups(0, 60, model.GroupSearchOpts{}, nil)
require.Nil(t, err)
require.ElementsMatch(t, []*model.Group{group}, groups)
}
diff --git a/app/notification.go b/app/notification.go
index f143789f4b..c5d36e14b8 100644
--- a/app/notification.go
+++ b/app/notification.go
@@ -1082,7 +1082,7 @@ func (a *App) getGroupsAllowedForReferenceInChannel(channel *model.Channel, team
return groupsMap, nil
}
- groups, err := a.Srv().Store().Group().GetGroups(0, 0, opts)
+ groups, err := a.Srv().Store().Group().GetGroups(0, 0, opts, nil)
if err != nil {
return nil, errors.Wrap(err, "unable to get groups")
}
diff --git a/app/opentracing/opentracing_layer.go b/app/opentracing/opentracing_layer.go
index 97f929c77d..3c5b181160 100644
--- a/app/opentracing/opentracing_layer.go
+++ b/app/opentracing/opentracing_layer.go
@@ -6151,7 +6151,7 @@ func (a *OpenTracingAppLayer) GetGlobalRetentionPolicy() (*model.GlobalRetention
return resultVar0, resultVar1
}
-func (a *OpenTracingAppLayer) GetGroup(id string, opts *model.GetGroupOpts) (*model.Group, *model.AppError) {
+func (a *OpenTracingAppLayer) GetGroup(id string, opts *model.GetGroupOpts, viewRestrictions *model.ViewUsersRestrictions) (*model.Group, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetGroup")
@@ -6163,7 +6163,7 @@ func (a *OpenTracingAppLayer) GetGroup(id string, opts *model.GetGroupOpts) (*mo
}()
defer span.Finish()
- resultVar0, resultVar1 := a.app.GetGroup(id, opts)
+ resultVar0, resultVar1 := a.app.GetGroup(id, opts, viewRestrictions)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@@ -6239,7 +6239,7 @@ func (a *OpenTracingAppLayer) GetGroupChannel(c request.CTX, userIDs []string) (
return resultVar0, resultVar1
}
-func (a *OpenTracingAppLayer) GetGroupMemberCount(groupID string) (int64, *model.AppError) {
+func (a *OpenTracingAppLayer) GetGroupMemberCount(groupID string, viewRestrictions *model.ViewUsersRestrictions) (int64, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetGroupMemberCount")
@@ -6251,7 +6251,7 @@ func (a *OpenTracingAppLayer) GetGroupMemberCount(groupID string) (int64, *model
}()
defer span.Finish()
- resultVar0, resultVar1 := a.app.GetGroupMemberCount(groupID)
+ resultVar0, resultVar1 := a.app.GetGroupMemberCount(groupID, viewRestrictions)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@@ -6283,7 +6283,7 @@ func (a *OpenTracingAppLayer) GetGroupMemberUsers(groupID string) ([]*model.User
return resultVar0, resultVar1
}
-func (a *OpenTracingAppLayer) GetGroupMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, int, *model.AppError) {
+func (a *OpenTracingAppLayer) GetGroupMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, int, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetGroupMemberUsersPage")
@@ -6295,7 +6295,7 @@ func (a *OpenTracingAppLayer) GetGroupMemberUsersPage(groupID string, page int,
}()
defer span.Finish()
- resultVar0, resultVar1, resultVar2 := a.app.GetGroupMemberUsersPage(groupID, page, perPage)
+ resultVar0, resultVar1, resultVar2 := a.app.GetGroupMemberUsersPage(groupID, page, perPage, viewRestrictions)
if resultVar2 != nil {
span.LogFields(spanlog.Error(resultVar2))
@@ -6349,7 +6349,7 @@ func (a *OpenTracingAppLayer) GetGroupSyncables(groupID string, syncableType mod
return resultVar0, resultVar1
}
-func (a *OpenTracingAppLayer) GetGroups(page int, perPage int, opts model.GroupSearchOpts) ([]*model.Group, *model.AppError) {
+func (a *OpenTracingAppLayer) GetGroups(page int, perPage int, opts model.GroupSearchOpts, viewRestrictions *model.ViewUsersRestrictions) ([]*model.Group, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetGroups")
@@ -6361,7 +6361,7 @@ func (a *OpenTracingAppLayer) GetGroups(page int, perPage int, opts model.GroupS
}()
defer span.Finish()
- resultVar0, resultVar1 := a.app.GetGroups(page, perPage, opts)
+ resultVar0, resultVar1 := a.app.GetGroups(page, perPage, opts, viewRestrictions)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@@ -10801,7 +10801,7 @@ func (a *OpenTracingAppLayer) GetUsersNotInChannelPage(teamID string, channelID
return resultVar0, resultVar1
}
-func (a *OpenTracingAppLayer) GetUsersNotInGroupPage(groupID string, page int, perPage int) ([]*model.User, *model.AppError) {
+func (a *OpenTracingAppLayer) GetUsersNotInGroupPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetUsersNotInGroupPage")
@@ -10813,7 +10813,7 @@ func (a *OpenTracingAppLayer) GetUsersNotInGroupPage(groupID string, page int, p
}()
defer span.Finish()
- resultVar0, resultVar1 := a.app.GetUsersNotInGroupPage(groupID, page, perPage)
+ resultVar0, resultVar1 := a.app.GetUsersNotInGroupPage(groupID, page, perPage, viewRestrictions)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@@ -18052,6 +18052,23 @@ func (a *OpenTracingAppLayer) UserCanSeeOtherUser(userID string, otherUserId str
return resultVar0, resultVar1
}
+func (a *OpenTracingAppLayer) UserIsFirstAdmin(user *model.User) bool {
+ origCtx := a.ctx
+ span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UserIsFirstAdmin")
+
+ a.ctx = newCtx
+ a.app.Srv().Store().SetContext(newCtx)
+ defer func() {
+ a.app.Srv().Store().SetContext(origCtx)
+ a.ctx = origCtx
+ }()
+
+ defer span.Finish()
+ resultVar0 := a.app.UserIsFirstAdmin(user)
+
+ return resultVar0
+}
+
func (a *OpenTracingAppLayer) UserIsInAdminRoleGroup(userID string, syncableID string, syncableType model.GroupSyncableType) (bool, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UserIsInAdminRoleGroup")
diff --git a/app/platform/service.go b/app/platform/service.go
index 6620b7f487..018dc9569a 100644
--- a/app/platform/service.go
+++ b/app/platform/service.go
@@ -173,7 +173,7 @@ func New(sc ServiceConfig, options ...Option) (*PlatformService, error) {
ps.initEnterprise()
// Step 5: Init Metrics
- if metricsInterfaceFn != nil {
+ if metricsInterfaceFn != nil && ps.metricsIFace == nil { // if the metrics interface is set by options, do not override it
ps.metricsIFace = metricsInterfaceFn(ps, *ps.configStore.Get().SqlSettings.DriverName, *ps.configStore.Get().SqlSettings.DataSource)
}
diff --git a/app/platform/service_test.go b/app/platform/service_test.go
index b2b7fa1e89..49470021f7 100644
--- a/app/platform/service_test.go
+++ b/app/platform/service_test.go
@@ -124,6 +124,7 @@ func TestMetrics(t *testing.T) {
})
t.Run("ensure the metrics server is started with advanced metrics", func(t *testing.T) {
+ t.Skip("MM-47635")
th := Setup(t, StartMetrics())
defer th.TearDown()
diff --git a/app/plugin_api.go b/app/plugin_api.go
index ad33a4d4d9..a9a3c67907 100644
--- a/app/plugin_api.go
+++ b/app/plugin_api.go
@@ -608,7 +608,7 @@ func (api *PluginAPI) DeleteChannelMember(channelID, userID string) *model.AppEr
}
func (api *PluginAPI) GetGroup(groupId string) (*model.Group, *model.AppError) {
- return api.app.GetGroup(groupId, nil)
+ return api.app.GetGroup(groupId, nil, nil)
}
func (api *PluginAPI) GetGroupByName(name string) (*model.Group, *model.AppError) {
@@ -616,7 +616,7 @@ func (api *PluginAPI) GetGroupByName(name string) (*model.Group, *model.AppError
}
func (api *PluginAPI) GetGroupMemberUsers(groupID string, page, perPage int) ([]*model.User, *model.AppError) {
- users, _, err := api.app.GetGroupMemberUsersPage(groupID, page, perPage)
+ users, _, err := api.app.GetGroupMemberUsersPage(groupID, page, perPage, nil)
return users, err
}
diff --git a/app/team.go b/app/team.go
index fc69fbb652..30020e25ef 100644
--- a/app/team.go
+++ b/app/team.go
@@ -1461,13 +1461,15 @@ func (a *App) InviteNewUsersToTeamGracefully(memberInvite *model.MemberInvite, t
if err != nil {
a.Log().Warn("Unable to get the sender user profile image.", mlog.String("user_id", user.Id), mlog.String("team_id", team.Id), mlog.Err(err))
}
+
+ userIsFirstAdmin := a.UserIsFirstAdmin(user)
var eErr error
var invitesWithErrors2 []*model.EmailInviteWithError
if len(channels) > 0 {
- invitesWithErrors2, eErr = a.Srv().EmailService.SendInviteEmailsToTeamAndChannels(team, channels, user.GetDisplayName(nameFormat), user.Id, senderProfileImage, goodEmails, a.GetSiteURL(), reminderData, memberInvite.Message, true)
+ invitesWithErrors2, eErr = a.Srv().EmailService.SendInviteEmailsToTeamAndChannels(team, channels, user.GetDisplayName(nameFormat), user.Id, senderProfileImage, goodEmails, a.GetSiteURL(), reminderData, memberInvite.Message, true, user.IsSystemAdmin(), userIsFirstAdmin)
inviteListWithErrors = append(inviteListWithErrors, invitesWithErrors2...)
} else {
- eErr = a.Srv().EmailService.SendInviteEmails(team, user.GetDisplayName(nameFormat), user.Id, goodEmails, a.GetSiteURL(), reminderData, true)
+ eErr = a.Srv().EmailService.SendInviteEmails(team, user.GetDisplayName(nameFormat), user.Id, goodEmails, a.GetSiteURL(), reminderData, true, user.IsSystemAdmin(), userIsFirstAdmin)
}
if eErr != nil {
switch {
@@ -1587,7 +1589,8 @@ func (a *App) InviteGuestsToChannelsGracefully(teamID string, guestsInvite *mode
if err != nil {
a.Log().Warn("Unable to get the sender user profile image.", mlog.String("user_id", user.Id), mlog.String("team_id", team.Id), mlog.Err(err))
}
- eErr := a.Srv().EmailService.SendGuestInviteEmails(team, channels, user.GetDisplayName(nameFormat), user.Id, senderProfileImage, goodEmails, a.GetSiteURL(), guestsInvite.Message, true)
+
+ eErr := a.Srv().EmailService.SendGuestInviteEmails(team, channels, user.GetDisplayName(nameFormat), user.Id, senderProfileImage, goodEmails, a.GetSiteURL(), guestsInvite.Message, true, user.IsSystemAdmin(), a.UserIsFirstAdmin(user))
if eErr != nil {
switch {
case errors.Is(eErr, email.SendMailError):
@@ -1644,7 +1647,7 @@ func (a *App) InviteNewUsersToTeam(emailList []string, teamID, senderId string)
}
nameFormat := *a.Config().TeamSettings.TeammateNameDisplay
- eErr := a.Srv().EmailService.SendInviteEmails(team, user.GetDisplayName(nameFormat), user.Id, emailList, a.GetSiteURL(), nil, false)
+ eErr := a.Srv().EmailService.SendInviteEmails(team, user.GetDisplayName(nameFormat), user.Id, emailList, a.GetSiteURL(), nil, false, user.IsSystemAdmin(), a.UserIsFirstAdmin(user))
if eErr != nil {
switch {
case errors.Is(eErr, email.NoRateLimiterError):
@@ -1686,7 +1689,8 @@ func (a *App) InviteGuestsToChannels(teamID string, guestsInvite *model.GuestsIn
if err != nil {
a.Log().Warn("Unable to get the sender user profile image.", mlog.String("user_id", user.Id), mlog.String("team_id", team.Id), mlog.Err(err))
}
- eErr := a.Srv().EmailService.SendGuestInviteEmails(team, channels, user.GetDisplayName(nameFormat), user.Id, senderProfileImage, guestsInvite.Emails, a.GetSiteURL(), guestsInvite.Message, false)
+
+ eErr := a.Srv().EmailService.SendGuestInviteEmails(team, channels, user.GetDisplayName(nameFormat), user.Id, senderProfileImage, guestsInvite.Emails, a.GetSiteURL(), guestsInvite.Message, false, user.IsSystemAdmin(), a.UserIsFirstAdmin(user))
if eErr != nil {
switch {
case errors.Is(eErr, email.NoRateLimiterError):
diff --git a/app/team_test.go b/app/team_test.go
index 14eb0273a7..604cef33d6 100644
--- a/app/team_test.go
+++ b/app/team_test.go
@@ -1457,6 +1457,8 @@ func TestInviteNewUsersToTeamGracefully(t *testing.T) {
"",
mock.Anything,
true,
+ false,
+ false,
).Once().Return(nil)
th.App.Srv().EmailService = &emailServiceMock
@@ -1479,6 +1481,8 @@ func TestInviteNewUsersToTeamGracefully(t *testing.T) {
"",
mock.Anything,
true,
+ false,
+ false,
).Once().Return(email.SendMailError)
th.App.Srv().EmailService = &emailServiceMock
@@ -1505,6 +1509,8 @@ func TestInviteNewUsersToTeamGracefully(t *testing.T) {
mock.Anything,
mock.AnythingOfType("string"),
true,
+ false,
+ false,
).Once().Return([]*model.EmailInviteWithError{}, nil)
th.App.Srv().EmailService = &emailServiceMock
@@ -1527,6 +1533,8 @@ func TestInviteNewUsersToTeamGracefully(t *testing.T) {
"",
mock.Anything,
true,
+ false,
+ false,
).Once().Return(nil)
th.App.Srv().EmailService = &emailServiceMock
@@ -1557,6 +1565,8 @@ func TestInviteGuestsToChannelsGracefully(t *testing.T) {
"",
"",
true,
+ false,
+ false,
).Once().Return(nil)
th.App.Srv().EmailService = &emailServiceMock
@@ -1581,6 +1591,8 @@ func TestInviteGuestsToChannelsGracefully(t *testing.T) {
"",
"",
true,
+ false,
+ false,
).Once().Return(email.SendMailError)
th.App.Srv().EmailService = &emailServiceMock
diff --git a/app/user.go b/app/user.go
index c0a9773e61..9b0840eea6 100644
--- a/app/user.go
+++ b/app/user.go
@@ -2671,3 +2671,25 @@ func getProfileImagePath(userID string) string {
func getProfileImageDirectory(userID string) string {
return filepath.Join("users", userID)
}
+
+func (a *App) UserIsFirstAdmin(user *model.User) bool {
+ if !user.IsSystemAdmin() {
+ return false
+ }
+
+ systemAdminUsers, errServer := a.Srv().Store().User().GetSystemAdminProfiles()
+ if errServer != nil {
+ mlog.Warn("Failed to get system admins to check for first admin from Mattermost.")
+ return false
+ }
+
+ for _, systemAdminUser := range systemAdminUsers {
+ systemAdminUser := systemAdminUser
+
+ if systemAdminUser.CreateAt < user.CreateAt {
+ return false
+ }
+ }
+
+ return true
+}
diff --git a/build/release.mk b/build/release.mk
index 4ac581fee7..e48c3d70b1 100644
--- a/build/release.mk
+++ b/build/release.mk
@@ -190,6 +190,17 @@ else
done
endif
+ @# Products
+
+ @if [ -d $(BUILD_BOARDS_DIR) ] ; then \
+ echo "Copying web app files for Boards product"; \
+ mkdir -p $(DIST_PATH_GENERIC)/client/products/boards; \
+ cp -R $(BUILD_BOARDS_DIR)/mattermost-plugin/webapp/dist/* $(DIST_PATH_GENERIC)/client/products/boards/; \
+ else \
+ echo "Unable to find files for Boards product. Please ensure that the Focalboard repository is checked out alongside the server and run 'make build-product' in it."; \
+ exit 1; \
+ fi
+
package-osx-amd64: package-prep
DIST_PATH_GENERIC=$(DIST_PATH_OSX_AMD64) CURRENT_PACKAGE_ARCH=darwin_amd64 PLUGIN_ARCH=osx-amd64 MMCTL_PLATFORM="Darwin-x86_64" MM_BIN_NAME=mattermost $(MAKE) package-general
@# Package
diff --git a/db/migrations/migrations.list b/db/migrations/migrations.list
index dcdb14f484..1bab52d768 100644
--- a/db/migrations/migrations.list
+++ b/db/migrations/migrations.list
@@ -186,6 +186,10 @@ db/migrations/mysql/000092_add_createat_to_teammembers.down.sql
db/migrations/mysql/000092_add_createat_to_teammembers.up.sql
db/migrations/mysql/000093_notify_admin.down.sql
db/migrations/mysql/000093_notify_admin.up.sql
+db/migrations/mysql/000094_threads_teamid.down.sql
+db/migrations/mysql/000094_threads_teamid.up.sql
+db/migrations/mysql/000095_remove_posts_parentid.down.sql
+db/migrations/mysql/000095_remove_posts_parentid.up.sql
db/migrations/postgres/000001_create_teams.down.sql
db/migrations/postgres/000001_create_teams.up.sql
db/migrations/postgres/000002_create_team_members.down.sql
@@ -372,3 +376,7 @@ db/migrations/postgres/000092_add_createat_to_teamembers.down.sql
db/migrations/postgres/000092_add_createat_to_teamembers.up.sql
db/migrations/postgres/000093_notify_admin.down.sql
db/migrations/postgres/000093_notify_admin.up.sql
+db/migrations/postgres/000094_threads_teamid.down.sql
+db/migrations/postgres/000094_threads_teamid.up.sql
+db/migrations/postgres/000095_remove_posts_parentid.down.sql
+db/migrations/postgres/000095_remove_posts_parentid.up.sql
diff --git a/db/migrations/mysql/000094_threads_teamid.down.sql b/db/migrations/mysql/000094_threads_teamid.down.sql
new file mode 100644
index 0000000000..2ac406dc39
--- /dev/null
+++ b/db/migrations/mysql/000094_threads_teamid.down.sql
@@ -0,0 +1,14 @@
+SET @preparedStatement = (SELECT IF(
+ EXISTS(
+ SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS
+ WHERE table_name = 'Threads'
+ AND table_schema = DATABASE()
+ AND column_name = 'TeamId'
+ ),
+ 'ALTER TABLE Threads DROP COLUMN TeamId;',
+ 'SELECT 1;'
+));
+
+PREPARE removeColumnIfExists FROM @preparedStatement;
+EXECUTE removeColumnIfExists;
+DEALLOCATE PREPARE removeColumnIfExists;
diff --git a/db/migrations/mysql/000094_threads_teamid.up.sql b/db/migrations/mysql/000094_threads_teamid.up.sql
new file mode 100644
index 0000000000..392a2d58ae
--- /dev/null
+++ b/db/migrations/mysql/000094_threads_teamid.up.sql
@@ -0,0 +1,19 @@
+SET @preparedStatement = (SELECT IF(
+ NOT EXISTS(
+ SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE table_name = 'Threads'
+ AND table_schema = DATABASE()
+ AND column_name = 'TeamId'
+ ),
+ 'ALTER TABLE Threads ADD COLUMN TeamId varchar(26) DEFAULT NULL;',
+ 'SELECT 1;'
+));
+
+PREPARE addColumnIfNotExists FROM @preparedStatement;
+EXECUTE addColumnIfNotExists;
+DEALLOCATE PREPARE addColumnIfNotExists;
+
+UPDATE Threads, Channels
+SET Threads.TeamId = Channels.TeamId
+WHERE Channels.Id = Threads.ChannelId
+AND Threads.TeamId IS NULL;
diff --git a/db/migrations/mysql/000095_remove_posts_parentid.down.sql b/db/migrations/mysql/000095_remove_posts_parentid.down.sql
new file mode 100644
index 0000000000..abdf428361
--- /dev/null
+++ b/db/migrations/mysql/000095_remove_posts_parentid.down.sql
@@ -0,0 +1 @@
+-- Intentionally left blank as forward migration is not reversible.
diff --git a/db/migrations/mysql/000095_remove_posts_parentid.up.sql b/db/migrations/mysql/000095_remove_posts_parentid.up.sql
new file mode 100644
index 0000000000..269febfac0
--- /dev/null
+++ b/db/migrations/mysql/000095_remove_posts_parentid.up.sql
@@ -0,0 +1,17 @@
+-- While upgrading from 5.x to 6.x with manual queries, there is a chance that this
+-- migration is skipped. In that case, we need to make sure that the column is dropped.
+
+SET @preparedStatement = (SELECT IF(
+ (
+ SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE table_name = 'Posts'
+ AND table_schema = DATABASE()
+ AND column_name = 'ParentId'
+ ) > 0,
+ 'ALTER TABLE Posts DROP COLUMN ParentId;',
+ 'SELECT 1'
+));
+
+PREPARE alterIfExists FROM @preparedStatement;
+EXECUTE alterIfExists;
+DEALLOCATE PREPARE alterIfExists;
diff --git a/db/migrations/postgres/000094_threads_teamid.down.sql b/db/migrations/postgres/000094_threads_teamid.down.sql
new file mode 100644
index 0000000000..9f4c061f39
--- /dev/null
+++ b/db/migrations/postgres/000094_threads_teamid.down.sql
@@ -0,0 +1 @@
+ALTER TABLE threads DROP COLUMN IF EXISTS teamid;
diff --git a/db/migrations/postgres/000094_threads_teamid.up.sql b/db/migrations/postgres/000094_threads_teamid.up.sql
new file mode 100644
index 0000000000..5c66138f1e
--- /dev/null
+++ b/db/migrations/postgres/000094_threads_teamid.up.sql
@@ -0,0 +1,2 @@
+ALTER TABLE threads ADD COLUMN IF NOT EXISTS teamid VARCHAR(26);
+UPDATE threads SET teamid = channels.teamid FROM channels WHERE threads.teamid IS NULL AND channels.id = threads.channelid;
diff --git a/db/migrations/postgres/000095_remove_posts_parentid.down.sql b/db/migrations/postgres/000095_remove_posts_parentid.down.sql
new file mode 100644
index 0000000000..abdf428361
--- /dev/null
+++ b/db/migrations/postgres/000095_remove_posts_parentid.down.sql
@@ -0,0 +1 @@
+-- Intentionally left blank as forward migration is not reversible.
diff --git a/db/migrations/postgres/000095_remove_posts_parentid.up.sql b/db/migrations/postgres/000095_remove_posts_parentid.up.sql
new file mode 100644
index 0000000000..173d58abef
--- /dev/null
+++ b/db/migrations/postgres/000095_remove_posts_parentid.up.sql
@@ -0,0 +1,4 @@
+-- While upgrading from 5.x to 6.x with manual queries, there is a chance that this
+-- migration is skipped. In that case, we need to make sure that the column is dropped.
+
+ALTER TABLE posts DROP COLUMN IF EXISTS parentid;
diff --git a/model/cloud.go b/model/cloud.go
index fd777672b4..2b072e1f00 100644
--- a/model/cloud.go
+++ b/model/cloud.go
@@ -52,13 +52,16 @@ type Product struct {
Family SubscriptionFamily `json:"product_family"`
RecurringInterval RecurringInterval `json:"recurring_interval"`
BillingScheme BillingScheme `json:"billing_scheme"`
+ CrossSellsTo string `json:"cross_sells_to"`
}
type UserFacingProduct struct {
- ID string `json:"id"`
- Name string `json:"name"`
- SKU string `json:"sku"`
- PricePerSeat float64 `json:"price_per_seat"`
+ ID string `json:"id"`
+ Name string `json:"name"`
+ SKU string `json:"sku"`
+ PricePerSeat float64 `json:"price_per_seat"`
+ RecurringInterval RecurringInterval `json:"recurring_interval"`
+ CrossSellsTo string `json:"cross_sells_to"`
}
// AddOn represents an addon to a product.
diff --git a/model/config.go b/model/config.go
index bccc3904a1..be7a88b61d 100644
--- a/model/config.go
+++ b/model/config.go
@@ -2751,9 +2751,13 @@ type ProductSettings struct {
EnablePublicSharedBoards *bool
}
-func (s *ProductSettings) SetDefaults() {
+func (s *ProductSettings) SetDefaults(plugins map[string]map[string]any) {
if s.EnablePublicSharedBoards == nil {
- s.EnablePublicSharedBoards = NewBool(false)
+ if p, ok := plugins[PluginIdFocalboard]; ok {
+ s.EnablePublicSharedBoards = NewBool(p["enablepublicsharedboards"].(bool))
+ } else {
+ s.EnablePublicSharedBoards = NewBool(false)
+ }
}
}
@@ -3246,7 +3250,7 @@ func (o *Config) SetDefaults() {
o.ThemeSettings.SetDefaults()
o.ClusterSettings.SetDefaults()
o.PluginSettings.SetDefaults(o.LogSettings)
- o.ProductSettings.SetDefaults()
+ o.ProductSettings.SetDefaults(o.PluginSettings.Plugins)
o.AnalyticsSettings.SetDefaults()
o.ComplianceSettings.SetDefaults()
o.LocalizationSettings.SetDefaults()
diff --git a/model/thread.go b/model/thread.go
index f9c4842c71..5d7bdb70f3 100644
--- a/model/thread.go
+++ b/model/thread.go
@@ -26,6 +26,9 @@ type Thread struct {
// DeleteAt is a denormalized copy of the root posts's DeleteAt. In the database, it's
// named ThreadDeleteAt to avoid introducing a query conflict with older server versions.
DeleteAt int64 `json:"delete_at"`
+
+ // TeamId is a denormalized copy of the Channel's teamId.
+ TeamId string `json:"team_id"`
}
type ThreadResponse struct {
diff --git a/model/user.go b/model/user.go
index d3ec889ce8..2140663006 100644
--- a/model/user.go
+++ b/model/user.go
@@ -318,89 +318,90 @@ func (u *User) DeepCopy() *User {
// correctly.
func (u *User) IsValid() *AppError {
if !IsValidId(u.Id) {
- return InvalidUserError("id", "")
+ return InvalidUserError("id", "", u.Id)
}
if u.CreateAt == 0 {
- return InvalidUserError("create_at", u.Id)
+ return InvalidUserError("create_at", u.Id, u.CreateAt)
}
if u.UpdateAt == 0 {
- return InvalidUserError("update_at", u.Id)
+ return InvalidUserError("update_at", u.Id, u.UpdateAt)
}
if u.IsRemote() {
if !IsValidUsernameAllowRemote(u.Username) {
- return InvalidUserError("username", u.Id)
+ return InvalidUserError("username", u.Id, u.Username)
}
} else {
if !IsValidUsername(u.Username) {
- return InvalidUserError("username", u.Id)
+ return InvalidUserError("username", u.Id, u.Username)
}
}
if len(u.Email) > UserEmailMaxLength || u.Email == "" || !IsValidEmail(u.Email) {
- return InvalidUserError("email", u.Id)
+ return InvalidUserError("email", u.Id, u.Email)
}
if utf8.RuneCountInString(u.Nickname) > UserNicknameMaxRunes {
- return InvalidUserError("nickname", u.Id)
+ return InvalidUserError("nickname", u.Id, u.Nickname)
}
if utf8.RuneCountInString(u.Position) > UserPositionMaxRunes {
- return InvalidUserError("position", u.Id)
+ return InvalidUserError("position", u.Id, u.Position)
}
if utf8.RuneCountInString(u.FirstName) > UserFirstNameMaxRunes {
- return InvalidUserError("first_name", u.Id)
+ return InvalidUserError("first_name", u.Id, u.FirstName)
}
if utf8.RuneCountInString(u.LastName) > UserLastNameMaxRunes {
- return InvalidUserError("last_name", u.Id)
+ return InvalidUserError("last_name", u.Id, u.LastName)
}
if u.AuthData != nil && len(*u.AuthData) > UserAuthDataMaxLength {
- return InvalidUserError("auth_data", u.Id)
+ return InvalidUserError("auth_data", u.Id, u.AuthData)
}
if u.AuthData != nil && *u.AuthData != "" && u.AuthService == "" {
- return InvalidUserError("auth_data_type", u.Id)
+ return InvalidUserError("auth_data_type", u.Id, *u.AuthData+" "+u.AuthService)
}
if u.Password != "" && u.AuthData != nil && *u.AuthData != "" {
- return InvalidUserError("auth_data_pwd", u.Id)
+ return InvalidUserError("auth_data_pwd", u.Id, *u.AuthData)
}
if len(u.Password) > UserPasswordMaxLength {
- return InvalidUserError("password_limit", u.Id)
+ return InvalidUserError("password_limit", u.Id, "")
}
if !IsValidLocale(u.Locale) {
- return InvalidUserError("locale", u.Id)
+ return InvalidUserError("locale", u.Id, u.Locale)
}
if len(u.Timezone) > 0 {
if tzJSON, err := json.Marshal(u.Timezone); err != nil {
return NewAppError("User.IsValid", "model.user.is_valid.marshal.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
} else if utf8.RuneCount(tzJSON) > UserTimezoneMaxRunes {
- return InvalidUserError("timezone_limit", u.Id)
+ return InvalidUserError("timezone_limit", u.Id, u.Timezone)
}
}
if len(u.Roles) > UserRolesMaxLength {
return NewAppError("User.IsValid", "model.user.is_valid.roles_limit.app_error",
- map[string]any{"Limit": UserRolesMaxLength}, "user_id="+u.Id, http.StatusBadRequest)
+ map[string]any{"Limit": UserRolesMaxLength}, "user_id="+u.Id+" roles_limit="+u.Roles, http.StatusBadRequest)
}
return nil
}
-func InvalidUserError(fieldName string, userId string) *AppError {
+func InvalidUserError(fieldName, userId string, fieldValue any) *AppError {
id := fmt.Sprintf("model.user.is_valid.%s.app_error", fieldName)
details := ""
if userId != "" {
details = "user_id=" + userId
}
+ details += fmt.Sprintf(" %s=%v", fieldName, fieldValue)
return NewAppError("User.IsValid", id, nil, details, http.StatusBadRequest)
}
diff --git a/model/user_test.go b/model/user_test.go
index d20ebacfa2..a99d36da47 100644
--- a/model/user_test.go
+++ b/model/user_test.go
@@ -77,37 +77,37 @@ func TestUserUpdateMentionKeysFromUsername(t *testing.T) {
func TestUserIsValid(t *testing.T) {
user := User{}
appErr := user.IsValid()
- require.True(t, HasExpectedUserIsValidError(appErr, "id", ""), "expected user is valid error: %s", appErr.Error())
+ require.True(t, HasExpectedUserIsValidError(appErr, "id", "", user.Id), "expected user is valid error: %s", appErr.Error())
user.Id = NewId()
appErr = user.IsValid()
- require.True(t, HasExpectedUserIsValidError(appErr, "create_at", user.Id), "expected user is valid error: %s", appErr.Error())
+ require.True(t, HasExpectedUserIsValidError(appErr, "create_at", user.Id, user.CreateAt), "expected user is valid error: %s", appErr.Error())
user.CreateAt = GetMillis()
appErr = user.IsValid()
- require.True(t, HasExpectedUserIsValidError(appErr, "update_at", user.Id), "expected user is valid error: %s", appErr.Error())
+ require.True(t, HasExpectedUserIsValidError(appErr, "update_at", user.Id, user.UpdateAt), "expected user is valid error: %s", appErr.Error())
user.UpdateAt = GetMillis()
appErr = user.IsValid()
- require.True(t, HasExpectedUserIsValidError(appErr, "username", user.Id), "expected user is valid error: %s", appErr.Error())
+ require.True(t, HasExpectedUserIsValidError(appErr, "username", user.Id, user.Username), "expected user is valid error: %s", appErr.Error())
user.Username = NewId() + "^hello#"
appErr = user.IsValid()
- require.True(t, HasExpectedUserIsValidError(appErr, "username", user.Id), "expected user is valid error: %s", appErr.Error())
+ require.True(t, HasExpectedUserIsValidError(appErr, "username", user.Id, user.Username), "expected user is valid error: %s", appErr.Error())
user.Username = NewId()
appErr = user.IsValid()
- require.True(t, HasExpectedUserIsValidError(appErr, "email", user.Id), "expected user is valid error: %s", appErr.Error())
+ require.True(t, HasExpectedUserIsValidError(appErr, "email", user.Id, user.Email), "expected user is valid error: %s", appErr.Error())
user.Email = strings.Repeat("01234567890", 20)
appErr = user.IsValid()
- require.True(t, HasExpectedUserIsValidError(appErr, "email", user.Id), "expected user is valid error: %s", appErr.Error())
+ require.True(t, HasExpectedUserIsValidError(appErr, "email", user.Id, user.Email), "expected user is valid error: %s", appErr.Error())
user.Email = "user@example.com"
user.Nickname = strings.Repeat("a", 65)
appErr = user.IsValid()
- require.True(t, HasExpectedUserIsValidError(appErr, "nickname", user.Id), "expected user is valid error: %s", appErr.Error())
+ require.True(t, HasExpectedUserIsValidError(appErr, "nickname", user.Id, user.Nickname), "expected user is valid error: %s", appErr.Error())
user.Nickname = strings.Repeat("a", 64)
require.Nil(t, user.IsValid())
@@ -118,12 +118,12 @@ func TestUserIsValid(t *testing.T) {
user.FirstName = strings.Repeat("a", 65)
appErr = user.IsValid()
- require.True(t, HasExpectedUserIsValidError(appErr, "first_name", user.Id), "expected user is valid error: %s", appErr.Error())
+ require.True(t, HasExpectedUserIsValidError(appErr, "first_name", user.Id, user.FirstName), "expected user is valid error: %s", appErr.Error())
user.FirstName = strings.Repeat("a", 64)
user.LastName = strings.Repeat("a", 65)
appErr = user.IsValid()
- require.True(t, HasExpectedUserIsValidError(appErr, "last_name", user.Id), "expected user is valid error: %s", appErr.Error())
+ require.True(t, HasExpectedUserIsValidError(appErr, "last_name", user.Id, user.LastName), "expected user is valid error: %s", appErr.Error())
user.LastName = strings.Repeat("a", 64)
user.Position = strings.Repeat("a", 128)
@@ -131,7 +131,7 @@ func TestUserIsValid(t *testing.T) {
user.Position = strings.Repeat("a", 129)
appErr = user.IsValid()
- require.True(t, HasExpectedUserIsValidError(appErr, "position", user.Id), "expected user is valid error: %s", appErr.Error())
+ require.True(t, HasExpectedUserIsValidError(appErr, "position", user.Id, user.Position), "expected user is valid error: %s", appErr.Error())
user.Position = ""
user.Roles = strings.Repeat("a", UserRolesMaxLength)
@@ -140,10 +140,10 @@ func TestUserIsValid(t *testing.T) {
user.Roles = strings.Repeat("a", UserRolesMaxLength+1)
appErr = user.IsValid()
- require.True(t, HasExpectedUserIsValidError(appErr, "roles_limit", user.Id), "expected user is valid error: %s", appErr.Error())
+ require.True(t, HasExpectedUserIsValidError(appErr, "roles_limit", user.Id, user.Roles), "expected user is valid error: %s", appErr.Error())
}
-func HasExpectedUserIsValidError(err *AppError, fieldName string, userId string) bool {
+func HasExpectedUserIsValidError(err *AppError, fieldName, userId string, fieldValue any) bool {
if err == nil {
return false
}
@@ -151,7 +151,7 @@ func HasExpectedUserIsValidError(err *AppError, fieldName string, userId string)
return err.Where == "User.IsValid" &&
err.Id == fmt.Sprintf("model.user.is_valid.%s.app_error", fieldName) &&
err.StatusCode == http.StatusBadRequest &&
- (userId == "" || err.DetailedError == "user_id="+userId)
+ (userId == "" || err.DetailedError == fmt.Sprintf("user_id=%s %s=%v", userId, fieldName, fieldValue))
}
func TestUserGetFullName(t *testing.T) {
diff --git a/store/opentracinglayer/opentracinglayer.go b/store/opentracinglayer/opentracinglayer.go
index 44f7d09e86..f66f3352b4 100644
--- a/store/opentracinglayer/opentracinglayer.go
+++ b/store/opentracinglayer/opentracinglayer.go
@@ -4152,7 +4152,7 @@ func (s *OpenTracingLayerGroupStore) GetGroupSyncable(groupID string, syncableID
return result, err
}
-func (s *OpenTracingLayerGroupStore) GetGroups(page int, perPage int, opts model.GroupSearchOpts) ([]*model.Group, error) {
+func (s *OpenTracingLayerGroupStore) GetGroups(page int, perPage int, opts model.GroupSearchOpts, viewRestrictions *model.ViewUsersRestrictions) ([]*model.Group, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "GroupStore.GetGroups")
s.Root.Store.SetContext(newCtx)
@@ -4161,7 +4161,7 @@ func (s *OpenTracingLayerGroupStore) GetGroups(page int, perPage int, opts model
}()
defer span.Finish()
- result, err := s.GroupStore.GetGroups(page, perPage, opts)
+ result, err := s.GroupStore.GetGroups(page, perPage, opts, viewRestrictions)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
@@ -4260,6 +4260,24 @@ func (s *OpenTracingLayerGroupStore) GetMemberCount(groupID string) (int64, erro
return result, err
}
+func (s *OpenTracingLayerGroupStore) GetMemberCountWithRestrictions(groupID string, viewRestrictions *model.ViewUsersRestrictions) (int64, error) {
+ origCtx := s.Root.Store.Context()
+ span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "GroupStore.GetMemberCountWithRestrictions")
+ s.Root.Store.SetContext(newCtx)
+ defer func() {
+ s.Root.Store.SetContext(origCtx)
+ }()
+
+ defer span.Finish()
+ result, err := s.GroupStore.GetMemberCountWithRestrictions(groupID, viewRestrictions)
+ if err != nil {
+ span.LogFields(spanlog.Error(err))
+ ext.Error.Set(span, true)
+ }
+
+ return result, err
+}
+
func (s *OpenTracingLayerGroupStore) GetMemberUsers(groupID string) ([]*model.User, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "GroupStore.GetMemberUsers")
@@ -4314,7 +4332,7 @@ func (s *OpenTracingLayerGroupStore) GetMemberUsersNotInChannel(groupID string,
return result, err
}
-func (s *OpenTracingLayerGroupStore) GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, error) {
+func (s *OpenTracingLayerGroupStore) GetMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "GroupStore.GetMemberUsersPage")
s.Root.Store.SetContext(newCtx)
@@ -4323,7 +4341,7 @@ func (s *OpenTracingLayerGroupStore) GetMemberUsersPage(groupID string, page int
}()
defer span.Finish()
- result, err := s.GroupStore.GetMemberUsersPage(groupID, page, perPage)
+ result, err := s.GroupStore.GetMemberUsersPage(groupID, page, perPage, viewRestrictions)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
@@ -4332,7 +4350,7 @@ func (s *OpenTracingLayerGroupStore) GetMemberUsersPage(groupID string, page int
return result, err
}
-func (s *OpenTracingLayerGroupStore) GetNonMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, error) {
+func (s *OpenTracingLayerGroupStore) GetNonMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "GroupStore.GetNonMemberUsersPage")
s.Root.Store.SetContext(newCtx)
@@ -4341,7 +4359,7 @@ func (s *OpenTracingLayerGroupStore) GetNonMemberUsersPage(groupID string, page
}()
defer span.Finish()
- result, err := s.GroupStore.GetNonMemberUsersPage(groupID, page, perPage)
+ result, err := s.GroupStore.GetNonMemberUsersPage(groupID, page, perPage, viewRestrictions)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
diff --git a/store/retrylayer/retrylayer.go b/store/retrylayer/retrylayer.go
index 292924476a..c28caa0d31 100644
--- a/store/retrylayer/retrylayer.go
+++ b/store/retrylayer/retrylayer.go
@@ -4677,11 +4677,11 @@ func (s *RetryLayerGroupStore) GetGroupSyncable(groupID string, syncableID strin
}
-func (s *RetryLayerGroupStore) GetGroups(page int, perPage int, opts model.GroupSearchOpts) ([]*model.Group, error) {
+func (s *RetryLayerGroupStore) GetGroups(page int, perPage int, opts model.GroupSearchOpts, viewRestrictions *model.ViewUsersRestrictions) ([]*model.Group, error) {
tries := 0
for {
- result, err := s.GroupStore.GetGroups(page, perPage, opts)
+ result, err := s.GroupStore.GetGroups(page, perPage, opts, viewRestrictions)
if err == nil {
return result, nil
}
@@ -4803,6 +4803,27 @@ func (s *RetryLayerGroupStore) GetMemberCount(groupID string) (int64, error) {
}
+func (s *RetryLayerGroupStore) GetMemberCountWithRestrictions(groupID string, viewRestrictions *model.ViewUsersRestrictions) (int64, error) {
+
+ tries := 0
+ for {
+ result, err := s.GroupStore.GetMemberCountWithRestrictions(groupID, viewRestrictions)
+ if err == nil {
+ return result, nil
+ }
+ if !isRepeatableError(err) {
+ return result, err
+ }
+ tries++
+ if tries >= 3 {
+ err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
+ return result, err
+ }
+ timepkg.Sleep(100 * timepkg.Millisecond)
+ }
+
+}
+
func (s *RetryLayerGroupStore) GetMemberUsers(groupID string) ([]*model.User, error) {
tries := 0
@@ -4866,11 +4887,11 @@ func (s *RetryLayerGroupStore) GetMemberUsersNotInChannel(groupID string, channe
}
-func (s *RetryLayerGroupStore) GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, error) {
+func (s *RetryLayerGroupStore) GetMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) {
tries := 0
for {
- result, err := s.GroupStore.GetMemberUsersPage(groupID, page, perPage)
+ result, err := s.GroupStore.GetMemberUsersPage(groupID, page, perPage, viewRestrictions)
if err == nil {
return result, nil
}
@@ -4887,11 +4908,11 @@ func (s *RetryLayerGroupStore) GetMemberUsersPage(groupID string, page int, perP
}
-func (s *RetryLayerGroupStore) GetNonMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, error) {
+func (s *RetryLayerGroupStore) GetNonMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) {
tries := 0
for {
- result, err := s.GroupStore.GetNonMemberUsersPage(groupID, page, perPage)
+ result, err := s.GroupStore.GetNonMemberUsersPage(groupID, page, perPage, viewRestrictions)
if err == nil {
return result, nil
}
diff --git a/store/sqlstore/group_store.go b/store/sqlstore/group_store.go
index 8a560395fa..c3341dc9d6 100644
--- a/store/sqlstore/group_store.go
+++ b/store/sqlstore/group_store.go
@@ -401,58 +401,59 @@ func (s *SqlGroupStore) GetMemberUsers(groupID string) ([]*model.User, error) {
return groupMembers, nil
}
-func (s *SqlGroupStore) GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, error) {
+func (s *SqlGroupStore) GetMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) {
groupMembers := []*model.User{}
- query := `
- SELECT
- Users.*
- FROM
- GroupMembers
- JOIN Users ON Users.Id = GroupMembers.UserId
- WHERE
- GroupMembers.DeleteAt = 0
- AND Users.DeleteAt = 0
- AND GroupId = ?
- ORDER BY
- GroupMembers.CreateAt DESC
- LIMIT
- ?
- OFFSET
- ?`
+ query := s.getQueryBuilder().
+ Select("u.*").
+ From("GroupMembers").
+ Join("Users u ON u.Id = GroupMembers.UserId").
+ Where(sq.Eq{"GroupMembers.DeleteAt": 0}).
+ Where(sq.Eq{"u.DeleteAt": 0}).
+ Where(sq.Eq{"GroupId": groupID}).
+ Limit(uint64(perPage)).
+ Offset(uint64(page * perPage)).
+ OrderBy("u.CreateAt DESC")
- if err := s.GetReplicaX().Select(&groupMembers, query, groupID, perPage, page*perPage); err != nil {
+ query = applyViewRestrictionsFilter(query, viewRestrictions, true)
+
+ queryString, args, err := query.ToSql()
+ if err != nil {
+ return nil, errors.Wrap(err, "")
+ }
+
+ if err := s.GetReplicaX().Select(&groupMembers, queryString, args...); err != nil {
return nil, errors.Wrapf(err, "failed to find member Users for Group with id=%s", groupID)
}
return groupMembers, nil
}
-func (s *SqlGroupStore) GetNonMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, error) {
+func (s *SqlGroupStore) GetNonMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) {
groupMembers := []*model.User{}
if err := s.GetReplicaX().Get(&model.Group{}, "SELECT * FROM UserGroups WHERE Id = ?", groupID); err != nil {
return nil, errors.Wrap(err, "GetNonMemberUsersPage")
}
- query := `
- SELECT
- Users.*
- FROM
- Users
- LEFT JOIN
- GroupMembers ON (GroupMembers.UserId = Users.Id AND GroupMembers.GroupId = ?)
- WHERE
- Users.DeleteAt = 0
- AND ( GroupMembers.UserId IS NULL OR GroupMembers.DeleteAt != 0)
- ORDER BY
- GroupMembers.CreateAt DESC
- LIMIT
- ?
- OFFSET
- ?`
+ query := s.getQueryBuilder().
+ Select("u.*").
+ From("Users u").
+ LeftJoin("GroupMembers ON (GroupMembers.UserId = u.Id AND GroupMembers.GroupId = ?)", groupID).
+ Where(sq.Eq{"u.DeleteAt": 0}).
+ Where("(GroupMembers.UserID IS NULL OR GroupMembers.DeleteAt != 0)").
+ Limit(uint64(perPage)).
+ Offset(uint64(page * perPage)).
+ OrderBy("u.CreateAt DESC")
- if err := s.GetReplicaX().Select(&groupMembers, query, groupID, perPage, page*perPage); err != nil {
+ query = applyViewRestrictionsFilter(query, viewRestrictions, true)
+
+ queryString, args, err := query.ToSql()
+ if err != nil {
+ return nil, errors.Wrap(err, "")
+ }
+
+ if err := s.GetReplicaX().Select(&groupMembers, queryString, args...); err != nil {
return nil, errors.Wrapf(err, "failed to find member Users for Group with id=%s", groupID)
}
@@ -460,19 +461,27 @@ func (s *SqlGroupStore) GetNonMemberUsersPage(groupID string, page int, perPage
}
func (s *SqlGroupStore) GetMemberCount(groupID string) (int64, error) {
- query := `
- SELECT
- count(*)
- FROM
- GroupMembers
- JOIN Users ON Users.Id = GroupMembers.UserId
- WHERE
- GroupMembers.GroupId = ?
- AND Users.DeleteAt = 0
- AND GroupMembers.DeleteAt = 0`
+ return s.GetMemberCountWithRestrictions(groupID, nil)
+}
+
+func (s *SqlGroupStore) GetMemberCountWithRestrictions(groupID string, viewRestrictions *model.ViewUsersRestrictions) (int64, error) {
+ query := s.getQueryBuilder().
+ Select("COUNT(DISTINCT u.Id)").
+ From("GroupMembers").
+ Join("Users u ON u.Id = GroupMembers.UserId").
+ Where(sq.Eq{"GroupMembers.GroupId": groupID}).
+ Where(sq.Eq{"u.DeleteAt": 0}).
+ Where(sq.Eq{"GroupMembers.DeleteAt": 0})
+
+ query = applyViewRestrictionsFilter(query, viewRestrictions, false)
+
+ queryString, args, err := query.ToSql()
+ if err != nil {
+ return int64(0), errors.Wrap(err, "")
+ }
var count int64
- err := s.GetReplicaX().Get(&count, query, groupID)
+ err = s.GetReplicaX().Get(&count, queryString, args...)
if err != nil {
return int64(0), errors.Wrapf(err, "failed to count member Users for Group with id=%s", groupID)
}
@@ -1385,15 +1394,30 @@ func (s *SqlGroupStore) GetGroupsAssociatedToChannelsByTeam(teamId string, opts
return groups, nil
}
-func (s *SqlGroupStore) GetGroups(page, perPage int, opts model.GroupSearchOpts) ([]*model.Group, error) {
+func (s *SqlGroupStore) GetGroups(page, perPage int, opts model.GroupSearchOpts, viewRestrictions *model.ViewUsersRestrictions) ([]*model.Group, error) {
groupsVar := groups{}
groupsQuery := s.getQueryBuilder().Select("g.*")
if opts.IncludeMemberCount {
+ countQuery := s.getQueryBuilder().
+ Select("GroupMembers.GroupId, COUNT(DISTINCT u.Id) AS MemberCount").
+ From("GroupMembers").
+ LeftJoin("Users u ON u.Id = GroupMembers.UserId").
+ Where(sq.Eq{"GroupMembers.DeleteAt": 0}).
+ Where(sq.Eq{"u.DeleteAt": 0}).
+ GroupBy("GroupId")
+
+ countQuery = applyViewRestrictionsFilter(countQuery, viewRestrictions, false)
+
+ countString, params, err := countQuery.PlaceholderFormat(sq.Question).ToSql()
+ if err != nil {
+ return nil, errors.Wrap(err, "get_groups_tosql")
+ }
+
groupsQuery = s.getQueryBuilder().
Select("g.*, coalesce(Members.MemberCount, 0) AS MemberCount").
- LeftJoin("(SELECT GroupMembers.GroupId, COUNT(*) AS MemberCount FROM GroupMembers LEFT JOIN Users ON Users.Id = GroupMembers.UserId WHERE GroupMembers.DeleteAt = 0 AND Users.DeleteAt = 0 GROUP BY GroupId) AS Members ON Members.GroupId = g.Id")
+ LeftJoin("("+countString+") AS Members ON Members.GroupId = g.Id", params...)
}
if opts.FilterHasMember != "" {
diff --git a/store/sqlstore/integrity.go b/store/sqlstore/integrity.go
index 51a262a3e4..3ee486aa9c 100644
--- a/store/sqlstore/integrity.go
+++ b/store/sqlstore/integrity.go
@@ -468,6 +468,7 @@ func checkPostsIntegrity(ss *SqlStore, results chan<- model.IntegrityCheckResult
results <- checkPostsFileInfoIntegrity(ss)
results <- checkPostsPostsRootIdIntegrity(ss)
results <- checkPostsReactionsIntegrity(ss)
+ results <- checkThreadsTeamsIntegrity(ss)
}
func checkSchemesIntegrity(ss *SqlStore, results chan<- model.IntegrityCheckResult) {
@@ -511,6 +512,16 @@ func checkUsersIntegrity(ss *SqlStore, results chan<- model.IntegrityCheckResult
results <- checkUsersUserAccessTokensIntegrity(ss)
}
+func checkThreadsTeamsIntegrity(ss *SqlStore) model.IntegrityCheckResult {
+ return checkParentChildIntegrity(ss, relationalCheckConfig{
+ parentName: "Teams",
+ parentIdAttr: "TeamId",
+ childName: "Threads",
+ childIdAttr: "PostId",
+ canParentIdBeEmpty: false,
+ })
+}
+
func CheckRelationalIntegrity(ss *SqlStore, results chan<- model.IntegrityCheckResult) {
mlog.Info("Starting relational integrity checks...")
checkChannelsIntegrity(ss, results)
diff --git a/store/sqlstore/integrity_test.go b/store/sqlstore/integrity_test.go
index 84096ab998..2b775c6bd3 100644
--- a/store/sqlstore/integrity_test.go
+++ b/store/sqlstore/integrity_test.go
@@ -650,9 +650,10 @@ func TestCheckPostsPostsRootIdIntegrity(t *testing.T) {
})
t.Run("should generate a report with one record", func(t *testing.T) {
- root := createPost(ss, model.NewId(), model.NewId(), "", "")
+ channel := createChannel(ss, model.NewId(), model.NewId())
+ root := createPost(ss, channel.Id, model.NewId(), "", "")
rootId := root.Id
- post := createPost(ss, model.NewId(), model.NewId(), root.Id, root.Id)
+ post := createPost(ss, channel.Id, model.NewId(), root.Id, root.Id)
dbmap.Exec(`DELETE FROM Posts WHERE Id=?`, root.Id)
result := checkPostsPostsRootIdIntegrity(store)
require.NoError(t, result.Err)
@@ -663,6 +664,8 @@ func TestCheckPostsPostsRootIdIntegrity(t *testing.T) {
ChildId: &post.Id,
}, data.Records[0])
dbmap.Exec(`DELETE FROM Posts WHERE Id=?`, post.Id)
+ dbmap.Exec(`DELETE FROM Channels WHERE Id=?`, channel.Id)
+ dbmap.Exec(`DELETE FROM Threads WHERE PostId=?`, rootId)
})
})
}
@@ -1602,3 +1605,39 @@ func TestCheckUsersUserAccessTokensIntegrity(t *testing.T) {
})
})
}
+
+func TestCheckThreadsTeamsIntegrity(t *testing.T) {
+ StoreTest(t, func(t *testing.T, ss store.Store) {
+ store := ss.(*SqlStore)
+ dbmap := store.GetMasterX()
+
+ t.Run("should generate a report with no records", func(t *testing.T) {
+ result := checkThreadsTeamsIntegrity(store)
+ require.NoError(t, result.Err)
+ data := result.Data.(model.RelationalIntegrityCheckData)
+ require.Empty(t, data.Records)
+ })
+
+ t.Run("should generate a report with one record", func(t *testing.T) {
+ team := createTeam(ss)
+ channel := createChannel(ss, team.Id, model.NewId())
+ root := createPost(ss, channel.Id, model.NewId(), "", "")
+ post := createPost(ss, channel.Id, model.NewId(), root.Id, root.Id)
+
+ dbmap.Exec(`DELETE FROM Teams WHERE Id=?`, team.Id)
+ result := checkThreadsTeamsIntegrity(store)
+ require.NoError(t, result.Err)
+ data := result.Data.(model.RelationalIntegrityCheckData)
+ require.Len(t, data.Records, 1)
+
+ require.Equal(t, model.OrphanedRecord{
+ ParentId: &team.Id,
+ ChildId: &root.Id,
+ }, data.Records[0])
+ dbmap.Exec(`DELETE FROM Posts WHERE Id=?`, post.Id)
+ dbmap.Exec(`DELETE FROM Posts WHERE Id=?`, root.Id)
+ dbmap.Exec(`DELETE FROM Channels WHERE Id=?`, channel.Id)
+ dbmap.Exec(`DELETE FROM Threads WHERE PostId=?`, root.Id)
+ })
+ })
+}
diff --git a/store/sqlstore/post_store.go b/store/sqlstore/post_store.go
index 33dee42c36..14cec75d2b 100644
--- a/store/sqlstore/post_store.go
+++ b/store/sqlstore/post_store.go
@@ -2948,7 +2948,8 @@ func (s *SqlPostStore) updateThreadsFromPosts(transaction *sqlxTxWrapper, posts
}
threadsByRoots := []*model.Thread{}
- if err := transaction.Select(&threadsByRoots, threadsByRootsSql, threadsByRootsArgs...); err != nil {
+ err = transaction.Select(&threadsByRoots, threadsByRootsSql, threadsByRootsArgs...)
+ if err != nil {
return err
}
@@ -2957,6 +2958,8 @@ func (s *SqlPostStore) updateThreadsFromPosts(transaction *sqlxTxWrapper, posts
threadByRoot[thread.PostId] = thread
}
+ teamIdByChannelId := map[string]string{}
+
for rootId, posts := range postsByRoot {
if thread, found := threadByRoot[rootId]; !found {
data := []struct {
@@ -2986,16 +2989,30 @@ func (s *SqlPostStore) updateThreadsFromPosts(transaction *sqlxTxWrapper, posts
if err != nil {
return err
}
+
+ channelId := posts[0].ChannelId
+ teamId, ok := teamIdByChannelId[channelId]
+ if !ok {
+ // get teamId for channel
+ err = transaction.Get(&teamId, "SELECT COALESCE(Channels.TeamId, '') FROM Channels WHERE Channels.Id=?", channelId)
+ if err != nil {
+ return err
+ }
+
+ // store teamId for channel for efficiency
+ teamIdByChannelId[channelId] = teamId
+ }
// no metadata entry, create one
if _, err := transaction.NamedExec(`INSERT INTO Threads
- (PostId, ChannelId, ReplyCount, LastReplyAt, Participants)
+ (PostId, ChannelId, ReplyCount, LastReplyAt, Participants, TeamId)
VALUES
- (:PostId, :ChannelId, :ReplyCount, :LastReplyAt, :Participants)`, &model.Thread{
+ (:PostId, :ChannelId, :ReplyCount, :LastReplyAt, :Participants, :TeamId)`, &model.Thread{
PostId: rootId,
- ChannelId: posts[0].ChannelId,
+ ChannelId: channelId,
ReplyCount: count,
LastReplyAt: lastReplyAt,
Participants: participants,
+ TeamId: teamId,
}); err != nil {
return err
}
diff --git a/store/sqlstore/thread_store.go b/store/sqlstore/thread_store.go
index a20b4f9362..3306a2856d 100644
--- a/store/sqlstore/thread_store.go
+++ b/store/sqlstore/thread_store.go
@@ -51,6 +51,7 @@ func (s *SqlThreadStore) initializeQueries() {
"Threads.LastReplyAt",
"Threads.Participants",
"COALESCE(Threads.ThreadDeleteAt, 0) AS DeleteAt",
+ "COALESCE(Threads.TeamId, '') AS TeamId",
).
From("Threads")
@@ -62,6 +63,7 @@ func (s *SqlThreadStore) initializeQueries() {
"Threads.LastReplyAt",
"Threads.Participants",
"COALESCE(Threads.ThreadDeleteAt, 0) AS ThreadDeleteAt",
+ "COALESCE(Threads.TeamId, '') AS TeamId",
).
From("Threads")
}
@@ -95,10 +97,9 @@ func (s *SqlThreadStore) getTotalThreadsQuery(userId, teamId string, opts model.
if teamId != "" {
query = query.
- LeftJoin("Channels ON Threads.ChannelId = Channels.Id").
Where(sq.Or{
- sq.Eq{"Channels.TeamId": teamId},
- sq.Eq{"Channels.TeamId": ""},
+ sq.Eq{"Threads.TeamId": teamId},
+ sq.Eq{"Threads.TeamId": ""},
})
}
@@ -158,10 +159,9 @@ func (s *SqlThreadStore) GetTotalUnreadMentions(userId, teamId string, opts mode
if teamId != "" {
query = query.
- LeftJoin("Channels ON Threads.ChannelId = Channels.Id").
Where(sq.Or{
- sq.Eq{"Channels.TeamId": teamId},
- sq.Eq{"Channels.TeamId": ""},
+ sq.Eq{"Threads.TeamId": teamId},
+ sq.Eq{"Threads.TeamId": ""},
})
}
@@ -192,6 +192,7 @@ func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.Get
UnreadMentions int64
Participants model.StringArray
ThreadDeleteAt int64
+ TeamId string
model.Post
}
@@ -223,10 +224,9 @@ func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.Get
// a team at all.
if teamId != "" {
query = query.
- Join("Channels ON Threads.ChannelId = Channels.Id").
Where(sq.Or{
- sq.Eq{"Channels.TeamId": teamId},
- sq.Eq{"Channels.TeamId": ""},
+ sq.Eq{"Threads.TeamId": teamId},
+ sq.Eq{"Threads.TeamId": ""},
})
}
@@ -322,7 +322,7 @@ func (s *SqlThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string)
fetchConditions := sq.And{
sq.Eq{"ThreadMemberships.UserId": userID},
sq.Eq{"ThreadMemberships.Following": true},
- sq.Eq{"Channels.TeamId": teamIDs},
+ sq.Eq{"Threads.TeamId": teamIDs},
sq.Eq{"COALESCE(Threads.ThreadDeleteAt, 0)": 0},
}
@@ -348,10 +348,9 @@ func (s *SqlThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string)
Select("COUNT(Threads.PostId) AS Count, TeamId").
From("Threads").
LeftJoin("ThreadMemberships ON Threads.PostId = ThreadMemberships.PostId").
- LeftJoin("Channels ON Threads.ChannelId = Channels.Id").
Where(fetchConditions).
Where("Threads.LastReplyAt > ThreadMemberships.LastViewed").
- GroupBy("Channels.TeamId")
+ GroupBy("Threads.TeamId")
err := s.GetReplicaX().SelectBuilder(&unreadThreads, repliesQuery)
if err != nil {
@@ -366,9 +365,8 @@ func (s *SqlThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string)
Select("COALESCE(SUM(ThreadMemberships.UnreadMentions),0) AS Count, TeamId").
From("ThreadMemberships").
LeftJoin("Threads ON Threads.PostId = ThreadMemberships.PostId").
- LeftJoin("Channels ON Threads.ChannelId = Channels.Id").
Where(fetchConditions).
- GroupBy("Channels.TeamId")
+ GroupBy("Threads.TeamId")
err := s.GetReplicaX().SelectBuilder(&unreadMentions, mentionsQuery)
if err != nil {
@@ -449,6 +447,7 @@ func (s *SqlThreadStore) GetThreadForUser(teamId string, threadMembership *model
UnreadMentions int64
Participants model.StringArray
ThreadDeleteAt int64
+ TeamId string
model.Post
}
@@ -462,7 +461,7 @@ func (s *SqlThreadStore) GetThreadForUser(teamId string, threadMembership *model
})
fetchConditions := sq.And{
- sq.Or{sq.Eq{"Channels.TeamId": teamId}, sq.Eq{"Channels.TeamId": ""}},
+ sq.Or{sq.Eq{"Threads.TeamId": teamId}, sq.Eq{"Threads.TeamId": ""}},
sq.Eq{"Threads.PostId": threadMembership.PostId},
}
@@ -476,7 +475,6 @@ func (s *SqlThreadStore) GetThreadForUser(teamId string, threadMembership *model
query = query.
Column(sq.Alias(unreadRepliesQuery, "UnreadReplies")).
LeftJoin("Posts ON Posts.Id = Threads.PostId").
- LeftJoin("Channels ON Posts.ChannelId = Channels.Id").
Where(fetchConditions)
err := s.GetReplicaX().GetBuilder(&thread, query)
@@ -671,9 +669,8 @@ func (s *SqlThreadStore) GetMembershipsForUser(userId, teamId string) ([]*model.
query := s.getQueryBuilder().
Select("ThreadMemberships.*").
Join("Threads ON Threads.PostId = ThreadMemberships.PostId").
- Join("Channels ON Threads.ChannelId = Channels.Id").
From("ThreadMemberships").
- Where(sq.Or{sq.Eq{"Channels.TeamId": teamId}, sq.Eq{"Channels.TeamId": ""}}).
+ Where(sq.Or{sq.Eq{"Threads.TeamId": teamId}, sq.Eq{"Threads.TeamId": ""}}).
Where(sq.Eq{"ThreadMemberships.UserId": userId})
err := s.GetReplicaX().SelectBuilder(&memberships, query)
diff --git a/store/store.go b/store/store.go
index f7c33a938e..5b4ca3dc6d 100644
--- a/store/store.go
+++ b/store/store.go
@@ -833,10 +833,11 @@ type GroupStore interface {
Delete(groupID string) (*model.Group, error)
GetMemberUsers(groupID string) ([]*model.User, error)
- GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, error)
+ GetMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error)
+ GetMemberCountWithRestrictions(groupID string, viewRestrictions *model.ViewUsersRestrictions) (int64, error)
GetMemberCount(groupID string) (int64, error)
- GetNonMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, error)
+ GetNonMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error)
GetMemberUsersInTeam(groupID string, teamID string) ([]*model.User, error)
GetMemberUsersNotInChannel(groupID string, channelID string) ([]*model.User, error)
@@ -880,7 +881,7 @@ type GroupStore interface {
GetGroupsAssociatedToChannelsByTeam(teamID string, opts model.GroupSearchOpts) (map[string][]*model.GroupWithSchemeAdmin, error)
CountGroupsByTeam(teamID string, opts model.GroupSearchOpts) (int64, error)
- GetGroups(page, perPage int, opts model.GroupSearchOpts) ([]*model.Group, error)
+ GetGroups(page, perPage int, opts model.GroupSearchOpts, viewRestrictions *model.ViewUsersRestrictions) ([]*model.Group, error)
TeamMembersMinusGroupMembers(teamID string, groupIDs []string, page, perPage int) ([]*model.UserWithGroups, error)
CountTeamMembersMinusGroupMembers(teamID string, groupIDs []string) (int64, error)
diff --git a/store/storetest/channel_store.go b/store/storetest/channel_store.go
index 3f4dd4cedd..2138f011ed 100644
--- a/store/storetest/channel_store.go
+++ b/store/storetest/channel_store.go
@@ -6894,11 +6894,19 @@ func testChannelStoreGetPinnedPosts(t *testing.T, ss store.Store) {
require.Empty(t, pl.Posts, "wasn't supposed to return posts")
t.Run("with correct ReplyCount", func(t *testing.T) {
- channelId := model.NewId()
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
userId := model.NewId()
post1, err := ss.Post().Save(&model.Post{
- ChannelId: channelId,
+ ChannelId: channel.Id,
UserId: userId,
Message: "message",
IsPinned: true,
@@ -6907,7 +6915,7 @@ func testChannelStoreGetPinnedPosts(t *testing.T, ss store.Store) {
time.Sleep(time.Millisecond)
post2, err := ss.Post().Save(&model.Post{
- ChannelId: channelId,
+ ChannelId: channel.Id,
UserId: userId,
Message: "message",
IsPinned: true,
@@ -6916,7 +6924,7 @@ func testChannelStoreGetPinnedPosts(t *testing.T, ss store.Store) {
time.Sleep(time.Millisecond)
post3, err := ss.Post().Save(&model.Post{
- ChannelId: channelId,
+ ChannelId: channel.Id,
UserId: userId,
RootId: post1.Id,
Message: "message",
@@ -6925,7 +6933,7 @@ func testChannelStoreGetPinnedPosts(t *testing.T, ss store.Store) {
require.NoError(t, err)
time.Sleep(time.Millisecond)
- posts, err := ss.Channel().GetPinnedPosts(channelId)
+ posts, err := ss.Channel().GetPinnedPosts(channel.Id)
require.NoError(t, err)
require.Len(t, posts.Posts, 3)
require.Equal(t, posts.Posts[post1.Id].ReplyCount, int64(1))
diff --git a/store/storetest/group_store.go b/store/storetest/group_store.go
index e7a199da24..8696e15bf1 100644
--- a/store/storetest/group_store.go
+++ b/store/storetest/group_store.go
@@ -836,25 +836,25 @@ func testGroupGetMemberUsersPage(t *testing.T, ss store.Store) {
require.NoError(t, err)
// Check returns members
- groupMembers, err := ss.Group().GetMemberUsersPage(group.Id, 0, 100)
+ groupMembers, err := ss.Group().GetMemberUsersPage(group.Id, 0, 100, nil)
require.NoError(t, err)
require.Equal(t, 3, len(groupMembers))
// Check page 1
- groupMembers, err = ss.Group().GetMemberUsersPage(group.Id, 0, 2)
+ groupMembers, err = ss.Group().GetMemberUsersPage(group.Id, 0, 2, nil)
require.NoError(t, err)
require.Equal(t, 2, len(groupMembers))
require.Equal(t, user3.Id, groupMembers[0].Id)
require.Equal(t, user2.Id, groupMembers[1].Id)
// Check page 2
- groupMembers, err = ss.Group().GetMemberUsersPage(group.Id, 1, 2)
+ groupMembers, err = ss.Group().GetMemberUsersPage(group.Id, 1, 2, nil)
require.NoError(t, err)
require.Equal(t, 1, len(groupMembers))
require.Equal(t, user1.Id, groupMembers[0].Id)
// Check madeup id
- groupMembers, err = ss.Group().GetMemberUsersPage(model.NewId(), 0, 100)
+ groupMembers, err = ss.Group().GetMemberUsersPage(model.NewId(), 0, 100, nil)
require.NoError(t, err)
require.Equal(t, 0, len(groupMembers))
@@ -863,7 +863,7 @@ func testGroupGetMemberUsersPage(t *testing.T, ss store.Store) {
require.NoError(t, err)
// Should not return deleted members
- groupMembers, err = ss.Group().GetMemberUsersPage(group.Id, 0, 100)
+ groupMembers, err = ss.Group().GetMemberUsersPage(group.Id, 0, 100, nil)
require.NoError(t, err)
require.Equal(t, 2, len(groupMembers))
}
@@ -3476,6 +3476,13 @@ func testGetGroups(t *testing.T, ss store.Store) {
user2, err := ss.User().Save(u2)
require.NoError(t, err)
+ u3 := &model.User{
+ Email: MakeEmail(),
+ Username: model.NewId(),
+ }
+ user3, err := ss.User().Save(u3)
+ require.NoError(t, err)
+
_, err = ss.Group().UpsertMember(group1.Id, user1.Id)
require.NoError(t, err)
@@ -3485,9 +3492,20 @@ func testGetGroups(t *testing.T, ss store.Store) {
_, err = ss.Group().UpsertMember(group2.Id, user2.Id)
require.NoError(t, err)
+ _, err = ss.Group().UpsertMember(group2.Id, user3.Id)
+ require.NoError(t, err)
+
_, err = ss.Group().UpsertMember(deletedGroup.Id, user1.Id)
require.NoError(t, err)
+ m1 := model.ChannelMember{
+ ChannelId: channel1.Id,
+ UserId: user1.Id,
+ NotifyProps: model.GetDefaultChannelNotifyProps(),
+ }
+ _, err = ss.Channel().SaveMember(&m1)
+ require.NoError(t, err)
+
user2.DeleteAt = 1
u2Update, _ := ss.User().Update(user2, true)
@@ -3519,39 +3537,44 @@ func testGetGroups(t *testing.T, ss store.Store) {
require.NoError(t, nErr)
testCases := []struct {
- Name string
- Page int
- PerPage int
- Opts model.GroupSearchOpts
- Resultf func([]*model.Group) bool
+ Name string
+ Page int
+ PerPage int
+ Opts model.GroupSearchOpts
+ Resultf func([]*model.Group) bool
+ Restrictions *model.ViewUsersRestrictions
}{
{
- Name: "Get all the Groups",
- Opts: model.GroupSearchOpts{},
- Page: 0,
- PerPage: 3,
- Resultf: func(groups []*model.Group) bool { return len(groups) == 3 },
+ Name: "Get all the Groups",
+ Opts: model.GroupSearchOpts{},
+ Page: 0,
+ PerPage: 3,
+ Resultf: func(groups []*model.Group) bool { return len(groups) == 3 },
+ Restrictions: nil,
},
{
- Name: "Get first Group with page 0 with 1 element",
- Opts: model.GroupSearchOpts{},
- Page: 0,
- PerPage: 1,
- Resultf: func(groups []*model.Group) bool { return len(groups) == 1 },
+ Name: "Get first Group with page 0 with 1 element",
+ Opts: model.GroupSearchOpts{},
+ Page: 0,
+ PerPage: 1,
+ Resultf: func(groups []*model.Group) bool { return len(groups) == 1 },
+ Restrictions: nil,
},
{
- Name: "Get single result from page 1",
- Opts: model.GroupSearchOpts{},
- Page: 1,
- PerPage: 1,
- Resultf: func(groups []*model.Group) bool { return len(groups) == 1 },
+ Name: "Get single result from page 1",
+ Opts: model.GroupSearchOpts{},
+ Page: 1,
+ PerPage: 1,
+ Resultf: func(groups []*model.Group) bool { return len(groups) == 1 },
+ Restrictions: nil,
},
{
- Name: "Get multiple results from page 1",
- Opts: model.GroupSearchOpts{},
- Page: 1,
- PerPage: 2,
- Resultf: func(groups []*model.Group) bool { return len(groups) == 2 },
+ Name: "Get multiple results from page 1",
+ Opts: model.GroupSearchOpts{},
+ Page: 1,
+ PerPage: 2,
+ Resultf: func(groups []*model.Group) bool { return len(groups) == 2 },
+ Restrictions: nil,
},
{
Name: "Get group matching name",
@@ -3566,6 +3589,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
}
return true
},
+ Restrictions: nil,
},
{
Name: "Get group matching display name",
@@ -3580,6 +3604,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
}
return true
},
+ Restrictions: nil,
},
{
Name: "Get group matching multiple display names",
@@ -3594,6 +3619,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
}
return true
},
+ Restrictions: nil,
},
{
Name: "Include member counts",
@@ -3605,7 +3631,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
if g.MemberCount == nil {
return false
}
- if g.Id == group1.Id && *g.MemberCount != 1 {
+ if (g.Id == group1.Id || g.Id == group2.Id) && *g.MemberCount != 1 {
return false
}
if g.DeleteAt != 0 {
@@ -3614,6 +3640,31 @@ func testGetGroups(t *testing.T, ss store.Store) {
}
return true
},
+ Restrictions: nil,
+ },
+ {
+ Name: "Include member counts with restrictions",
+ Opts: model.GroupSearchOpts{IncludeMemberCount: true},
+ Page: 0,
+ PerPage: 100,
+ Resultf: func(groups []*model.Group) bool {
+ for _, g := range groups {
+ if g.MemberCount == nil {
+ return false
+ }
+ if g.Id == group1.Id && *g.MemberCount != 1 {
+ return false
+ }
+ if g.Id == group2.Id && *g.MemberCount != 0 {
+ return false
+ }
+ if g.DeleteAt != 0 {
+ return false
+ }
+ }
+ return true
+ },
+ Restrictions: &model.ViewUsersRestrictions{Channels: []string{channel1.Id}},
},
{
Name: "Not associated to team",
@@ -3634,6 +3685,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
}
return true
},
+ Restrictions: nil,
},
{
Name: "Not associated to other team",
@@ -3654,6 +3706,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
}
return true
},
+ Restrictions: nil,
},
{
Name: "Include allow reference",
@@ -3674,6 +3727,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
}
return true
},
+ Restrictions: nil,
},
{
Name: "Use Since return all",
@@ -3691,6 +3745,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
}
return true
},
+ Restrictions: nil,
},
{
Name: "Use Since return none",
@@ -3700,6 +3755,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
Resultf: func(groups []*model.Group) bool {
return len(groups) == 0
},
+ Restrictions: nil,
},
{
Name: "Filter groups from group-constrained teams",
@@ -3709,6 +3765,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
Resultf: func(groups []*model.Group) bool {
return len(groups) == 2 && groups[0].Id == group1.Id && groups[1].Id == group2.Id
},
+ Restrictions: nil,
},
{
Name: "Filter groups from group-constrained page 0",
@@ -3718,6 +3775,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
Resultf: func(groups []*model.Group) bool {
return groups[0].Id == group1.Id
},
+ Restrictions: nil,
},
{
Name: "Filter groups from group-constrained page 1",
@@ -3727,6 +3785,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
Resultf: func(groups []*model.Group) bool {
return groups[0].Id == group2.Id
},
+ Restrictions: nil,
},
{
Name: "Non-group constrained team with no associated groups still returns groups for the child channel",
@@ -3736,6 +3795,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
Resultf: func(groups []*model.Group) bool {
return len(groups) > 0
},
+ Restrictions: nil,
},
{
Name: "Filter by group member",
@@ -3745,6 +3805,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
Resultf: func(groups []*model.Group) bool {
return len(groups) == 1 && groups[0].Id == group1.Id
},
+ Restrictions: nil,
},
{
Name: "Filter by non-existent group member",
@@ -3754,6 +3815,7 @@ func testGetGroups(t *testing.T, ss store.Store) {
Resultf: func(groups []*model.Group) bool {
return len(groups) == 0
},
+ Restrictions: nil,
},
{
Name: "Filter by non-member member",
@@ -3763,12 +3825,13 @@ func testGetGroups(t *testing.T, ss store.Store) {
Resultf: func(groups []*model.Group) bool {
return len(groups) == 2
},
+ Restrictions: nil,
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
- groups, err := ss.Group().GetGroups(tc.Page, tc.PerPage, tc.Opts)
+ groups, err := ss.Group().GetGroups(tc.Page, tc.PerPage, tc.Opts, tc.Restrictions)
require.NoError(t, err)
require.True(t, tc.Resultf(groups))
})
@@ -5048,7 +5111,7 @@ func groupTestGetNonMemberUsersPage(t *testing.T, ss store.Store) {
_, nErr = ss.User().Save(u2)
require.NoError(t, nErr)
- users, err := ss.Group().GetNonMemberUsersPage(group.Id, 0, 1000)
+ users, err := ss.Group().GetNonMemberUsersPage(group.Id, 0, 1000, nil)
require.NoError(t, err)
originalLen := len(users)
@@ -5056,11 +5119,11 @@ func groupTestGetNonMemberUsersPage(t *testing.T, ss store.Store) {
_, err = ss.Group().UpsertMember(group.Id, user1.Id)
require.NoError(t, err)
- users, err = ss.Group().GetNonMemberUsersPage(group.Id, 0, 1000)
+ users, err = ss.Group().GetNonMemberUsersPage(group.Id, 0, 1000, nil)
require.NoError(t, err)
require.Len(t, users, originalLen-1)
- users, err = ss.Group().GetNonMemberUsersPage(model.NewId(), 0, 1000)
+ users, err = ss.Group().GetNonMemberUsersPage(model.NewId(), 0, 1000, nil)
require.Error(t, err)
require.Nil(t, users)
}
diff --git a/store/storetest/mocks/GroupStore.go b/store/storetest/mocks/GroupStore.go
index f3f2f839a9..80d6fdd492 100644
--- a/store/storetest/mocks/GroupStore.go
+++ b/store/storetest/mocks/GroupStore.go
@@ -577,13 +577,13 @@ func (_m *GroupStore) GetGroupSyncable(groupID string, syncableID string, syncab
return r0, r1
}
-// GetGroups provides a mock function with given fields: page, perPage, opts
-func (_m *GroupStore) GetGroups(page int, perPage int, opts model.GroupSearchOpts) ([]*model.Group, error) {
- ret := _m.Called(page, perPage, opts)
+// GetGroups provides a mock function with given fields: page, perPage, opts, viewRestrictions
+func (_m *GroupStore) GetGroups(page int, perPage int, opts model.GroupSearchOpts, viewRestrictions *model.ViewUsersRestrictions) ([]*model.Group, error) {
+ ret := _m.Called(page, perPage, opts, viewRestrictions)
var r0 []*model.Group
- if rf, ok := ret.Get(0).(func(int, int, model.GroupSearchOpts) []*model.Group); ok {
- r0 = rf(page, perPage, opts)
+ if rf, ok := ret.Get(0).(func(int, int, model.GroupSearchOpts, *model.ViewUsersRestrictions) []*model.Group); ok {
+ r0 = rf(page, perPage, opts, viewRestrictions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Group)
@@ -591,8 +591,8 @@ func (_m *GroupStore) GetGroups(page int, perPage int, opts model.GroupSearchOpt
}
var r1 error
- if rf, ok := ret.Get(1).(func(int, int, model.GroupSearchOpts) error); ok {
- r1 = rf(page, perPage, opts)
+ if rf, ok := ret.Get(1).(func(int, int, model.GroupSearchOpts, *model.ViewUsersRestrictions) error); ok {
+ r1 = rf(page, perPage, opts, viewRestrictions)
} else {
r1 = ret.Error(1)
}
@@ -713,6 +713,27 @@ func (_m *GroupStore) GetMemberCount(groupID string) (int64, error) {
return r0, r1
}
+// GetMemberCountWithRestrictions provides a mock function with given fields: groupID, viewRestrictions
+func (_m *GroupStore) GetMemberCountWithRestrictions(groupID string, viewRestrictions *model.ViewUsersRestrictions) (int64, error) {
+ ret := _m.Called(groupID, viewRestrictions)
+
+ var r0 int64
+ if rf, ok := ret.Get(0).(func(string, *model.ViewUsersRestrictions) int64); ok {
+ r0 = rf(groupID, viewRestrictions)
+ } else {
+ r0 = ret.Get(0).(int64)
+ }
+
+ var r1 error
+ if rf, ok := ret.Get(1).(func(string, *model.ViewUsersRestrictions) error); ok {
+ r1 = rf(groupID, viewRestrictions)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
// GetMemberUsers provides a mock function with given fields: groupID
func (_m *GroupStore) GetMemberUsers(groupID string) ([]*model.User, error) {
ret := _m.Called(groupID)
@@ -782,13 +803,13 @@ func (_m *GroupStore) GetMemberUsersNotInChannel(groupID string, channelID strin
return r0, r1
}
-// GetMemberUsersPage provides a mock function with given fields: groupID, page, perPage
-func (_m *GroupStore) GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, error) {
- ret := _m.Called(groupID, page, perPage)
+// GetMemberUsersPage provides a mock function with given fields: groupID, page, perPage, viewRestrictions
+func (_m *GroupStore) GetMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) {
+ ret := _m.Called(groupID, page, perPage, viewRestrictions)
var r0 []*model.User
- if rf, ok := ret.Get(0).(func(string, int, int) []*model.User); ok {
- r0 = rf(groupID, page, perPage)
+ if rf, ok := ret.Get(0).(func(string, int, int, *model.ViewUsersRestrictions) []*model.User); ok {
+ r0 = rf(groupID, page, perPage, viewRestrictions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.User)
@@ -796,8 +817,8 @@ func (_m *GroupStore) GetMemberUsersPage(groupID string, page int, perPage int)
}
var r1 error
- if rf, ok := ret.Get(1).(func(string, int, int) error); ok {
- r1 = rf(groupID, page, perPage)
+ if rf, ok := ret.Get(1).(func(string, int, int, *model.ViewUsersRestrictions) error); ok {
+ r1 = rf(groupID, page, perPage, viewRestrictions)
} else {
r1 = ret.Error(1)
}
@@ -805,13 +826,13 @@ func (_m *GroupStore) GetMemberUsersPage(groupID string, page int, perPage int)
return r0, r1
}
-// GetNonMemberUsersPage provides a mock function with given fields: groupID, page, perPage
-func (_m *GroupStore) GetNonMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, error) {
- ret := _m.Called(groupID, page, perPage)
+// GetNonMemberUsersPage provides a mock function with given fields: groupID, page, perPage, viewRestrictions
+func (_m *GroupStore) GetNonMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) {
+ ret := _m.Called(groupID, page, perPage, viewRestrictions)
var r0 []*model.User
- if rf, ok := ret.Get(0).(func(string, int, int) []*model.User); ok {
- r0 = rf(groupID, page, perPage)
+ if rf, ok := ret.Get(0).(func(string, int, int, *model.ViewUsersRestrictions) []*model.User); ok {
+ r0 = rf(groupID, page, perPage, viewRestrictions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.User)
@@ -819,8 +840,8 @@ func (_m *GroupStore) GetNonMemberUsersPage(groupID string, page int, perPage in
}
var r1 error
- if rf, ok := ret.Get(1).(func(string, int, int) error); ok {
- r1 = rf(groupID, page, perPage)
+ if rf, ok := ret.Get(1).(func(string, int, int, *model.ViewUsersRestrictions) error); ok {
+ r1 = rf(groupID, page, perPage, viewRestrictions)
} else {
r1 = ret.Error(1)
}
diff --git a/store/storetest/post_store.go b/store/storetest/post_store.go
index dd1ba40ae4..677d049218 100644
--- a/store/storetest/post_store.go
+++ b/store/storetest/post_store.go
@@ -78,20 +78,45 @@ func testPostStoreSave(t *testing.T, ss store.Store) {
})
t.Run("Save replies", func(t *testing.T) {
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o1 := model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel1.Id
o1.UserId = model.NewId()
o1.RootId = model.NewId()
o1.Message = NewTestId()
+ channel2, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName2",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o2 := model.Post{}
- o2.ChannelId = model.NewId()
+ o2.ChannelId = channel2.Id
o2.UserId = model.NewId()
o2.RootId = o1.RootId
o2.Message = NewTestId()
+ channel3, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName3",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o3 := model.Post{}
- o3.ChannelId = model.NewId()
+ o3.ChannelId = channel3.Id
o3.UserId = model.NewId()
o3.RootId = model.NewId()
o3.Message = NewTestId()
@@ -123,12 +148,21 @@ func testPostStoreSave(t *testing.T, ss store.Store) {
})
t.Run("Update reply should update the UpdateAt of the root post", func(t *testing.T) {
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
rootPost := model.Post{}
- rootPost.ChannelId = model.NewId()
+ rootPost.ChannelId = channel.Id
rootPost.UserId = model.NewId()
rootPost.Message = NewTestId()
- _, err := ss.Post().Save(&rootPost)
+ _, err = ss.Post().Save(&rootPost)
require.NoError(t, err)
time.Sleep(2 * time.Millisecond)
@@ -241,26 +275,58 @@ func testPostStoreSaveMultiple(t *testing.T, ss store.Store) {
})
t.Run("Save replies", func(t *testing.T) {
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channel2, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName2",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channel3, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName3",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channel4, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName4",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
o1 := model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel1.Id
o1.UserId = model.NewId()
o1.RootId = model.NewId()
o1.Message = NewTestId()
o2 := model.Post{}
- o2.ChannelId = model.NewId()
+ o2.ChannelId = channel2.Id
o2.UserId = model.NewId()
o2.RootId = o1.RootId
o2.Message = NewTestId()
o3 := model.Post{}
- o3.ChannelId = model.NewId()
+ o3.ChannelId = channel3.Id
o3.UserId = model.NewId()
o3.RootId = model.NewId()
o3.Message = NewTestId()
o4 := model.Post{}
- o4.ChannelId = model.NewId()
+ o4.ChannelId = channel4.Id
o4.UserId = model.NewId()
o4.Message = NewTestId()
@@ -291,8 +357,17 @@ func testPostStoreSaveMultiple(t *testing.T, ss store.Store) {
})
t.Run("Update reply should update the UpdateAt of the root post", func(t *testing.T) {
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
rootPost := model.Post{}
- rootPost.ChannelId = model.NewId()
+ rootPost.ChannelId = channel.Id
rootPost.UserId = model.NewId()
rootPost.Message = NewTestId()
@@ -302,7 +377,7 @@ func testPostStoreSaveMultiple(t *testing.T, ss store.Store) {
replyPost.Message = NewTestId()
replyPost.RootId = rootPost.Id
- _, _, err := ss.Post().SaveMultiple([]*model.Post{&rootPost, &replyPost})
+ _, _, err = ss.Post().SaveMultiple([]*model.Post{&rootPost, &replyPost})
require.NoError(t, err)
rrootPost, err := ss.Post().GetSingle(rootPost.Id, false)
@@ -367,34 +442,74 @@ func testPostStoreSaveMultiple(t *testing.T, ss store.Store) {
})
t.Run("Thread participants", func(t *testing.T) {
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o1 := model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel1.Id
o1.UserId = model.NewId()
o1.Message = "jessica hyde" + model.NewId() + "b"
root, err := ss.Post().Save(&o1)
require.NoError(t, err)
+ channel2, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName2",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channel3, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName3",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channel4, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName4",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channel5, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName5",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
o2 := model.Post{}
- o2.ChannelId = model.NewId()
+ o2.ChannelId = channel2.Id
o2.UserId = model.NewId()
o2.RootId = root.Id
o2.Message = "zz" + model.NewId() + "b"
o3 := model.Post{}
- o3.ChannelId = model.NewId()
+ o3.ChannelId = channel3.Id
o3.UserId = model.NewId()
o3.RootId = root.Id
o3.Message = "zz" + model.NewId() + "b"
o4 := model.Post{}
- o4.ChannelId = model.NewId()
+ o4.ChannelId = channel4.Id
o4.UserId = o2.UserId
o4.RootId = root.Id
o4.Message = "zz" + model.NewId() + "b"
o5 := model.Post{}
- o5.ChannelId = model.NewId()
+ o5.ChannelId = channel5.Id
o5.UserId = o1.UserId
o5.RootId = root.Id
o5.Message = "zz" + model.NewId() + "b"
@@ -438,7 +553,7 @@ func testPostStoreSaveMultiple(t *testing.T, ss store.Store) {
}
func testPostStoreSaveChannelMsgCounts(t *testing.T, ss store.Store) {
- c1 := &model.Channel{Name: model.NewId(), DisplayName: "posttestchannel", Type: model.ChannelTypeOpen}
+ c1 := &model.Channel{Name: model.NewId(), DisplayName: "posttestchannel", Type: model.ChannelTypeOpen, TeamId: model.NewId()}
_, err := ss.Channel().Save(c1, 1000000)
require.NoError(t, err)
@@ -484,15 +599,23 @@ func testPostStoreSaveChannelMsgCounts(t *testing.T, ss store.Store) {
}
func testPostStoreGet(t *testing.T, ss store.Store) {
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
o1 := &model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel.Id
o1.UserId = model.NewId()
o1.Message = NewTestId()
etag1 := ss.Post().GetEtag(o1.ChannelId, false, false)
require.Equal(t, 0, strings.Index(etag1, model.CurrentVersion+"."), "Invalid Etag")
- o1, err := ss.Post().Save(o1)
+ o1, err = ss.Post().Save(o1)
require.NoError(t, err)
etag2 := ss.Post().GetEtag(o1.ChannelId, false, false)
@@ -511,8 +634,17 @@ func testPostStoreGet(t *testing.T, ss store.Store) {
func testPostStoreGetForThread(t *testing.T, ss store.Store) {
t.Run("Post thread is followed", func(t *testing.T) {
- o1 := &model.Post{ChannelId: model.NewId(), UserId: model.NewId(), Message: NewTestId()}
- o1, err := ss.Post().Save(o1)
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ o1 := &model.Post{ChannelId: channel.Id, UserId: model.NewId(), Message: NewTestId()}
+ o1, err = ss.Post().Save(o1)
require.NoError(t, err)
_, err = ss.Post().Save(&model.Post{ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestId(), RootId: o1.Id})
require.NoError(t, err)
@@ -532,8 +664,17 @@ func testPostStoreGetForThread(t *testing.T, ss store.Store) {
})
t.Run("Post thread is explicitly not followed", func(t *testing.T) {
- o1 := &model.Post{ChannelId: model.NewId(), UserId: model.NewId(), Message: NewTestId()}
- o1, err := ss.Post().Save(o1)
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ o1 := &model.Post{ChannelId: channel.Id, UserId: model.NewId(), Message: NewTestId()}
+ o1, err = ss.Post().Save(o1)
require.NoError(t, err)
_, err = ss.Post().Save(&model.Post{ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestId(), RootId: o1.Id})
require.NoError(t, err)
@@ -553,8 +694,17 @@ func testPostStoreGetForThread(t *testing.T, ss store.Store) {
})
t.Run("Post threadmembership does not exist", func(t *testing.T) {
- o1 := &model.Post{ChannelId: model.NewId(), UserId: model.NewId(), Message: NewTestId()}
- o1, err := ss.Post().Save(o1)
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ o1 := &model.Post{ChannelId: channel.Id, UserId: model.NewId(), Message: NewTestId()}
+ o1, err = ss.Post().Save(o1)
require.NoError(t, err)
_, err = ss.Post().Save(&model.Post{ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestId(), RootId: o1.Id})
require.NoError(t, err)
@@ -570,7 +720,16 @@ func testPostStoreGetForThread(t *testing.T, ss store.Store) {
t.Run("Pagination", func(t *testing.T) {
t.Skip("MM-46134")
- o1, err := ss.Post().Save(&model.Post{ChannelId: model.NewId(), UserId: model.NewId(), Message: NewTestId()})
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ o1, err := ss.Post().Save(&model.Post{ChannelId: channel.Id, UserId: model.NewId(), Message: NewTestId()})
require.NoError(t, err)
_, err = ss.Post().Save(&model.Post{ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestId(), RootId: o1.Id})
require.NoError(t, err)
@@ -698,8 +857,17 @@ func testPostStoreGetForThread(t *testing.T, ss store.Store) {
}
func testPostStoreGetSingle(t *testing.T, ss store.Store) {
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o1 := &model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel.Id
o1.UserId = model.NewId()
o1.Message = NewTestId()
@@ -708,7 +876,7 @@ func testPostStoreGetSingle(t *testing.T, ss store.Store) {
o2.UserId = o1.UserId
o2.Message = NewTestId()
- o1, err := ss.Post().Save(o1)
+ o1, err = ss.Post().Save(o1)
require.NoError(t, err)
o2, err = ss.Post().Save(o2)
@@ -757,11 +925,20 @@ func testPostStoreGetSingle(t *testing.T, ss store.Store) {
}
func testPostStoreUpdate(t *testing.T, ss store.Store) {
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o1 := &model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel.Id
o1.UserId = model.NewId()
o1.Message = NewTestId()
- o1, err := ss.Post().Save(o1)
+ o1, err = ss.Post().Save(o1)
require.NoError(t, err)
o2 := &model.Post{}
@@ -828,8 +1005,15 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) {
require.Equal(t, ro3a.Hashtags, o3a.Hashtags, "Failed to update/get")
}
+ channel2, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
o4, err := ss.Post().Save(&model.Post{
- ChannelId: model.NewId(),
+ ChannelId: channel2.Id,
UserId: model.NewId(),
Message: model.NewId(),
Filenames: []string{"test"},
@@ -856,9 +1040,18 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) {
func testPostStoreDelete(t *testing.T, ss store.Store) {
t.Run("single post, no replies", func(t *testing.T) {
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
// Create a post
rootPost, err := ss.Post().Save(&model.Post{
- ChannelId: model.NewId(),
+ ChannelId: channel.Id,
UserId: model.NewId(),
Message: model.NewRandomString(10),
})
@@ -896,9 +1089,18 @@ func testPostStoreDelete(t *testing.T, ss store.Store) {
})
t.Run("thread with one reply", func(t *testing.T) {
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
// Create a root post
rootPost, err := ss.Post().Save(&model.Post{
- ChannelId: model.NewId(),
+ ChannelId: channel.Id,
UserId: model.NewId(),
Message: NewTestId(),
})
@@ -929,9 +1131,18 @@ func testPostStoreDelete(t *testing.T, ss store.Store) {
})
t.Run("thread with multiple replies", func(t *testing.T) {
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
// Create a root post
rootPost1, err := ss.Post().Save(&model.Post{
- ChannelId: model.NewId(),
+ ChannelId: channel.Id,
UserId: model.NewId(),
Message: NewTestId(),
})
@@ -955,9 +1166,17 @@ func testPostStoreDelete(t *testing.T, ss store.Store) {
})
require.NoError(t, err)
+ channel2, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
// Create another root post in a separate channel
rootPost2, err := ss.Post().Save(&model.Post{
- ChannelId: model.NewId(),
+ ChannelId: channel2.Id,
UserId: model.NewId(),
Message: NewTestId(),
})
@@ -983,9 +1202,18 @@ func testPostStoreDelete(t *testing.T, ss store.Store) {
})
t.Run("thread with multiple replies, update thread last reply at", func(t *testing.T) {
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
// Create a root post
rootPost1, err := ss.Post().Save(&model.Post{
- ChannelId: model.NewId(),
+ ChannelId: channel.Id,
UserId: model.NewId(),
Message: NewTestId(),
})
@@ -1052,11 +1280,20 @@ func testPostStoreDelete(t *testing.T, ss store.Store) {
}
func testPostStorePermDelete1Level(t *testing.T, ss store.Store) {
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o1 := &model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel.Id
o1.UserId = model.NewId()
o1.Message = NewTestId()
- o1, err := ss.Post().Save(o1)
+ o1, err = ss.Post().Save(o1)
require.NoError(t, err)
o2 := &model.Post{}
@@ -1067,15 +1304,30 @@ func testPostStorePermDelete1Level(t *testing.T, ss store.Store) {
o2, err = ss.Post().Save(o2)
require.NoError(t, err)
+ channel2, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName2",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
o3 := &model.Post{}
- o3.ChannelId = model.NewId()
+ o3.ChannelId = channel2.Id
o3.UserId = model.NewId()
o3.Message = NewTestId()
o3, err = ss.Post().Save(o3)
require.NoError(t, err)
+ channel3, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName3",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o4 := &model.Post{}
- o4.ChannelId = model.NewId()
+ o4.ChannelId = channel3.Id
o4.RootId = o1.Id
o4.UserId = o2.UserId
o4.Message = NewTestId()
@@ -1144,11 +1396,20 @@ func testPostStorePermDelete1Level(t *testing.T, ss store.Store) {
}
func testPostStorePermDelete1Level2(t *testing.T, ss store.Store) {
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o1 := &model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel1.Id
o1.UserId = model.NewId()
o1.Message = NewTestId()
- o1, err := ss.Post().Save(o1)
+ o1, err = ss.Post().Save(o1)
require.NoError(t, err)
o2 := &model.Post{}
@@ -1159,8 +1420,16 @@ func testPostStorePermDelete1Level2(t *testing.T, ss store.Store) {
o2, err = ss.Post().Save(o2)
require.NoError(t, err)
+ channel2, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName2",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o3 := &model.Post{}
- o3.ChannelId = model.NewId()
+ o3.ChannelId = channel2.Id
o3.UserId = model.NewId()
o3.Message = NewTestId()
o3, err = ss.Post().Save(o3)
@@ -1180,11 +1449,20 @@ func testPostStorePermDelete1Level2(t *testing.T, ss store.Store) {
}
func testPostStoreGetWithChildren(t *testing.T, ss store.Store) {
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o1 := &model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel1.Id
o1.UserId = model.NewId()
o1.Message = NewTestId()
- o1, err := ss.Post().Save(o1)
+ o1, err = ss.Post().Save(o1)
require.NoError(t, err)
o2 := &model.Post{}
@@ -1226,11 +1504,20 @@ func testPostStoreGetWithChildren(t *testing.T, ss store.Store) {
}
func testPostStoreGetPostsWithDetails(t *testing.T, ss store.Store) {
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o1 := &model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel1.Id
o1.UserId = model.NewId()
o1.Message = NewTestId()
- o1, err := ss.Post().Save(o1)
+ o1, err = ss.Post().Save(o1)
require.NoError(t, err)
time.Sleep(2 * time.Millisecond)
@@ -1321,7 +1608,16 @@ func testPostStoreGetPostsWithDetails(t *testing.T, ss store.Store) {
func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) {
t.Run("without threads", func(t *testing.T) {
- channelId := model.NewId()
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channelId := channel1.Id
userId := model.NewId()
var posts []*model.Post
@@ -1416,7 +1712,16 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) {
})
})
t.Run("with threads", func(t *testing.T) {
- channelId := model.NewId()
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channelId := channel1.Id
userId := model.NewId()
// This creates a series of posts that looks like:
@@ -1514,7 +1819,16 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) {
})
})
t.Run("with threads (skipFetchThreads)", func(t *testing.T) {
- channelId := model.NewId()
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channelId := channel1.Id
userId := model.NewId()
// This creates a series of posts that looks like:
@@ -1620,7 +1934,16 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) {
})
})
t.Run("with threads (collapsedThreads)", func(t *testing.T) {
- channelId := model.NewId()
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channelId := channel1.Id
userId := model.NewId()
// This creates a series of posts that looks like:
@@ -1715,7 +2038,16 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) {
func testPostStoreGetPostsSince(t *testing.T, ss store.Store) {
t.Run("should return posts created after the given time", func(t *testing.T) {
- channelId := model.NewId()
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channelId := channel1.Id
userId := model.NewId()
post1, err := ss.Post().Save(&model.Post{
@@ -1788,7 +2120,16 @@ func testPostStoreGetPostsSince(t *testing.T, ss store.Store) {
})
t.Run("should return empty list when nothing has changed", func(t *testing.T) {
- channelId := model.NewId()
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channelId := channel1.Id
userId := model.NewId()
post1, err := ss.Post().Save(&model.Post{
@@ -1809,7 +2150,16 @@ func testPostStoreGetPostsSince(t *testing.T, ss store.Store) {
t.Run("should not cache a timestamp of 0 when nothing has changed", func(t *testing.T) {
ss.Post().ClearCaches()
- channelId := model.NewId()
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channelId := channel1.Id
userId := model.NewId()
post1, err := ss.Post().Save(&model.Post{
@@ -1837,7 +2187,16 @@ func testPostStoreGetPostsSince(t *testing.T, ss store.Store) {
}
func testPostStoreGetPosts(t *testing.T, ss store.Store) {
- channelId := model.NewId()
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channelId := channel1.Id
userId := model.NewId()
post1, err := ss.Post().Save(&model.Post{
@@ -2016,13 +2375,22 @@ func testPostStoreGetPosts(t *testing.T, ss store.Store) {
}
func testPostStoreGetPostBeforeAfter(t *testing.T, ss store.Store) {
- channelId := model.NewId()
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channelId := channel1.Id
o0 := &model.Post{}
o0.ChannelId = channelId
o0.UserId = model.NewId()
o0.Message = NewTestId()
- _, err := ss.Post().Save(o0)
+ _, err = ss.Post().Save(o0)
require.NoError(t, err)
time.Sleep(2 * time.Millisecond)
@@ -2054,8 +2422,16 @@ func testPostStoreGetPostBeforeAfter(t *testing.T, ss store.Store) {
require.NoError(t, err)
time.Sleep(2 * time.Millisecond)
+ channel2, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName2",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
otherChannelPost := &model.Post{}
- otherChannelPost.ChannelId = model.NewId()
+ otherChannelPost.ChannelId = channel2.Id
otherChannelPost.UserId = model.NewId()
otherChannelPost.Message = NewTestId()
_, err = ss.Post().Save(otherChannelPost)
@@ -2366,8 +2742,17 @@ func testPostStoreGetFlaggedPostsForTeam(t *testing.T, ss store.Store, s SqlStor
_, err = ss.Channel().SaveMember(m0)
require.NoError(t, err)
+ teamId := model.NewId()
+ channel2, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName2",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o4 := &model.Post{}
- o4.ChannelId = model.NewId()
+ o4.ChannelId = channel2.Id
o4.UserId = model.NewId()
o4.Message = NewTestId()
o4, err = ss.Post().Save(o4)
@@ -2401,8 +2786,16 @@ func testPostStoreGetFlaggedPostsForTeam(t *testing.T, ss store.Store, s SqlStor
time.Sleep(2 * time.Millisecond)
// Post on channel where user is not a member
+ channel3, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName3",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o6 := &model.Post{}
- o6.ChannelId = model.NewId()
+ o6.ChannelId = channel3.Id
o6.UserId = m2.UserId
o6.Message = NewTestId()
o6, err = ss.Post().Save(o6)
@@ -2562,8 +2955,17 @@ func testPostStoreGetFlaggedPosts(t *testing.T, ss store.Store) {
time.Sleep(2 * time.Millisecond)
// Post on channel where user is not a member
+ teamId := model.NewId()
+ channel2, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName2",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o4 := &model.Post{}
- o4.ChannelId = model.NewId()
+ o4.ChannelId = channel2.Id
o4.UserId = model.NewId()
o4.Message = NewTestId()
o4, err = ss.Post().Save(o4)
@@ -2692,8 +3094,17 @@ func testPostStoreGetFlaggedPostsForChannel(t *testing.T, ss store.Store) {
time.Sleep(2 * time.Millisecond)
// deleted post
+ teamId := model.NewId()
+ channel3, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName3",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o3 := &model.Post{}
- o3.ChannelId = model.NewId()
+ o3.ChannelId = channel3.Id
o3.UserId = o1.ChannelId
o3.Message = NewTestId()
o3.DeleteAt = 1
@@ -2710,8 +3121,16 @@ func testPostStoreGetFlaggedPostsForChannel(t *testing.T, ss store.Store) {
time.Sleep(2 * time.Millisecond)
// Post on channel where user is not a member
+ channel4, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName4",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o5 := &model.Post{}
- o5.ChannelId = model.NewId()
+ o5.ChannelId = channel4.Id
o5.UserId = model.NewId()
o5.Message = NewTestId()
o5, err = ss.Post().Save(o5)
@@ -2792,13 +3211,22 @@ func testPostStoreGetFlaggedPostsForChannel(t *testing.T, ss store.Store) {
}
func testPostStoreGetLastPostRowCreateAt(t *testing.T, ss store.Store) {
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
createTime1 := model.GetMillis() + 1
o0 := &model.Post{}
- o0.ChannelId = model.NewId()
+ o0.ChannelId = channel1.Id
o0.UserId = model.NewId()
o0.Message = NewTestId()
o0.CreateAt = createTime1
- o0, err := ss.Post().Save(o0)
+ o0, err = ss.Post().Save(o0)
require.NoError(t, err)
createTime2 := model.GetMillis() + 2
@@ -2817,14 +3245,23 @@ func testPostStoreGetLastPostRowCreateAt(t *testing.T, ss store.Store) {
}
func testPostStoreGetPostsCreatedAt(t *testing.T, ss store.Store) {
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
createTime := model.GetMillis() + 1
o0 := &model.Post{}
- o0.ChannelId = model.NewId()
+ o0.ChannelId = channel1.Id
o0.UserId = model.NewId()
o0.Message = NewTestId()
o0.CreateAt = createTime
- o0, err := ss.Post().Save(o0)
+ o0, err = ss.Post().Save(o0)
require.NoError(t, err)
o1 := &model.Post{}
@@ -2844,8 +3281,16 @@ func testPostStoreGetPostsCreatedAt(t *testing.T, ss store.Store) {
_, err = ss.Post().Save(o2)
require.NoError(t, err)
+ channel2, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName2",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o3 := &model.Post{}
- o3.ChannelId = model.NewId()
+ o3.ChannelId = channel2.Id
o3.UserId = model.NewId()
o3.Message = NewTestId()
o3.CreateAt = createTime
@@ -2857,11 +3302,20 @@ func testPostStoreGetPostsCreatedAt(t *testing.T, ss store.Store) {
}
func testPostStoreOverwriteMultiple(t *testing.T, ss store.Store) {
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o1 := &model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel1.Id
o1.UserId = model.NewId()
o1.Message = NewTestId()
- o1, err := ss.Post().Save(o1)
+ o1, err = ss.Post().Save(o1)
require.NoError(t, err)
o2 := &model.Post{}
@@ -2879,16 +3333,31 @@ func testPostStoreOverwriteMultiple(t *testing.T, ss store.Store) {
o3, err = ss.Post().Save(o3)
require.NoError(t, err)
+ channel2, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName2",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o4, err := ss.Post().Save(&model.Post{
- ChannelId: model.NewId(),
+ ChannelId: channel2.Id,
UserId: model.NewId(),
Message: model.NewId(),
Filenames: []string{"test"},
})
require.NoError(t, err)
+ channel3, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName3",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
o5, err := ss.Post().Save(&model.Post{
- ChannelId: model.NewId(),
+ ChannelId: channel3.Id,
UserId: model.NewId(),
Message: model.NewId(),
Filenames: []string{"test2", "test3"},
@@ -2983,11 +3452,20 @@ func testPostStoreOverwriteMultiple(t *testing.T, ss store.Store) {
}
func testPostStoreOverwrite(t *testing.T, ss store.Store) {
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o1 := &model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel1.Id
o1.UserId = model.NewId()
o1.Message = NewTestId()
- o1, err := ss.Post().Save(o1)
+ o1, err = ss.Post().Save(o1)
require.NoError(t, err)
o2 := &model.Post{}
@@ -3005,8 +3483,15 @@ func testPostStoreOverwrite(t *testing.T, ss store.Store) {
o3, err = ss.Post().Save(o3)
require.NoError(t, err)
+ channel2, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName2",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
o4, err := ss.Post().Save(&model.Post{
- ChannelId: model.NewId(),
+ ChannelId: channel2.Id,
UserId: model.NewId(),
Message: model.NewId(),
Filenames: []string{"test"},
@@ -3084,11 +3569,20 @@ func testPostStoreOverwrite(t *testing.T, ss store.Store) {
}
func testPostStoreGetPostsByIds(t *testing.T, ss store.Store) {
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o1 := &model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel1.Id
o1.UserId = model.NewId()
o1.Message = NewTestId()
- o1, err := ss.Post().Save(o1)
+ o1, err = ss.Post().Save(o1)
require.NoError(t, err)
o2 := &model.Post{}
@@ -3402,12 +3896,21 @@ func testPostStorePermanentDeleteBatch(t *testing.T, ss store.Store) {
}
func testPostStoreGetOldest(t *testing.T, ss store.Store) {
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o0 := &model.Post{}
- o0.ChannelId = model.NewId()
+ o0.ChannelId = channel1.Id
o0.UserId = model.NewId()
o0.Message = NewTestId()
o0.CreateAt = 3
- o0, err := ss.Post().Save(o0)
+ o0, err = ss.Post().Save(o0)
require.NoError(t, err)
o1 := &model.Post{}
@@ -3751,10 +4254,19 @@ func testPostStoreGetDirectPostParentsForExportAfterBatched(t *testing.T, ss sto
func testHasAutoResponsePostByUserSince(t *testing.T, ss store.Store) {
t.Run("should return posts created after the given time", func(t *testing.T) {
- channelId := model.NewId()
+ teamId := model.NewId()
+ channel1, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
+ channelId := channel1.Id
userId := model.NewId()
- _, err := ss.Post().Save(&model.Post{
+ _, err = ss.Post().Save(&model.Post{
ChannelId: channelId,
UserId: userId,
Message: "message",
diff --git a/store/storetest/thread_store.go b/store/storetest/thread_store.go
index e438ed8516..885a48c1a4 100644
--- a/store/storetest/thread_store.go
+++ b/store/storetest/thread_store.go
@@ -106,8 +106,17 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
require.Equal(t, int64(2), thread.ReplyCount)
require.ElementsMatch(t, model.StringArray{newPosts[0].UserId, newPosts[1].UserId}, thread.Participants)
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName1",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o5 := model.Post{}
- o5.ChannelId = model.NewId()
+ o5.ChannelId = channel.Id
o5.UserId = model.NewId()
o5.RootId = newPosts[0].Id
o5.Message = NewTestId()
@@ -141,9 +150,18 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
})
t.Run("Update reply should update the UpdateAt of the thread", func(t *testing.T) {
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
rootPost := model.Post{}
rootPost.RootId = model.NewId()
- rootPost.ChannelId = model.NewId()
+ rootPost.ChannelId = channel.Id
rootPost.UserId = model.NewId()
rootPost.Message = NewTestId()
@@ -188,8 +206,17 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
})
t.Run("Deleting reply should update the thread", func(t *testing.T) {
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
o1 := model.Post{}
- o1.ChannelId = model.NewId()
+ o1.ChannelId = channel.Id
o1.UserId = model.NewId()
o1.Message = NewTestId()
rootPost, err := ss.Post().Save(&o1)
@@ -240,8 +267,17 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
})
t.Run("Deleting root post should delete the thread", func(t *testing.T) {
+ teamId := model.NewId()
+ channel, err := ss.Channel().Save(&model.Channel{
+ TeamId: teamId,
+ DisplayName: "DisplayName",
+ Name: "channel" + model.NewId(),
+ Type: model.ChannelTypeOpen,
+ }, -1)
+ require.NoError(t, err)
+
rootPost := model.Post{}
- rootPost.ChannelId = model.NewId()
+ rootPost.ChannelId = channel.Id
rootPost.UserId = model.NewId()
rootPost.Message = NewTestId()
diff --git a/store/timerlayer/timerlayer.go b/store/timerlayer/timerlayer.go
index 0558fa66c1..cec6251fc7 100644
--- a/store/timerlayer/timerlayer.go
+++ b/store/timerlayer/timerlayer.go
@@ -3781,10 +3781,10 @@ func (s *TimerLayerGroupStore) GetGroupSyncable(groupID string, syncableID strin
return result, err
}
-func (s *TimerLayerGroupStore) GetGroups(page int, perPage int, opts model.GroupSearchOpts) ([]*model.Group, error) {
+func (s *TimerLayerGroupStore) GetGroups(page int, perPage int, opts model.GroupSearchOpts, viewRestrictions *model.ViewUsersRestrictions) ([]*model.Group, error) {
start := time.Now()
- result, err := s.GroupStore.GetGroups(page, perPage, opts)
+ result, err := s.GroupStore.GetGroups(page, perPage, opts, viewRestrictions)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
@@ -3877,6 +3877,22 @@ func (s *TimerLayerGroupStore) GetMemberCount(groupID string) (int64, error) {
return result, err
}
+func (s *TimerLayerGroupStore) GetMemberCountWithRestrictions(groupID string, viewRestrictions *model.ViewUsersRestrictions) (int64, error) {
+ start := time.Now()
+
+ result, err := s.GroupStore.GetMemberCountWithRestrictions(groupID, viewRestrictions)
+
+ elapsed := float64(time.Since(start)) / float64(time.Second)
+ if s.Root.Metrics != nil {
+ success := "false"
+ if err == nil {
+ success = "true"
+ }
+ s.Root.Metrics.ObserveStoreMethodDuration("GroupStore.GetMemberCountWithRestrictions", success, elapsed)
+ }
+ return result, err
+}
+
func (s *TimerLayerGroupStore) GetMemberUsers(groupID string) ([]*model.User, error) {
start := time.Now()
@@ -3925,10 +3941,10 @@ func (s *TimerLayerGroupStore) GetMemberUsersNotInChannel(groupID string, channe
return result, err
}
-func (s *TimerLayerGroupStore) GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, error) {
+func (s *TimerLayerGroupStore) GetMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) {
start := time.Now()
- result, err := s.GroupStore.GetMemberUsersPage(groupID, page, perPage)
+ result, err := s.GroupStore.GetMemberUsersPage(groupID, page, perPage, viewRestrictions)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
@@ -3941,10 +3957,10 @@ func (s *TimerLayerGroupStore) GetMemberUsersPage(groupID string, page int, perP
return result, err
}
-func (s *TimerLayerGroupStore) GetNonMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, error) {
+func (s *TimerLayerGroupStore) GetNonMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) {
start := time.Now()
- result, err := s.GroupStore.GetNonMemberUsersPage(groupID, page, perPage)
+ result, err := s.GroupStore.GetNonMemberUsersPage(groupID, page, perPage, viewRestrictions)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
diff --git a/templates/cloud_14_day_arrears.html b/templates/cloud_14_day_arrears.html
index 395a78824d..d5115d151e 100644
--- a/templates/cloud_14_day_arrears.html
+++ b/templates/cloud_14_day_arrears.html
@@ -103,7 +103,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -350,7 +350,7 @@
-
+
diff --git a/templates/cloud_30_day_arrears.html b/templates/cloud_30_day_arrears.html
index aae98b9df4..19290dd87f 100644
--- a/templates/cloud_30_day_arrears.html
+++ b/templates/cloud_30_day_arrears.html
@@ -103,7 +103,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -350,7 +350,7 @@
-
+
@@ -416,15 +416,13 @@
-
-
+
{{ range .Props.BulletListItems}}
- {{.}}
{{end}}
-
-
+
|
diff --git a/templates/cloud_45_day_arrears.html b/templates/cloud_45_day_arrears.html
index 0225d5b2e6..9c4af6731c 100644
--- a/templates/cloud_45_day_arrears.html
+++ b/templates/cloud_45_day_arrears.html
@@ -103,7 +103,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -350,7 +350,7 @@
-
+
diff --git a/templates/cloud_7_day_arrears.html b/templates/cloud_7_day_arrears.html
index b59e370f29..712b29fa37 100644
--- a/templates/cloud_7_day_arrears.html
+++ b/templates/cloud_7_day_arrears.html
@@ -103,7 +103,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -350,7 +350,7 @@
-
+
diff --git a/templates/cloud_90_day_arrears.html b/templates/cloud_90_day_arrears.html
index da1137dbb9..f63b2b2a07 100644
--- a/templates/cloud_90_day_arrears.html
+++ b/templates/cloud_90_day_arrears.html
@@ -103,7 +103,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -350,7 +350,7 @@
-
+
diff --git a/templates/cloud_upgrade_confirmation.html b/templates/cloud_upgrade_confirmation.html
index 9b30132f49..27f4fca15c 100644
--- a/templates/cloud_upgrade_confirmation.html
+++ b/templates/cloud_upgrade_confirmation.html
@@ -93,7 +93,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -340,7 +340,7 @@
-
+
diff --git a/templates/cloud_welcome_email.html b/templates/cloud_welcome_email.html
index 1ef02cd670..d2e8235cc5 100644
--- a/templates/cloud_welcome_email.html
+++ b/templates/cloud_welcome_email.html
@@ -14,15 +14,15 @@
+ style="width: 724px; padding: 32px; background: #FFFFFF; color: #3D3C40;">
-
+
|
diff --git a/templates/inactivity_body.html b/templates/inactivity_body.html
index 1c5507cd14..36bb7795cb 100644
--- a/templates/inactivity_body.html
+++ b/templates/inactivity_body.html
@@ -93,7 +93,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -340,7 +340,7 @@
-
+
diff --git a/templates/invite_body.html b/templates/invite_body.html
index 017afb677e..8a3b232b65 100644
--- a/templates/invite_body.html
+++ b/templates/invite_body.html
@@ -113,7 +113,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -360,7 +360,7 @@
-
+
diff --git a/templates/license_up_for_renewal.html b/templates/license_up_for_renewal.html
index 13780c89c5..d05cc8d8be 100644
--- a/templates/license_up_for_renewal.html
+++ b/templates/license_up_for_renewal.html
@@ -93,7 +93,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -340,7 +340,7 @@
-
+
diff --git a/templates/messages_notification.html b/templates/messages_notification.html
index 7b47478cfa..ba2aaee252 100644
--- a/templates/messages_notification.html
+++ b/templates/messages_notification.html
@@ -113,7 +113,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -513,7 +513,7 @@
-
+
diff --git a/templates/partials/style.css b/templates/partials/style.css
index f91787b5d9..3c72f05ea0 100644
--- a/templates/partials/style.css
+++ b/templates/partials/style.css
@@ -1,7 +1,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a{
diff --git a/templates/password_change_body.html b/templates/password_change_body.html
index 550b40f665..663b608e59 100644
--- a/templates/password_change_body.html
+++ b/templates/password_change_body.html
@@ -10,7 +10,7 @@
-
+
|
diff --git a/templates/payment_failed_body.html b/templates/payment_failed_body.html
index 0d8daf6b7c..81060f9e37 100644
--- a/templates/payment_failed_body.html
+++ b/templates/payment_failed_body.html
@@ -103,7 +103,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -350,7 +350,7 @@
-
+
diff --git a/templates/reset_body.html b/templates/reset_body.html
index e5f62d4dec..ab2b11be32 100644
--- a/templates/reset_body.html
+++ b/templates/reset_body.html
@@ -93,7 +93,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -339,8 +339,8 @@
-
-
+
+
diff --git a/templates/reset_body.mjml b/templates/reset_body.mjml
index 06bd55c508..3d2e3c8e66 100644
--- a/templates/reset_body.mjml
+++ b/templates/reset_body.mjml
@@ -2,7 +2,7 @@
-
+
diff --git a/templates/verify_body.html b/templates/verify_body.html
index 5c8cc5550f..a94df5f287 100644
--- a/templates/verify_body.html
+++ b/templates/verify_body.html
@@ -93,7 +93,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -340,7 +340,7 @@
-
+
diff --git a/templates/welcome_body.html b/templates/welcome_body.html
index 28ea7d65f5..5e85097a31 100644
--- a/templates/welcome_body.html
+++ b/templates/welcome_body.html
@@ -93,7 +93,7 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700);
.emailBody {
- background: #F3F3F3 !important;
+ background-color: #F3F3F3
}
.emailBody a {
@@ -339,8 +339,8 @@
-
-
+
+
diff --git a/templates/welcome_body.mjml b/templates/welcome_body.mjml
index 960689d6ef..f27ad1dc3c 100644
--- a/templates/welcome_body.mjml
+++ b/templates/welcome_body.mjml
@@ -2,7 +2,7 @@
-
+
diff --git a/web/handlers.go b/web/handlers.go
index 9792460aff..65dda02cf0 100644
--- a/web/handlers.go
+++ b/web/handlers.go
@@ -8,6 +8,7 @@ import (
"context"
"fmt"
"net/http"
+ "os"
"reflect"
"runtime"
"strconv"
@@ -133,8 +134,13 @@ func generateDevCSP(c Context) string {
// Add flags for Webpack dev servers used by other products during development
if model.BuildNumber == "dev" {
- // Focalboard runs on http://localhost:9006
- devCSP = append(devCSP, "http://localhost:9006")
+ boardsURL := os.Getenv("MM_BOARDS_DEV_SERVER_URL")
+ if boardsURL == "" {
+ // Focalboard runs on http://localhost:9006 by default
+ boardsURL = "http://localhost:9006"
+ }
+
+ devCSP = append(devCSP, boardsURL)
}
if len(devCSP) == 0 {
| |