mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
Updating a Group or a GroupMember only changed the UpdateAt or CreateAt times respectively. And it threw an error if number of rows changed was not 1. However, it can happen that 2 calls happen so fast that 1 milisecond does not pass, or even 2 concurrent calls at the same time might happen so that model.GetMillis return the same timestamp. In those cases, the number of rows updated can be 0. The error should just check if the number is greater than 1, instead of not equal to 1. This makes it more robust and correct. Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
1515 lines
56 KiB
Go
1515 lines
56 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package sqlstore
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
sq "github.com/Masterminds/squirrel"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/mattermost/mattermost-server/v5/model"
|
|
"github.com/mattermost/mattermost-server/v5/store"
|
|
)
|
|
|
|
type selectType int
|
|
|
|
const (
|
|
selectGroups selectType = iota
|
|
selectCountGroups
|
|
)
|
|
|
|
type groupTeam struct {
|
|
model.GroupSyncable
|
|
TeamId string `db:"TeamId"`
|
|
}
|
|
|
|
type groupChannel struct {
|
|
model.GroupSyncable
|
|
ChannelId string `db:"ChannelId"`
|
|
}
|
|
|
|
type groupTeamJoin struct {
|
|
groupTeam
|
|
TeamDisplayName string `db:"TeamDisplayName"`
|
|
TeamType string `db:"TeamType"`
|
|
}
|
|
|
|
type groupChannelJoin struct {
|
|
groupChannel
|
|
ChannelDisplayName string `db:"ChannelDisplayName"`
|
|
TeamDisplayName string `db:"TeamDisplayName"`
|
|
TeamType string `db:"TeamType"`
|
|
ChannelType string `db:"ChannelType"`
|
|
TeamID string `db:"TeamId"`
|
|
}
|
|
|
|
type SqlGroupStore struct {
|
|
SqlStore
|
|
}
|
|
|
|
func newSqlGroupStore(sqlStore SqlStore) store.GroupStore {
|
|
s := &SqlGroupStore{SqlStore: sqlStore}
|
|
for _, db := range sqlStore.GetAllConns() {
|
|
groups := db.AddTableWithName(model.Group{}, "UserGroups").SetKeys(false, "Id")
|
|
groups.ColMap("Id").SetMaxSize(26)
|
|
groups.ColMap("Name").SetMaxSize(model.GroupNameMaxLength).SetUnique(true)
|
|
groups.ColMap("DisplayName").SetMaxSize(model.GroupDisplayNameMaxLength)
|
|
groups.ColMap("Description").SetMaxSize(model.GroupDescriptionMaxLength)
|
|
groups.ColMap("Source").SetMaxSize(model.GroupSourceMaxLength)
|
|
groups.ColMap("RemoteId").SetMaxSize(model.GroupRemoteIDMaxLength)
|
|
groups.SetUniqueTogether("Source", "RemoteId")
|
|
|
|
groupMembers := db.AddTableWithName(model.GroupMember{}, "GroupMembers").SetKeys(false, "GroupId", "UserId")
|
|
groupMembers.ColMap("GroupId").SetMaxSize(26)
|
|
groupMembers.ColMap("UserId").SetMaxSize(26)
|
|
|
|
groupTeams := db.AddTableWithName(groupTeam{}, "GroupTeams").SetKeys(false, "GroupId", "TeamId")
|
|
groupTeams.ColMap("GroupId").SetMaxSize(26)
|
|
groupTeams.ColMap("TeamId").SetMaxSize(26)
|
|
|
|
groupChannels := db.AddTableWithName(groupChannel{}, "GroupChannels").SetKeys(false, "GroupId", "ChannelId")
|
|
groupChannels.ColMap("GroupId").SetMaxSize(26)
|
|
groupChannels.ColMap("ChannelId").SetMaxSize(26)
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (s *SqlGroupStore) createIndexesIfNotExists() {
|
|
s.CreateIndexIfNotExists("idx_groupmembers_create_at", "GroupMembers", "CreateAt")
|
|
s.CreateIndexIfNotExists("idx_usergroups_remote_id", "UserGroups", "RemoteId")
|
|
s.CreateIndexIfNotExists("idx_usergroups_delete_at", "UserGroups", "DeleteAt")
|
|
s.CreateIndexIfNotExists("idx_groupteams_teamid", "GroupTeams", "TeamId")
|
|
s.CreateIndexIfNotExists("idx_groupchannels_channelid", "GroupChannels", "ChannelId")
|
|
s.CreateColumnIfNotExistsNoDefault("Channels", "GroupConstrained", "tinyint(1)", "boolean")
|
|
s.CreateColumnIfNotExistsNoDefault("Teams", "GroupConstrained", "tinyint(1)", "boolean")
|
|
s.CreateIndexIfNotExists("idx_groupteams_schemeadmin", "GroupTeams", "SchemeAdmin")
|
|
s.CreateIndexIfNotExists("idx_groupchannels_schemeadmin", "GroupChannels", "SchemeAdmin")
|
|
}
|
|
|
|
func (s *SqlGroupStore) Create(group *model.Group) (*model.Group, *model.AppError) {
|
|
if len(group.Id) != 0 {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupCreate", "model.group.id.app_error", nil, "", http.StatusBadRequest)
|
|
}
|
|
|
|
if err := group.IsValidForCreate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
group.Id = model.NewId()
|
|
group.CreateAt = model.GetMillis()
|
|
group.UpdateAt = group.CreateAt
|
|
|
|
if err := s.GetMaster().Insert(group); err != nil {
|
|
if IsUniqueConstraintError(err, []string{"Name", "groups_name_key"}) {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupCreate", "store.sql_group.unique_constraint", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
return nil, model.NewAppError("SqlGroupStore.GroupCreate", "store.insert_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return group, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) Get(groupId string) (*model.Group, *model.AppError) {
|
|
var group *model.Group
|
|
if err := s.GetReplica().SelectOne(&group, "SELECT * from UserGroups WHERE Id = :Id", map[string]interface{}{"Id": groupId}); err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupGet", "store.sql_group.no_rows", nil, err.Error(), http.StatusNotFound)
|
|
}
|
|
return nil, model.NewAppError("SqlGroupStore.GroupGet", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return group, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetByName(name string, opts model.GroupSearchOpts) (*model.Group, *model.AppError) {
|
|
var group *model.Group
|
|
query := s.getQueryBuilder().Select("*").From("UserGroups").Where(sq.Eq{"Name": name})
|
|
if opts.FilterAllowReference {
|
|
query = query.Where("AllowReference = true")
|
|
}
|
|
|
|
queryString, args, err := query.ToSql()
|
|
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetByName", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
if err := s.GetReplica().SelectOne(&group, queryString, args...); err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupGetByName", "store.sql_group.no_rows", nil, err.Error(), http.StatusNotFound)
|
|
}
|
|
return nil, model.NewAppError("SqlGroupStore.GroupGetByName", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return group, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetByIDs(groupIDs []string) ([]*model.Group, *model.AppError) {
|
|
var groups []*model.Group
|
|
query := s.getQueryBuilder().Select("*").From("UserGroups").Where(sq.Eq{"Id": groupIDs})
|
|
queryString, args, err := query.ToSql()
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetByIDs", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
if _, err := s.GetReplica().Select(&groups, queryString, args...); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetByIDs", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
return groups, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetByRemoteID(remoteID string, groupSource model.GroupSource) (*model.Group, *model.AppError) {
|
|
var group *model.Group
|
|
if err := s.GetReplica().SelectOne(&group, "SELECT * from UserGroups WHERE RemoteId = :RemoteId AND Source = :Source", map[string]interface{}{"RemoteId": remoteID, "Source": groupSource}); err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupGetByRemoteID", "store.sql_group.no_rows", nil, err.Error(), http.StatusNotFound)
|
|
}
|
|
return nil, model.NewAppError("SqlGroupStore.GroupGetByRemoteID", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return group, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetAllBySource(groupSource model.GroupSource) ([]*model.Group, *model.AppError) {
|
|
var groups []*model.Group
|
|
|
|
if _, err := s.GetReplica().Select(&groups, "SELECT * from UserGroups WHERE DeleteAt = 0 AND Source = :Source", map[string]interface{}{"Source": groupSource}); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupGetAllBySource", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groups, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetByUser(userId string) ([]*model.Group, *model.AppError) {
|
|
var groups []*model.Group
|
|
|
|
query := `
|
|
SELECT
|
|
UserGroups.*
|
|
FROM
|
|
GroupMembers
|
|
JOIN UserGroups ON UserGroups.Id = GroupMembers.GroupId
|
|
WHERE
|
|
GroupMembers.DeleteAt = 0
|
|
AND UserId = :UserId`
|
|
|
|
if _, err := s.GetReplica().Select(&groups, query, map[string]interface{}{"UserId": userId}); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetByUser", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groups, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) Update(group *model.Group) (*model.Group, *model.AppError) {
|
|
var retrievedGroup *model.Group
|
|
if err := s.GetReplica().SelectOne(&retrievedGroup, "SELECT * FROM UserGroups WHERE Id = :Id", map[string]interface{}{"Id": group.Id}); err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupUpdate", "store.sql_group.no_rows", nil, "id="+group.Id+","+err.Error(), http.StatusNotFound)
|
|
}
|
|
return nil, model.NewAppError("SqlGroupStore.GroupUpdate", "store.select_error", nil, "id="+group.Id+","+err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
// If updating DeleteAt it can only be to 0
|
|
if group.DeleteAt != retrievedGroup.DeleteAt && group.DeleteAt != 0 {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupUpdate", "model.group.delete_at.app_error", nil, "", http.StatusInternalServerError)
|
|
}
|
|
|
|
// Reset these properties, don't update them based on input
|
|
group.CreateAt = retrievedGroup.CreateAt
|
|
group.UpdateAt = model.GetMillis()
|
|
|
|
if err := group.IsValidForUpdate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rowsChanged, err := s.GetMaster().Update(group)
|
|
if err != nil {
|
|
if IsUniqueConstraintError(err, []string{"Name", "groups_name_key"}) {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupUpdate", "store.sql_group.unique_constraint", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
return nil, model.NewAppError("SqlGroupStore.GroupUpdate", "store.update_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
if rowsChanged > 1 {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupUpdate", "store.sql_group.more_than_one_row_changed", nil, "", http.StatusInternalServerError)
|
|
}
|
|
|
|
return group, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) Delete(groupID string) (*model.Group, *model.AppError) {
|
|
var group *model.Group
|
|
if err := s.GetReplica().SelectOne(&group, "SELECT * from UserGroups WHERE Id = :Id AND DeleteAt = 0", map[string]interface{}{"Id": groupID}); err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupDelete", "store.sql_group.no_rows", nil, "Id="+groupID+", "+err.Error(), http.StatusNotFound)
|
|
}
|
|
return nil, model.NewAppError("SqlGroupStore.GroupDelete", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
time := model.GetMillis()
|
|
group.DeleteAt = time
|
|
group.UpdateAt = time
|
|
|
|
if _, err := s.GetMaster().Update(group); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupDelete", "store.update_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return group, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetMemberUsers(groupID string) ([]*model.User, *model.AppError) {
|
|
var 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 = :GroupId`
|
|
|
|
if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID}); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetMemberUsers", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groupMembers, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, *model.AppError) {
|
|
var 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 = :GroupId
|
|
ORDER BY
|
|
GroupMembers.CreateAt DESC
|
|
LIMIT
|
|
:Limit
|
|
OFFSET
|
|
:Offset`
|
|
|
|
if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID, "Limit": perPage, "Offset": page * perPage}); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupGetMemberUsersPage", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groupMembers, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetMemberCount(groupID string) (int64, *model.AppError) {
|
|
query := `
|
|
SELECT
|
|
count(*)
|
|
FROM
|
|
GroupMembers
|
|
JOIN Users ON Users.Id = GroupMembers.UserId
|
|
WHERE
|
|
GroupMembers.GroupId = :GroupId
|
|
AND Users.DeleteAt = 0`
|
|
|
|
count, err := s.GetReplica().SelectInt(query, map[string]interface{}{"GroupId": groupID})
|
|
if err != nil {
|
|
return int64(0), model.NewAppError("SqlGroupStore.GroupGetMemberUsersPage", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetMemberUsersInTeam(groupID string, teamID string) ([]*model.User, *model.AppError) {
|
|
var groupMembers []*model.User
|
|
|
|
query := `
|
|
SELECT
|
|
Users.*
|
|
FROM
|
|
GroupMembers
|
|
JOIN Users ON Users.Id = GroupMembers.UserId
|
|
WHERE
|
|
GroupId = :GroupId
|
|
AND GroupMembers.UserId IN (
|
|
SELECT TeamMembers.UserId
|
|
FROM TeamMembers
|
|
JOIN Teams ON Teams.Id = :TeamId
|
|
WHERE TeamMembers.TeamId = Teams.Id
|
|
AND TeamMembers.DeleteAt = 0
|
|
)
|
|
AND GroupMembers.DeleteAt = 0
|
|
AND Users.DeleteAt = 0
|
|
`
|
|
|
|
if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID, "TeamId": teamID}); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetMemberUsersInTeam", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groupMembers, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetMemberUsersNotInChannel(groupID string, channelID string) ([]*model.User, *model.AppError) {
|
|
var groupMembers []*model.User
|
|
|
|
query := `
|
|
SELECT
|
|
Users.*
|
|
FROM
|
|
GroupMembers
|
|
JOIN Users ON Users.Id = GroupMembers.UserId
|
|
WHERE
|
|
GroupId = :GroupId
|
|
AND GroupMembers.UserId NOT IN (
|
|
SELECT ChannelMembers.UserId
|
|
FROM ChannelMembers
|
|
WHERE ChannelMembers.ChannelId = :ChannelId
|
|
)
|
|
AND GroupMembers.UserId IN (
|
|
SELECT TeamMembers.UserId
|
|
FROM TeamMembers
|
|
JOIN Channels ON Channels.Id = :ChannelId
|
|
JOIN Teams ON Teams.Id = Channels.TeamId
|
|
WHERE TeamMembers.TeamId = Teams.Id
|
|
AND TeamMembers.DeleteAt = 0
|
|
)
|
|
AND GroupMembers.DeleteAt = 0
|
|
AND Users.DeleteAt = 0
|
|
`
|
|
|
|
if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID, "ChannelId": channelID}); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetMemberUsersNotInChannel", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groupMembers, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) UpsertMember(groupID string, userID string) (*model.GroupMember, *model.AppError) {
|
|
member := &model.GroupMember{
|
|
GroupId: groupID,
|
|
UserId: userID,
|
|
CreateAt: model.GetMillis(),
|
|
}
|
|
|
|
if err := member.IsValid(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var retrievedGroup *model.Group
|
|
if err := s.GetReplica().SelectOne(&retrievedGroup, "SELECT * FROM UserGroups WHERE Id = :Id", map[string]interface{}{"Id": groupID}); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupCreateOrRestoreMember", "store.insert_error", nil, "group_id="+member.GroupId+"user_id="+member.UserId+","+err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var retrievedMember *model.GroupMember
|
|
if err := s.GetReplica().SelectOne(&retrievedMember, "SELECT * FROM GroupMembers WHERE GroupId = :GroupId AND UserId = :UserId", map[string]interface{}{"GroupId": member.GroupId, "UserId": member.UserId}); err != nil {
|
|
if err != sql.ErrNoRows {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupCreateOrRestoreMember", "store.select_error", nil, "group_id="+member.GroupId+"user_id="+member.UserId+","+err.Error(), http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
if retrievedMember == nil {
|
|
if err := s.GetMaster().Insert(member); err != nil {
|
|
if IsUniqueConstraintError(err, []string{"GroupId", "UserId", "groupmembers_pkey", "PRIMARY"}) {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupCreateOrRestoreMember", "store.sql_group.uniqueness_error", nil, "group_id="+member.GroupId+", user_id="+member.UserId+", "+err.Error(), http.StatusBadRequest)
|
|
}
|
|
return nil, model.NewAppError("SqlGroupStore.GroupCreateOrRestoreMember", "store.insert_error", nil, "group_id="+member.GroupId+", user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError)
|
|
}
|
|
} else {
|
|
member.DeleteAt = 0
|
|
var rowsChanged int64
|
|
var err error
|
|
if rowsChanged, err = s.GetMaster().Update(member); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupCreateOrRestoreMember", "store.update_error", nil, "group_id="+member.GroupId+", user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError)
|
|
}
|
|
if rowsChanged > 1 {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupCreateOrRestoreMember", "store.sql_group.more_than_one_row_changed", nil, "", http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
return member, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) DeleteMember(groupID string, userID string) (*model.GroupMember, *model.AppError) {
|
|
var retrievedMember *model.GroupMember
|
|
if err := s.GetReplica().SelectOne(&retrievedMember, "SELECT * FROM GroupMembers WHERE GroupId = :GroupId AND UserId = :UserId AND DeleteAt = 0", map[string]interface{}{"GroupId": groupID, "UserId": userID}); err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupDeleteMember", "store.sql_group.no_rows", nil, "group_id="+groupID+"user_id="+userID+","+err.Error(), http.StatusNotFound)
|
|
}
|
|
return nil, model.NewAppError("SqlGroupStore.GroupDeleteMember", "store.select_error", nil, "group_id="+groupID+"user_id="+userID+","+err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
retrievedMember.DeleteAt = model.GetMillis()
|
|
|
|
if _, err := s.GetMaster().Update(retrievedMember); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupDeleteMember", "store.update_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return retrievedMember, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) PermanentDeleteMembersByUser(userId string) *model.AppError {
|
|
if _, err := s.GetMaster().Exec("DELETE FROM GroupMembers WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}); err != nil {
|
|
return model.NewAppError("SqlGroupStore.GroupPermanentDeleteMembersByUser", "store.sql_group.permanent_delete_members_by_user.app_error", map[string]interface{}{"UserId": userId}, "", http.StatusInternalServerError)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) CreateGroupSyncable(groupSyncable *model.GroupSyncable) (*model.GroupSyncable, *model.AppError) {
|
|
if err := groupSyncable.IsValid(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Reset values that shouldn't be updatable by parameter
|
|
groupSyncable.DeleteAt = 0
|
|
groupSyncable.CreateAt = model.GetMillis()
|
|
groupSyncable.UpdateAt = groupSyncable.CreateAt
|
|
|
|
var insertErr error
|
|
|
|
switch groupSyncable.Type {
|
|
case model.GroupSyncableTypeTeam:
|
|
if _, err := s.Team().Get(groupSyncable.SyncableId); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
insertErr = s.GetMaster().Insert(groupSyncableToGroupTeam(groupSyncable))
|
|
case model.GroupSyncableTypeChannel:
|
|
if _, err := s.Channel().Get(groupSyncable.SyncableId, false); err != nil {
|
|
var nfErr *store.ErrNotFound
|
|
switch {
|
|
case errors.As(err, &nfErr):
|
|
return nil, model.NewAppError("CreateGroupSyncable", "store.sql_channel.get.existing.app_error", nil, nfErr.Error(), http.StatusNotFound)
|
|
default:
|
|
return nil, model.NewAppError("CreateGroupSyncable", "store.sql_channel.get.find.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
insertErr = s.GetMaster().Insert(groupSyncableToGroupChannel(groupSyncable))
|
|
default:
|
|
return nil, model.NewAppError("SqlGroupStore.GroupCreateGroupSyncable", "model.group_syncable.type.app_error", nil, "group_id="+groupSyncable.GroupId+", syncable_id="+groupSyncable.SyncableId, http.StatusInternalServerError)
|
|
}
|
|
|
|
if insertErr != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupCreateGroupSyncable", "store.insert_error", nil, "group_id="+groupSyncable.GroupId+", syncable_id="+groupSyncable.SyncableId+", "+insertErr.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groupSyncable, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) (*model.GroupSyncable, *model.AppError) {
|
|
groupSyncable, err := s.getGroupSyncable(groupID, syncableID, syncableType)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupGetGroupSyncable", "store.sql_group.no_rows", nil, err.Error(), http.StatusNotFound)
|
|
}
|
|
return nil, model.NewAppError("SqlGroupStore.GroupGetGroupSyncable", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groupSyncable, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) getGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) (*model.GroupSyncable, error) {
|
|
var err error
|
|
var result interface{}
|
|
|
|
switch syncableType {
|
|
case model.GroupSyncableTypeTeam:
|
|
result, err = s.GetReplica().Get(groupTeam{}, groupID, syncableID)
|
|
case model.GroupSyncableTypeChannel:
|
|
result, err = s.GetReplica().Get(groupChannel{}, groupID, syncableID)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if result == nil {
|
|
return nil, sql.ErrNoRows
|
|
}
|
|
|
|
groupSyncable := model.GroupSyncable{}
|
|
switch syncableType {
|
|
case model.GroupSyncableTypeTeam:
|
|
groupTeam := result.(*groupTeam)
|
|
groupSyncable.SyncableId = groupTeam.TeamId
|
|
groupSyncable.GroupId = groupTeam.GroupId
|
|
groupSyncable.AutoAdd = groupTeam.AutoAdd
|
|
groupSyncable.CreateAt = groupTeam.CreateAt
|
|
groupSyncable.DeleteAt = groupTeam.DeleteAt
|
|
groupSyncable.UpdateAt = groupTeam.UpdateAt
|
|
groupSyncable.Type = syncableType
|
|
case model.GroupSyncableTypeChannel:
|
|
groupChannel := result.(*groupChannel)
|
|
groupSyncable.SyncableId = groupChannel.ChannelId
|
|
groupSyncable.GroupId = groupChannel.GroupId
|
|
groupSyncable.AutoAdd = groupChannel.AutoAdd
|
|
groupSyncable.CreateAt = groupChannel.CreateAt
|
|
groupSyncable.DeleteAt = groupChannel.DeleteAt
|
|
groupSyncable.UpdateAt = groupChannel.UpdateAt
|
|
groupSyncable.Type = syncableType
|
|
default:
|
|
return nil, fmt.Errorf("unable to convert syncableType: %s", syncableType.String())
|
|
}
|
|
|
|
return &groupSyncable, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetAllGroupSyncablesByGroupId(groupID string, syncableType model.GroupSyncableType) ([]*model.GroupSyncable, *model.AppError) {
|
|
args := map[string]interface{}{"GroupId": groupID}
|
|
|
|
appErrF := func(msg string) *model.AppError {
|
|
return model.NewAppError("SqlGroupStore.GroupGetAllGroupSyncablesByGroup", "store.select_error", nil, msg, http.StatusInternalServerError)
|
|
}
|
|
|
|
groupSyncables := []*model.GroupSyncable{}
|
|
|
|
switch syncableType {
|
|
case model.GroupSyncableTypeTeam:
|
|
sqlQuery := `
|
|
SELECT
|
|
GroupTeams.*,
|
|
Teams.DisplayName AS TeamDisplayName,
|
|
Teams.Type AS TeamType
|
|
FROM
|
|
GroupTeams
|
|
JOIN Teams ON Teams.Id = GroupTeams.TeamId
|
|
WHERE
|
|
GroupId = :GroupId AND GroupTeams.DeleteAt = 0`
|
|
|
|
results := []*groupTeamJoin{}
|
|
_, err := s.GetReplica().Select(&results, sqlQuery, args)
|
|
if err != nil {
|
|
return nil, appErrF(err.Error())
|
|
}
|
|
for _, result := range results {
|
|
groupSyncable := &model.GroupSyncable{
|
|
SyncableId: result.TeamId,
|
|
GroupId: result.GroupId,
|
|
AutoAdd: result.AutoAdd,
|
|
CreateAt: result.CreateAt,
|
|
DeleteAt: result.DeleteAt,
|
|
UpdateAt: result.UpdateAt,
|
|
Type: syncableType,
|
|
TeamDisplayName: result.TeamDisplayName,
|
|
TeamType: result.TeamType,
|
|
SchemeAdmin: result.SchemeAdmin,
|
|
}
|
|
groupSyncables = append(groupSyncables, groupSyncable)
|
|
}
|
|
case model.GroupSyncableTypeChannel:
|
|
sqlQuery := `
|
|
SELECT
|
|
GroupChannels.*,
|
|
Channels.DisplayName AS ChannelDisplayName,
|
|
Teams.DisplayName AS TeamDisplayName,
|
|
Channels.Type As ChannelType,
|
|
Teams.Type As TeamType,
|
|
Teams.Id AS TeamId
|
|
FROM
|
|
GroupChannels
|
|
JOIN Channels ON Channels.Id = GroupChannels.ChannelId
|
|
JOIN Teams ON Teams.Id = Channels.TeamId
|
|
WHERE
|
|
GroupId = :GroupId AND GroupChannels.DeleteAt = 0`
|
|
|
|
results := []*groupChannelJoin{}
|
|
_, err := s.GetReplica().Select(&results, sqlQuery, args)
|
|
if err != nil {
|
|
return nil, appErrF(err.Error())
|
|
}
|
|
for _, result := range results {
|
|
groupSyncable := &model.GroupSyncable{
|
|
SyncableId: result.ChannelId,
|
|
GroupId: result.GroupId,
|
|
AutoAdd: result.AutoAdd,
|
|
CreateAt: result.CreateAt,
|
|
DeleteAt: result.DeleteAt,
|
|
UpdateAt: result.UpdateAt,
|
|
Type: syncableType,
|
|
ChannelDisplayName: result.ChannelDisplayName,
|
|
ChannelType: result.ChannelType,
|
|
TeamDisplayName: result.TeamDisplayName,
|
|
TeamType: result.TeamType,
|
|
TeamID: result.TeamID,
|
|
SchemeAdmin: result.SchemeAdmin,
|
|
}
|
|
groupSyncables = append(groupSyncables, groupSyncable)
|
|
}
|
|
}
|
|
|
|
return groupSyncables, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) UpdateGroupSyncable(groupSyncable *model.GroupSyncable) (*model.GroupSyncable, *model.AppError) {
|
|
retrievedGroupSyncable, err := s.getGroupSyncable(groupSyncable.GroupId, groupSyncable.SyncableId, groupSyncable.Type)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupUpdateGroupSyncable", "store.sql_group.no_rows", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
return nil, model.NewAppError("SqlGroupStore.GroupUpdateGroupSyncable", "store.select_error", nil, "GroupId="+groupSyncable.GroupId+", SyncableId="+groupSyncable.SyncableId+", SyncableType="+groupSyncable.Type.String()+", "+err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
if err := groupSyncable.IsValid(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// If updating DeleteAt it can only be to 0
|
|
if groupSyncable.DeleteAt != retrievedGroupSyncable.DeleteAt && groupSyncable.DeleteAt != 0 {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupUpdateGroupSyncable", "model.group.delete_at.app_error", nil, "", http.StatusInternalServerError)
|
|
}
|
|
|
|
// Reset these properties, don't update them based on input
|
|
groupSyncable.CreateAt = retrievedGroupSyncable.CreateAt
|
|
groupSyncable.UpdateAt = model.GetMillis()
|
|
|
|
switch groupSyncable.Type {
|
|
case model.GroupSyncableTypeTeam:
|
|
_, err = s.GetMaster().Update(groupSyncableToGroupTeam(groupSyncable))
|
|
case model.GroupSyncableTypeChannel:
|
|
_, err = s.GetMaster().Update(groupSyncableToGroupChannel(groupSyncable))
|
|
default:
|
|
return nil, model.NewAppError("SqlGroupStore.GroupUpdateGroupSyncable", "model.group_syncable.type.app_error", nil, "group_id="+groupSyncable.GroupId+", syncable_id="+groupSyncable.SyncableId, http.StatusInternalServerError)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupUpdateGroupSyncable", "store.update_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groupSyncable, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) DeleteGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) (*model.GroupSyncable, *model.AppError) {
|
|
groupSyncable, err := s.getGroupSyncable(groupID, syncableID, syncableType)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupDeleteGroupSyncable", "store.sql_group.no_rows", nil, "Id="+groupID+", "+err.Error(), http.StatusNotFound)
|
|
}
|
|
return nil, model.NewAppError("SqlGroupStore.GroupDeleteGroupSyncable", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
if groupSyncable.DeleteAt != 0 {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupDeleteGroupSyncable", "store.sql_group.group_syncable_already_deleted", nil, "group_id="+groupID+"syncable_id="+syncableID, http.StatusBadRequest)
|
|
}
|
|
|
|
time := model.GetMillis()
|
|
groupSyncable.DeleteAt = time
|
|
groupSyncable.UpdateAt = time
|
|
|
|
switch groupSyncable.Type {
|
|
case model.GroupSyncableTypeTeam:
|
|
_, err = s.GetMaster().Update(groupSyncableToGroupTeam(groupSyncable))
|
|
case model.GroupSyncableTypeChannel:
|
|
_, err = s.GetMaster().Update(groupSyncableToGroupChannel(groupSyncable))
|
|
default:
|
|
return nil, model.NewAppError("SqlGroupStore.GroupDeleteGroupSyncable", "model.group_syncable.type.app_error", nil, "group_id="+groupSyncable.GroupId+", syncable_id="+groupSyncable.SyncableId, http.StatusInternalServerError)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GroupDeleteGroupSyncable", "store.update_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groupSyncable, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) TeamMembersToAdd(since int64, teamID *string) ([]*model.UserTeamIDPair, *model.AppError) {
|
|
query := s.getQueryBuilder().Select("GroupMembers.UserId", "GroupTeams.TeamId").
|
|
From("GroupMembers").
|
|
Join("GroupTeams ON GroupTeams.GroupId = GroupMembers.GroupId").
|
|
Join("UserGroups ON UserGroups.Id = GroupMembers.GroupId").
|
|
Join("Teams ON Teams.Id = GroupTeams.TeamId").
|
|
JoinClause("LEFT OUTER JOIN TeamMembers ON TeamMembers.TeamId = GroupTeams.TeamId AND TeamMembers.UserId = GroupMembers.UserId").
|
|
Where(sq.Eq{
|
|
"TeamMembers.UserId": nil,
|
|
"UserGroups.DeleteAt": 0,
|
|
"GroupTeams.DeleteAt": 0,
|
|
"GroupTeams.AutoAdd": true,
|
|
"GroupMembers.DeleteAt": 0,
|
|
"Teams.DeleteAt": 0,
|
|
}).
|
|
Where("(GroupMembers.CreateAt >= ? OR GroupTeams.UpdateAt >= ?)", since, since)
|
|
|
|
if teamID != nil {
|
|
query = query.Where(sq.Eq{"Teams.Id": *teamID})
|
|
}
|
|
|
|
sql, params, err := query.ToSql()
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.TeamMembersToAdd", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var teamMembers []*model.UserTeamIDPair
|
|
|
|
_, err = s.GetReplica().Select(&teamMembers, sql, params...)
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.TeamMembersToAdd", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return teamMembers, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) ChannelMembersToAdd(since int64, channelID *string) ([]*model.UserChannelIDPair, *model.AppError) {
|
|
query := s.getQueryBuilder().Select("GroupMembers.UserId", "GroupChannels.ChannelId").
|
|
From("GroupMembers").
|
|
Join("GroupChannels ON GroupChannels.GroupId = GroupMembers.GroupId").
|
|
Join("UserGroups ON UserGroups.Id = GroupMembers.GroupId").
|
|
Join("Channels ON Channels.Id = GroupChannels.ChannelId").
|
|
JoinClause("LEFT OUTER JOIN ChannelMemberHistory ON ChannelMemberHistory.ChannelId = GroupChannels.ChannelId AND ChannelMemberHistory.UserId = GroupMembers.UserId").
|
|
Where(sq.Eq{
|
|
"ChannelMemberHistory.UserId": nil,
|
|
"ChannelMemberHistory.LeaveTime": nil,
|
|
"UserGroups.DeleteAt": 0,
|
|
"GroupChannels.DeleteAt": 0,
|
|
"GroupChannels.AutoAdd": true,
|
|
"GroupMembers.DeleteAt": 0,
|
|
"Channels.DeleteAt": 0,
|
|
}).
|
|
Where("(GroupMembers.CreateAt >= ? OR GroupChannels.UpdateAt >= ?)", since, since)
|
|
|
|
if channelID != nil {
|
|
query = query.Where(sq.Eq{"Channels.Id": *channelID})
|
|
}
|
|
|
|
sql, params, err := query.ToSql()
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.ChannelMembersToAdd", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var channelMembers []*model.UserChannelIDPair
|
|
|
|
_, err = s.GetReplica().Select(&channelMembers, sql, params...)
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.ChannelMembersToAdd", "store.select_error", nil, "", http.StatusInternalServerError)
|
|
}
|
|
|
|
return channelMembers, nil
|
|
}
|
|
|
|
func groupSyncableToGroupTeam(groupSyncable *model.GroupSyncable) *groupTeam {
|
|
return &groupTeam{
|
|
GroupSyncable: *groupSyncable,
|
|
TeamId: groupSyncable.SyncableId,
|
|
}
|
|
}
|
|
|
|
func groupSyncableToGroupChannel(groupSyncable *model.GroupSyncable) *groupChannel {
|
|
return &groupChannel{
|
|
GroupSyncable: *groupSyncable,
|
|
ChannelId: groupSyncable.SyncableId,
|
|
}
|
|
}
|
|
|
|
func (s *SqlGroupStore) TeamMembersToRemove(teamID *string) ([]*model.TeamMember, *model.AppError) {
|
|
whereStmt := `
|
|
(TeamMembers.TeamId,
|
|
TeamMembers.UserId)
|
|
NOT IN (
|
|
SELECT
|
|
Teams.Id AS TeamId,
|
|
GroupMembers.UserId
|
|
FROM
|
|
Teams
|
|
JOIN GroupTeams ON GroupTeams.TeamId = Teams.Id
|
|
JOIN UserGroups ON UserGroups.Id = GroupTeams.GroupId
|
|
JOIN GroupMembers ON GroupMembers.GroupId = UserGroups.Id
|
|
WHERE
|
|
Teams.GroupConstrained = TRUE
|
|
AND GroupTeams.DeleteAt = 0
|
|
AND UserGroups.DeleteAt = 0
|
|
AND Teams.DeleteAt = 0
|
|
AND GroupMembers.DeleteAt = 0
|
|
GROUP BY
|
|
Teams.Id,
|
|
GroupMembers.UserId)`
|
|
|
|
query := s.getQueryBuilder().Select(
|
|
"TeamMembers.TeamId",
|
|
"TeamMembers.UserId",
|
|
"TeamMembers.Roles",
|
|
"TeamMembers.DeleteAt",
|
|
"TeamMembers.SchemeUser",
|
|
"TeamMembers.SchemeAdmin",
|
|
"(TeamMembers.SchemeGuest IS NOT NULL AND TeamMembers.SchemeGuest) AS SchemeGuest",
|
|
).
|
|
From("TeamMembers").
|
|
Join("Teams ON Teams.Id = TeamMembers.TeamId").
|
|
LeftJoin("Bots ON Bots.UserId = TeamMembers.UserId").
|
|
Where(sq.Eq{"TeamMembers.DeleteAt": 0, "Teams.DeleteAt": 0, "Teams.GroupConstrained": true, "Bots.UserId": nil}).
|
|
Where(whereStmt)
|
|
|
|
if teamID != nil {
|
|
query = query.Where(sq.Eq{"TeamMembers.TeamId": *teamID})
|
|
}
|
|
|
|
sql, params, err := query.ToSql()
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.TeamMembersToRemove", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var teamMembers []*model.TeamMember
|
|
|
|
_, err = s.GetReplica().Select(&teamMembers, sql, params...)
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.TeamMembersToRemove", "store.select_error", nil, "", http.StatusInternalServerError)
|
|
}
|
|
|
|
return teamMembers, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) CountGroupsByChannel(channelId string, opts model.GroupSearchOpts) (int64, *model.AppError) {
|
|
countQuery := s.groupsBySyncableBaseQuery(model.GroupSyncableTypeChannel, selectCountGroups, channelId, opts)
|
|
|
|
countQueryString, args, err := countQuery.ToSql()
|
|
if err != nil {
|
|
return int64(0), model.NewAppError("SqlGroupStore.CountGroupsByChannel", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
count, err := s.GetReplica().SelectInt(countQueryString, args...)
|
|
if err != nil {
|
|
return int64(0), model.NewAppError("SqlGroupStore.CountGroupsByChannel", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetGroupsByChannel(channelId string, opts model.GroupSearchOpts) ([]*model.GroupWithSchemeAdmin, *model.AppError) {
|
|
query := s.groupsBySyncableBaseQuery(model.GroupSyncableTypeChannel, selectGroups, channelId, opts)
|
|
|
|
if opts.PageOpts != nil {
|
|
offset := uint64(opts.PageOpts.Page * opts.PageOpts.PerPage)
|
|
query = query.OrderBy("ug.DisplayName").Limit(uint64(opts.PageOpts.PerPage)).Offset(offset)
|
|
}
|
|
|
|
queryString, args, err := query.ToSql()
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetGroupsByChannel", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var groups []*model.GroupWithSchemeAdmin
|
|
|
|
_, err = s.GetReplica().Select(&groups, queryString, args...)
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetGroupsByChannel", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groups, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) ChannelMembersToRemove(channelID *string) ([]*model.ChannelMember, *model.AppError) {
|
|
whereStmt := `
|
|
(ChannelMembers.ChannelId,
|
|
ChannelMembers.UserId)
|
|
NOT IN (
|
|
SELECT
|
|
Channels.Id AS ChannelId,
|
|
GroupMembers.UserId
|
|
FROM
|
|
Channels
|
|
JOIN GroupChannels ON GroupChannels.ChannelId = Channels.Id
|
|
JOIN UserGroups ON UserGroups.Id = GroupChannels.GroupId
|
|
JOIN GroupMembers ON GroupMembers.GroupId = UserGroups.Id
|
|
WHERE
|
|
Channels.GroupConstrained = TRUE
|
|
AND GroupChannels.DeleteAt = 0
|
|
AND UserGroups.DeleteAt = 0
|
|
AND Channels.DeleteAt = 0
|
|
AND GroupMembers.DeleteAt = 0
|
|
GROUP BY
|
|
Channels.Id,
|
|
GroupMembers.UserId)`
|
|
|
|
query := s.getQueryBuilder().Select(
|
|
"ChannelMembers.ChannelId",
|
|
"ChannelMembers.UserId",
|
|
"ChannelMembers.LastViewedAt",
|
|
"ChannelMembers.MsgCount",
|
|
"ChannelMembers.MentionCount",
|
|
"ChannelMembers.NotifyProps",
|
|
"ChannelMembers.LastUpdateAt",
|
|
"ChannelMembers.LastUpdateAt",
|
|
"ChannelMembers.SchemeUser",
|
|
"ChannelMembers.SchemeAdmin",
|
|
"(ChannelMembers.SchemeGuest IS NOT NULL AND ChannelMembers.SchemeGuest) AS SchemeGuest",
|
|
).
|
|
From("ChannelMembers").
|
|
Join("Channels ON Channels.Id = ChannelMembers.ChannelId").
|
|
LeftJoin("Bots ON Bots.UserId = ChannelMembers.UserId").
|
|
Where(sq.Eq{"Channels.DeleteAt": 0, "Channels.GroupConstrained": true, "Bots.UserId": nil}).
|
|
Where(whereStmt)
|
|
|
|
if channelID != nil {
|
|
query = query.Where(sq.Eq{"ChannelMembers.ChannelId": *channelID})
|
|
}
|
|
|
|
sql, params, err := query.ToSql()
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.ChannelMembersToRemove", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var channelMembers []*model.ChannelMember
|
|
|
|
_, err = s.GetReplica().Select(&channelMembers, sql, params...)
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.ChannelMembersToRemove", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return channelMembers, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) groupsBySyncableBaseQuery(st model.GroupSyncableType, t selectType, syncableID string, opts model.GroupSearchOpts) sq.SelectBuilder {
|
|
selectStrs := map[selectType]string{
|
|
selectGroups: "ug.*, gs.SchemeAdmin AS SyncableSchemeAdmin",
|
|
selectCountGroups: "COUNT(*)",
|
|
}
|
|
|
|
var table string
|
|
var idCol string
|
|
if st == model.GroupSyncableTypeTeam {
|
|
table = "GroupTeams"
|
|
idCol = "TeamId"
|
|
} else {
|
|
table = "GroupChannels"
|
|
idCol = "ChannelId"
|
|
}
|
|
|
|
query := s.getQueryBuilder().
|
|
Select(selectStrs[t]).
|
|
From(fmt.Sprintf("%s gs", table)).
|
|
LeftJoin("UserGroups ug ON gs.GroupId = ug.Id").
|
|
Where(fmt.Sprintf("ug.DeleteAt = 0 AND gs.%s = ? AND gs.DeleteAt = 0", idCol), syncableID)
|
|
|
|
if opts.IncludeMemberCount && t == selectGroups {
|
|
query = s.getQueryBuilder().
|
|
Select(fmt.Sprintf("ug.*, coalesce(Members.MemberCount, 0) AS MemberCount, Group%ss.SchemeAdmin AS SyncableSchemeAdmin", st)).
|
|
From("UserGroups ug").
|
|
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 = ug.Id").
|
|
LeftJoin(fmt.Sprintf("%[1]s ON %[1]s.GroupId = ug.Id", table)).
|
|
Where(fmt.Sprintf("ug.DeleteAt = 0 AND %[1]s.DeleteAt = 0 AND %[1]s.%[2]s = ?", table, idCol), syncableID).
|
|
OrderBy("ug.DisplayName")
|
|
}
|
|
|
|
if opts.FilterAllowReference && t == selectGroups {
|
|
query = query.Where("ug.AllowReference = true")
|
|
}
|
|
|
|
if len(opts.Q) > 0 {
|
|
pattern := fmt.Sprintf("%%%s%%", sanitizeSearchTerm(opts.Q, "\\"))
|
|
operatorKeyword := "ILIKE"
|
|
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
|
operatorKeyword = "LIKE"
|
|
}
|
|
query = query.Where(fmt.Sprintf("(ug.Name %[1]s ? OR ug.DisplayName %[1]s ?)", operatorKeyword), pattern, pattern)
|
|
}
|
|
|
|
return query
|
|
}
|
|
|
|
func (s *SqlGroupStore) getGroupsAssociatedToChannelsByTeam(st model.GroupSyncableType, teamID string, opts model.GroupSearchOpts) sq.SelectBuilder {
|
|
query := s.getQueryBuilder().
|
|
Select("gc.ChannelId, ug.*, gc.SchemeAdmin AS SyncableSchemeAdmin").
|
|
From("UserGroups ug").
|
|
LeftJoin(`
|
|
(SELECT
|
|
GroupChannels.GroupId, GroupChannels.ChannelId, GroupChannels.DeleteAt, GroupChannels.SchemeAdmin
|
|
FROM
|
|
GroupChannels
|
|
LEFT JOIN
|
|
Channels ON (Channels.Id = GroupChannels.ChannelId)
|
|
WHERE
|
|
GroupChannels.DeleteAt = 0
|
|
AND Channels.DeleteAt = 0
|
|
AND Channels.TeamId = ?) AS gc ON gc.GroupId = ug.Id`, teamID).
|
|
Where("ug.DeleteAt = 0 AND gc.DeleteAt = 0").
|
|
OrderBy("ug.DisplayName")
|
|
|
|
if opts.IncludeMemberCount {
|
|
query = s.getQueryBuilder().
|
|
Select("gc.ChannelId, ug.*, coalesce(Members.MemberCount, 0) AS MemberCount, gc.SchemeAdmin AS SyncableSchemeAdmin").
|
|
From("UserGroups ug").
|
|
LeftJoin(`
|
|
(SELECT
|
|
GroupChannels.ChannelId, GroupChannels.DeleteAt, GroupChannels.GroupId, GroupChannels.SchemeAdmin
|
|
FROM
|
|
GroupChannels
|
|
LEFT JOIN
|
|
Channels ON (Channels.Id = GroupChannels.ChannelId)
|
|
WHERE
|
|
GroupChannels.DeleteAt = 0
|
|
AND Channels.DeleteAt = 0
|
|
AND Channels.TeamId = ?) AS gc ON gc.GroupId = ug.Id`, teamID).
|
|
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 = ug.Id`).
|
|
Where("ug.DeleteAt = 0 AND gc.DeleteAt = 0").
|
|
OrderBy("ug.DisplayName")
|
|
}
|
|
|
|
if opts.FilterAllowReference {
|
|
query = query.Where("ug.AllowReference = true")
|
|
}
|
|
|
|
if len(opts.Q) > 0 {
|
|
pattern := fmt.Sprintf("%%%s%%", sanitizeSearchTerm(opts.Q, "\\"))
|
|
operatorKeyword := "ILIKE"
|
|
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
|
operatorKeyword = "LIKE"
|
|
}
|
|
query = query.Where(fmt.Sprintf("(ug.Name %[1]s ? OR ug.DisplayName %[1]s ?)", operatorKeyword), pattern, pattern)
|
|
}
|
|
|
|
return query
|
|
}
|
|
|
|
func (s *SqlGroupStore) CountGroupsByTeam(teamId string, opts model.GroupSearchOpts) (int64, *model.AppError) {
|
|
countQuery := s.groupsBySyncableBaseQuery(model.GroupSyncableTypeTeam, selectCountGroups, teamId, opts)
|
|
|
|
countQueryString, args, err := countQuery.ToSql()
|
|
if err != nil {
|
|
return int64(0), model.NewAppError("SqlGroupStore.CountGroupsByTeam", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
count, err := s.GetReplica().SelectInt(countQueryString, args...)
|
|
if err != nil {
|
|
return int64(0), model.NewAppError("SqlGroupStore.CountGroupsByTeam", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetGroupsByTeam(teamId string, opts model.GroupSearchOpts) ([]*model.GroupWithSchemeAdmin, *model.AppError) {
|
|
query := s.groupsBySyncableBaseQuery(model.GroupSyncableTypeTeam, selectGroups, teamId, opts)
|
|
|
|
if opts.PageOpts != nil {
|
|
offset := uint64(opts.PageOpts.Page * opts.PageOpts.PerPage)
|
|
query = query.OrderBy("ug.DisplayName").Limit(uint64(opts.PageOpts.PerPage)).Offset(offset)
|
|
}
|
|
|
|
queryString, args, err := query.ToSql()
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetGroupsByTeam", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var groups []*model.GroupWithSchemeAdmin
|
|
|
|
_, err = s.GetReplica().Select(&groups, queryString, args...)
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetGroupsByTeam", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groups, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetGroupsAssociatedToChannelsByTeam(teamId string, opts model.GroupSearchOpts) (map[string][]*model.GroupWithSchemeAdmin, *model.AppError) {
|
|
query := s.getGroupsAssociatedToChannelsByTeam(model.GroupSyncableTypeTeam, teamId, opts)
|
|
|
|
if opts.PageOpts != nil {
|
|
offset := uint64(opts.PageOpts.Page * opts.PageOpts.PerPage)
|
|
query = query.OrderBy("ug.DisplayName").Limit(uint64(opts.PageOpts.PerPage)).Offset(offset)
|
|
}
|
|
|
|
queryString, args, err := query.ToSql()
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetGroupsAssociatedToChannelsByTeam", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var tgroups []*model.GroupsAssociatedToChannelWithSchemeAdmin
|
|
|
|
_, err = s.GetReplica().Select(&tgroups, queryString, args...)
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetGroupsAssociatedToChannelsByTeam", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
groups := map[string][]*model.GroupWithSchemeAdmin{}
|
|
for _, tgroup := range tgroups {
|
|
var group = model.GroupWithSchemeAdmin{}
|
|
group.Group = tgroup.Group
|
|
group.SchemeAdmin = tgroup.SchemeAdmin
|
|
|
|
if val, ok := groups[tgroup.ChannelId]; ok {
|
|
groups[tgroup.ChannelId] = append(val, &group)
|
|
} else {
|
|
groups[tgroup.ChannelId] = []*model.GroupWithSchemeAdmin{&group}
|
|
}
|
|
}
|
|
|
|
return groups, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GetGroups(page, perPage int, opts model.GroupSearchOpts) ([]*model.Group, *model.AppError) {
|
|
var groups []*model.Group
|
|
|
|
groupsQuery := s.getQueryBuilder().Select("g.*")
|
|
|
|
if opts.IncludeMemberCount {
|
|
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")
|
|
}
|
|
|
|
groupsQuery = groupsQuery.
|
|
From("UserGroups g").
|
|
OrderBy("g.DisplayName")
|
|
|
|
if opts.Since > 0 {
|
|
groupsQuery = groupsQuery.Where(sq.Gt{
|
|
"g.UpdateAt": opts.Since,
|
|
})
|
|
} else {
|
|
groupsQuery = groupsQuery.Where("g.DeleteAt = 0")
|
|
}
|
|
|
|
if perPage != 0 {
|
|
groupsQuery = groupsQuery.
|
|
Limit(uint64(perPage)).
|
|
Offset(uint64(page * perPage))
|
|
}
|
|
|
|
if opts.FilterAllowReference {
|
|
groupsQuery = groupsQuery.Where("g.AllowReference = true")
|
|
}
|
|
|
|
if len(opts.Q) > 0 {
|
|
pattern := fmt.Sprintf("%%%s%%", sanitizeSearchTerm(opts.Q, "\\"))
|
|
operatorKeyword := "ILIKE"
|
|
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
|
operatorKeyword = "LIKE"
|
|
}
|
|
groupsQuery = groupsQuery.Where(fmt.Sprintf("(g.Name %[1]s ? OR g.DisplayName %[1]s ?)", operatorKeyword), pattern, pattern)
|
|
}
|
|
|
|
if len(opts.NotAssociatedToTeam) == 26 {
|
|
groupsQuery = groupsQuery.Where(`
|
|
g.Id NOT IN (
|
|
SELECT
|
|
Id
|
|
FROM
|
|
UserGroups
|
|
JOIN GroupTeams ON GroupTeams.GroupId = UserGroups.Id
|
|
WHERE
|
|
GroupTeams.DeleteAt = 0
|
|
AND UserGroups.DeleteAt = 0
|
|
AND GroupTeams.TeamId = ?
|
|
)
|
|
`, opts.NotAssociatedToTeam)
|
|
}
|
|
|
|
if len(opts.NotAssociatedToChannel) == 26 {
|
|
groupsQuery = groupsQuery.Where(`
|
|
g.Id NOT IN (
|
|
SELECT
|
|
Id
|
|
FROM
|
|
UserGroups
|
|
JOIN GroupChannels ON GroupChannels.GroupId = UserGroups.Id
|
|
WHERE
|
|
GroupChannels.DeleteAt = 0
|
|
AND UserGroups.DeleteAt = 0
|
|
AND GroupChannels.ChannelId = ?
|
|
)
|
|
`, opts.NotAssociatedToChannel)
|
|
}
|
|
|
|
if opts.FilterParentTeamPermitted && len(opts.NotAssociatedToChannel) == 26 {
|
|
groupsQuery = groupsQuery.Where(`
|
|
CASE
|
|
WHEN (
|
|
SELECT
|
|
Teams.GroupConstrained
|
|
FROM
|
|
Teams
|
|
JOIN Channels ON Channels.TeamId = Teams.Id
|
|
WHERE
|
|
Channels.Id = ?
|
|
) THEN g.Id IN (
|
|
SELECT
|
|
GroupId
|
|
FROM
|
|
GroupTeams
|
|
WHERE
|
|
GroupTeams.DeleteAt = 0
|
|
AND GroupTeams.TeamId = (
|
|
SELECT
|
|
TeamId
|
|
FROM
|
|
Channels
|
|
WHERE
|
|
Id = ?
|
|
)
|
|
)
|
|
ELSE TRUE
|
|
END
|
|
`, opts.NotAssociatedToChannel, opts.NotAssociatedToChannel)
|
|
}
|
|
|
|
queryString, args, err := groupsQuery.ToSql()
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetGroups", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
if _, err = s.GetReplica().Select(&groups, queryString, args...); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.GetGroups", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groups, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) teamMembersMinusGroupMembersQuery(teamID string, groupIDs []string, isCount bool) sq.SelectBuilder {
|
|
var selectStr string
|
|
|
|
if isCount {
|
|
selectStr = "count(DISTINCT Users.Id)"
|
|
} else {
|
|
tmpl := "Users.*, coalesce(TeamMembers.SchemeGuest, false), TeamMembers.SchemeAdmin, TeamMembers.SchemeUser, %s AS GroupIDs"
|
|
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
|
selectStr = fmt.Sprintf(tmpl, "group_concat(UserGroups.Id)")
|
|
} else {
|
|
selectStr = fmt.Sprintf(tmpl, "string_agg(UserGroups.Id, ',')")
|
|
}
|
|
}
|
|
|
|
subQuery := s.getQueryBuilder().Select("GroupMembers.UserId").
|
|
From("GroupMembers").
|
|
Join("UserGroups ON UserGroups.Id = GroupMembers.GroupId").
|
|
Where("GroupMembers.DeleteAt = 0").
|
|
Where(fmt.Sprintf("GroupMembers.GroupId IN ('%s')", strings.Join(groupIDs, "', '")))
|
|
|
|
sql, _ := subQuery.MustSql()
|
|
|
|
query := s.getQueryBuilder().Select(selectStr).
|
|
From("TeamMembers").
|
|
Join("Teams ON Teams.Id = TeamMembers.TeamId").
|
|
Join("Users ON Users.Id = TeamMembers.UserId").
|
|
LeftJoin("Bots ON Bots.UserId = TeamMembers.UserId").
|
|
LeftJoin("GroupMembers ON GroupMembers.UserId = Users.Id").
|
|
LeftJoin("UserGroups ON UserGroups.Id = GroupMembers.GroupId").
|
|
Where("TeamMembers.DeleteAt = 0").
|
|
Where("Teams.DeleteAt = 0").
|
|
Where("Users.DeleteAt = 0").
|
|
Where("Bots.UserId IS NULL").
|
|
Where("Teams.Id = ?", teamID).
|
|
Where(fmt.Sprintf("Users.Id NOT IN (%s)", sql))
|
|
|
|
if !isCount {
|
|
query = query.GroupBy("Users.Id, TeamMembers.SchemeGuest, TeamMembers.SchemeAdmin, TeamMembers.SchemeUser")
|
|
}
|
|
|
|
return query
|
|
}
|
|
|
|
// TeamMembersMinusGroupMembers returns the set of users on the given team minus the set of users in the given
|
|
// groups.
|
|
func (s *SqlGroupStore) TeamMembersMinusGroupMembers(teamID string, groupIDs []string, page, perPage int) ([]*model.UserWithGroups, *model.AppError) {
|
|
query := s.teamMembersMinusGroupMembersQuery(teamID, groupIDs, false)
|
|
query = query.OrderBy("Users.Username ASC").Limit(uint64(perPage)).Offset(uint64(page * perPage))
|
|
|
|
queryString, args, err := query.ToSql()
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.TeamMembersMinusGroupMembers", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var users []*model.UserWithGroups
|
|
if _, err = s.GetReplica().Select(&users, queryString, args...); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.TeamMembersMinusGroupMembers", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return users, nil
|
|
}
|
|
|
|
// CountTeamMembersMinusGroupMembers returns the count of the set of users on the given team minus the set of users
|
|
// in the given groups.
|
|
func (s *SqlGroupStore) CountTeamMembersMinusGroupMembers(teamID string, groupIDs []string) (int64, *model.AppError) {
|
|
queryString, args, err := s.teamMembersMinusGroupMembersQuery(teamID, groupIDs, true).ToSql()
|
|
if err != nil {
|
|
return 0, model.NewAppError("SqlGroupStore.CountTeamMembersMinusGroupMembers", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var count int64
|
|
if count, err = s.GetReplica().SelectInt(queryString, args...); err != nil {
|
|
return 0, model.NewAppError("SqlGroupStore.CountTeamMembersMinusGroupMembers", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) channelMembersMinusGroupMembersQuery(channelID string, groupIDs []string, isCount bool) sq.SelectBuilder {
|
|
var selectStr string
|
|
|
|
if isCount {
|
|
selectStr = "count(DISTINCT Users.Id)"
|
|
} else {
|
|
tmpl := "Users.*, coalesce(ChannelMembers.SchemeGuest, false), ChannelMembers.SchemeAdmin, ChannelMembers.SchemeUser, %s AS GroupIDs"
|
|
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
|
selectStr = fmt.Sprintf(tmpl, "group_concat(UserGroups.Id)")
|
|
} else {
|
|
selectStr = fmt.Sprintf(tmpl, "string_agg(UserGroups.Id, ',')")
|
|
}
|
|
}
|
|
|
|
subQuery := s.getQueryBuilder().Select("GroupMembers.UserId").
|
|
From("GroupMembers").
|
|
Join("UserGroups ON UserGroups.Id = GroupMembers.GroupId").
|
|
Where("GroupMembers.DeleteAt = 0").
|
|
Where(fmt.Sprintf("GroupMembers.GroupId IN ('%s')", strings.Join(groupIDs, "', '")))
|
|
|
|
sql, _ := subQuery.MustSql()
|
|
|
|
query := s.getQueryBuilder().Select(selectStr).
|
|
From("ChannelMembers").
|
|
Join("Channels ON Channels.Id = ChannelMembers.ChannelId").
|
|
Join("Users ON Users.Id = ChannelMembers.UserId").
|
|
LeftJoin("Bots ON Bots.UserId = ChannelMembers.UserId").
|
|
LeftJoin("GroupMembers ON GroupMembers.UserId = Users.Id").
|
|
LeftJoin("UserGroups ON UserGroups.Id = GroupMembers.GroupId").
|
|
Where("Channels.DeleteAt = 0").
|
|
Where("Users.DeleteAt = 0").
|
|
Where("Bots.UserId IS NULL").
|
|
Where("Channels.Id = ?", channelID).
|
|
Where(fmt.Sprintf("Users.Id NOT IN (%s)", sql))
|
|
|
|
if !isCount {
|
|
query = query.GroupBy("Users.Id, ChannelMembers.SchemeGuest, ChannelMembers.SchemeAdmin, ChannelMembers.SchemeUser")
|
|
}
|
|
|
|
return query
|
|
}
|
|
|
|
// ChannelMembersMinusGroupMembers returns the set of users in the given channel minus the set of users in the given
|
|
// groups.
|
|
func (s *SqlGroupStore) ChannelMembersMinusGroupMembers(channelID string, groupIDs []string, page, perPage int) ([]*model.UserWithGroups, *model.AppError) {
|
|
query := s.channelMembersMinusGroupMembersQuery(channelID, groupIDs, false)
|
|
query = query.OrderBy("Users.Username ASC").Limit(uint64(perPage)).Offset(uint64(page * perPage))
|
|
|
|
queryString, args, err := query.ToSql()
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.ChannelMembersMinusGroupMembers", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var users []*model.UserWithGroups
|
|
if _, err = s.GetReplica().Select(&users, queryString, args...); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.ChannelMembersMinusGroupMembers", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return users, nil
|
|
}
|
|
|
|
// CountChannelMembersMinusGroupMembers returns the count of the set of users in the given channel minus the set of users
|
|
// in the given groups.
|
|
func (s *SqlGroupStore) CountChannelMembersMinusGroupMembers(channelID string, groupIDs []string) (int64, *model.AppError) {
|
|
queryString, args, err := s.channelMembersMinusGroupMembersQuery(channelID, groupIDs, true).ToSql()
|
|
if err != nil {
|
|
return 0, model.NewAppError("SqlGroupStore.CountChannelMembersMinusGroupMembers", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var count int64
|
|
if count, err = s.GetReplica().SelectInt(queryString, args...); err != nil {
|
|
return 0, model.NewAppError("SqlGroupStore.CountChannelMembersMinusGroupMembers", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) AdminRoleGroupsForSyncableMember(userID, syncableID string, syncableType model.GroupSyncableType) ([]string, *model.AppError) {
|
|
var groupIds []string
|
|
|
|
sql := fmt.Sprintf(`
|
|
SELECT
|
|
GroupMembers.GroupId
|
|
FROM
|
|
GroupMembers
|
|
INNER JOIN
|
|
Group%[1]ss ON Group%[1]ss.GroupId = GroupMembers.GroupId
|
|
WHERE
|
|
GroupMembers.UserId = :UserId
|
|
AND GroupMembers.DeleteAt = 0
|
|
AND %[1]sId = :%[1]sId
|
|
AND Group%[1]ss.DeleteAt = 0
|
|
AND Group%[1]ss.SchemeAdmin = TRUE`, syncableType)
|
|
|
|
_, err := s.GetReplica().Select(&groupIds, sql, map[string]interface{}{"UserId": userID, fmt.Sprintf("%sId", syncableType): syncableID})
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore AdminRoleGroupsForSyncableMember", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return groupIds, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) PermittedSyncableAdmins(syncableID string, syncableType model.GroupSyncableType) ([]string, *model.AppError) {
|
|
query := s.getQueryBuilder().Select("UserId").
|
|
From(fmt.Sprintf("Group%ss", syncableType)).
|
|
Join(fmt.Sprintf("GroupMembers ON GroupMembers.GroupId = Group%ss.GroupId AND Group%[1]ss.SchemeAdmin = TRUE AND GroupMembers.DeleteAt = 0", syncableType.String())).Where(fmt.Sprintf("Group%[1]ss.%[1]sId = ?", syncableType.String()), syncableID)
|
|
|
|
sql, args, err := query.ToSql()
|
|
if err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.PermittedSyncableAdmins", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var userIDs []string
|
|
if _, err = s.GetReplica().Select(&userIDs, sql, args...); err != nil {
|
|
return nil, model.NewAppError("SqlGroupStore.PermittedSyncableAdmins", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return userIDs, nil
|
|
}
|
|
|
|
func (s *SqlGroupStore) GroupCount() (int64, *model.AppError) {
|
|
return s.countTable("UserGroups")
|
|
}
|
|
|
|
func (s *SqlGroupStore) GroupTeamCount() (int64, *model.AppError) {
|
|
return s.countTable("GroupTeams")
|
|
}
|
|
|
|
func (s *SqlGroupStore) GroupChannelCount() (int64, *model.AppError) {
|
|
return s.countTable("GroupChannels")
|
|
}
|
|
|
|
func (s *SqlGroupStore) GroupMemberCount() (int64, *model.AppError) {
|
|
return s.countTable("GroupMembers")
|
|
}
|
|
|
|
func (s *SqlGroupStore) DistinctGroupMemberCount() (int64, *model.AppError) {
|
|
return s.countTableWithSelectAndWhere("COUNT(DISTINCT UserId)", "GroupMembers", nil)
|
|
}
|
|
|
|
func (s *SqlGroupStore) GroupCountWithAllowReference() (int64, *model.AppError) {
|
|
return s.countTableWithSelectAndWhere("COUNT(*)", "UserGroups", sq.Eq{"AllowReference": true, "DeleteAt": 0})
|
|
}
|
|
|
|
func (s *SqlGroupStore) countTable(tableName string) (int64, *model.AppError) {
|
|
return s.countTableWithSelectAndWhere("COUNT(*)", tableName, nil)
|
|
}
|
|
|
|
func (s *SqlGroupStore) countTableWithSelectAndWhere(selectStr, tableName string, whereStmt map[string]interface{}) (int64, *model.AppError) {
|
|
if whereStmt == nil {
|
|
whereStmt = sq.Eq{"DeleteAt": 0}
|
|
}
|
|
|
|
query := s.getQueryBuilder().Select(selectStr).From(tableName).Where(whereStmt)
|
|
|
|
sql, args, err := query.ToSql()
|
|
if err != nil {
|
|
return 0, model.NewAppError("SqlGroupStore.countTableWithSelect", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
count, err := s.GetReplica().SelectInt(sql, args...)
|
|
if err != nil {
|
|
return 0, model.NewAppError("SqlGroupStore.countTableWithSelect", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
return count, nil
|
|
}
|