4
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2025-07-03 04:57:44 +00:00

Update dependencies (#1610)

* Update dependencies

* Update module to go 1.17
This commit is contained in:
Wim
2021-10-17 00:47:22 +02:00
committed by GitHub
parent 7ae45c42e7
commit 4dd8bae5c9
494 changed files with 15698 additions and 19720 deletions

View File

@ -31,17 +31,18 @@ type AccessResponse struct {
ExpiresIn int32 `json:"expires_in"`
Scope string `json:"scope"`
RefreshToken string `json:"refresh_token"`
IdToken string `json:"id_token"`
}
// IsValid validates the AccessData and returns an error if it isn't configured
// correctly.
func (ad *AccessData) IsValid() *AppError {
if len(ad.ClientId) == 0 || len(ad.ClientId) > 26 {
if ad.ClientId == "" || len(ad.ClientId) > 26 {
return NewAppError("AccessData.IsValid", "model.access.is_valid.client_id.app_error", nil, "", http.StatusBadRequest)
}
if len(ad.UserId) == 0 || len(ad.UserId) > 26 {
if ad.UserId == "" || len(ad.UserId) > 26 {
return NewAppError("AccessData.IsValid", "model.access.is_valid.user_id.app_error", nil, "", http.StatusBadRequest)
}
@ -53,7 +54,7 @@ func (ad *AccessData) IsValid() *AppError {
return NewAppError("AccessData.IsValid", "model.access.is_valid.refresh_token.app_error", nil, "", http.StatusBadRequest)
}
if len(ad.RedirectUri) == 0 || len(ad.RedirectUri) > 256 || !IsValidHttpUrl(ad.RedirectUri) {
if ad.RedirectUri == "" || len(ad.RedirectUri) > 256 || !IsValidHttpUrl(ad.RedirectUri) {
return NewAppError("AccessData.IsValid", "model.access.is_valid.redirect_uri.app_error", nil, "", http.StatusBadRequest)
}

View File

@ -15,27 +15,27 @@ type AnalyticsRow struct {
type AnalyticsRows []*AnalyticsRow
func (me *AnalyticsRow) ToJson() string {
b, _ := json.Marshal(me)
func (ar *AnalyticsRow) ToJson() string {
b, _ := json.Marshal(ar)
return string(b)
}
func AnalyticsRowFromJson(data io.Reader) *AnalyticsRow {
var me *AnalyticsRow
json.NewDecoder(data).Decode(&me)
return me
var ar *AnalyticsRow
json.NewDecoder(data).Decode(&ar)
return ar
}
func (me AnalyticsRows) ToJson() string {
if b, err := json.Marshal(me); err != nil {
func (ar AnalyticsRows) ToJson() string {
b, err := json.Marshal(ar)
if err != nil {
return "[]"
} else {
return string(b)
}
return string(b)
}
func AnalyticsRowsFromJson(data io.Reader) AnalyticsRows {
var me AnalyticsRows
json.NewDecoder(data).Decode(&me)
return me
var ar AnalyticsRows
json.NewDecoder(data).Decode(&ar)
return ar
}

View File

@ -8,7 +8,7 @@ import (
"strings"
)
var atMentionRegexp = regexp.MustCompile(`\B@[[:alnum:]][[:alnum:]\.\-_]*`)
var atMentionRegexp = regexp.MustCompile(`\B@[[:alnum:]][[:alnum:]\.\-_:]*`)
const usernameSpecialChars = ".-_"
@ -24,7 +24,7 @@ func PossibleAtMentions(message string) []string {
alreadyMentioned := make(map[string]bool)
for _, match := range atMentionRegexp.FindAllString(message, -1) {
name := NormalizeUsername(match[1:])
if !alreadyMentioned[name] && IsValidUsername(name) {
if !alreadyMentioned[name] && IsValidUsernameAllowRemote(name) {
names = append(names, name)
alreadyMentioned[name] = true
}

View File

@ -3,7 +3,9 @@
package model
import "github.com/francoispqt/gojay"
import (
"github.com/francoispqt/gojay"
)
// AuditModelTypeConv converts key model types to something better suited for audit output.
func AuditModelTypeConv(val interface{}) (newVal interface{}, converted bool) {
@ -49,6 +51,8 @@ func AuditModelTypeConv(val interface{}) (newVal interface{}, converted bool) {
return newAuditIncomingWebhook(v), true
case *OutgoingWebhook:
return newAuditOutgoingWebhook(v), true
case *RemoteCluster:
return newRemoteCluster(v), true
}
return val, false
}
@ -665,3 +669,45 @@ func (h auditOutgoingWebhook) MarshalJSONObject(enc *gojay.Encoder) {
func (h auditOutgoingWebhook) IsNil() bool {
return false
}
type auditRemoteCluster struct {
RemoteId string
RemoteTeamId string
Name string
DisplayName string
SiteURL string
CreateAt int64
LastPingAt int64
CreatorId string
}
// newRemoteCluster creates a simplified representation of RemoteCluster for output to audit log.
func newRemoteCluster(r *RemoteCluster) auditRemoteCluster {
var rc auditRemoteCluster
if r != nil {
rc.RemoteId = r.RemoteId
rc.RemoteTeamId = r.RemoteTeamId
rc.Name = r.Name
rc.DisplayName = r.DisplayName
rc.SiteURL = r.SiteURL
rc.CreateAt = r.CreateAt
rc.LastPingAt = r.LastPingAt
rc.CreatorId = r.CreatorId
}
return rc
}
func (r auditRemoteCluster) MarshalJSONObject(enc *gojay.Encoder) {
enc.StringKey("remote_id", r.RemoteId)
enc.StringKey("remote_team_id", r.RemoteTeamId)
enc.StringKey("name", r.Name)
enc.StringKey("display_name", r.DisplayName)
enc.StringKey("site_url", r.SiteURL)
enc.Int64Key("create_at", r.CreateAt)
enc.Int64Key("last_ping_at", r.LastPingAt)
enc.StringKey("creator_id", r.CreatorId)
}
func (r auditRemoteCluster) IsNil() bool {
return false
}

View File

@ -14,17 +14,16 @@ func (o Audits) Etag() string {
if len(o) > 0 {
// the first in the list is always the most current
return Etag(o[0].CreateAt)
} else {
return ""
}
return ""
}
func (o Audits) ToJson() string {
if b, err := json.Marshal(o); err != nil {
b, err := json.Marshal(o)
if err != nil {
return "[]"
} else {
return string(b)
}
return string(b)
}
func AuditsFromJson(data io.Reader) Audits {

View File

@ -47,7 +47,7 @@ func (ad *AuthData) IsValid() *AppError {
return NewAppError("AuthData.IsValid", "model.authorize.is_valid.user_id.app_error", nil, "", http.StatusBadRequest)
}
if len(ad.Code) == 0 || len(ad.Code) > 128 {
if ad.Code == "" || len(ad.Code) > 128 {
return NewAppError("AuthData.IsValid", "model.authorize.is_valid.auth_code.app_error", nil, "client_id="+ad.ClientId, http.StatusBadRequest)
}
@ -82,11 +82,11 @@ func (ar *AuthorizeRequest) IsValid() *AppError {
return NewAppError("AuthData.IsValid", "model.authorize.is_valid.client_id.app_error", nil, "", http.StatusBadRequest)
}
if len(ar.ResponseType) == 0 {
if ar.ResponseType == "" {
return NewAppError("AuthData.IsValid", "model.authorize.is_valid.response_type.app_error", nil, "", http.StatusBadRequest)
}
if len(ar.RedirectUri) == 0 || len(ar.RedirectUri) > 256 || !IsValidHttpUrl(ar.RedirectUri) {
if ar.RedirectUri == "" || len(ar.RedirectUri) > 256 || !IsValidHttpUrl(ar.RedirectUri) {
return NewAppError("AuthData.IsValid", "model.authorize.is_valid.redirect_uri.app_error", nil, "client_id="+ar.ClientId, http.StatusBadRequest)
}
@ -110,7 +110,7 @@ func (ad *AuthData) PreSave() {
ad.CreateAt = GetMillis()
}
if len(ad.Scope) == 0 {
if ad.Scope == "" {
ad.Scope = DEFAULT_SCOPE
}
}

View File

@ -17,6 +17,7 @@ const (
BOT_DESCRIPTION_MAX_RUNES = 1024
BOT_CREATOR_ID_MAX_RUNES = KEY_VALUE_PLUGIN_ID_MAX_RUNES // UserId or PluginId
BOT_WARN_METRIC_BOT_USERNAME = "mattermost-advisor"
BOT_SYSTEM_BOT_USERNAME = "system-bot"
)
// Bot is a special type of User meant for programmatic interactions.
@ -82,7 +83,7 @@ func (b *Bot) IsValid() *AppError {
return NewAppError("Bot.IsValid", "model.bot.is_valid.description.app_error", b.Trace(), "", http.StatusBadRequest)
}
if len(b.OwnerId) == 0 || utf8.RuneCountInString(b.OwnerId) > BOT_CREATOR_ID_MAX_RUNES {
if b.OwnerId == "" || utf8.RuneCountInString(b.OwnerId) > BOT_CREATOR_ID_MAX_RUNES {
return NewAppError("Bot.IsValid", "model.bot.is_valid.creator_id.app_error", b.Trace(), "", http.StatusBadRequest)
}
@ -128,6 +129,8 @@ func BotFromJson(data io.Reader) *Bot {
}
// Patch modifies an existing bot with optional fields from the given patch.
// TODO 6.0: consider returning a boolean to indicate whether or not the patch
// applied any changes.
func (b *Bot) Patch(patch *BotPatch) {
if patch.Username != nil {
b.Username = *patch.Username
@ -142,6 +145,23 @@ func (b *Bot) Patch(patch *BotPatch) {
}
}
// WouldPatch returns whether or not the given patch would be applied or not.
func (b *Bot) WouldPatch(patch *BotPatch) bool {
if patch == nil {
return false
}
if patch.Username != nil && *patch.Username != b.Username {
return true
}
if patch.DisplayName != nil && *patch.DisplayName != b.DisplayName {
return true
}
if patch.Description != nil && *patch.Description != b.Description {
return true
}
return false
}
// ToJson serializes the bot patch to json.
func (b *BotPatch) ToJson() []byte {
data, err := json.Marshal(b)

View File

@ -1,288 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
var BotDefaultImage = []byte{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x7e,
0x08, 0x06, 0x00, 0x00, 0x00, 0xec, 0xa6, 0x19, 0xa2, 0x00, 0x00, 0x00,
0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61,
0x05, 0x00, 0x00, 0x0c, 0xe3, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed,
0x5d, 0x5b, 0x88, 0x15, 0xc9, 0x19, 0xae, 0xf1, 0xba, 0x5e, 0xc6, 0x4b,
0xbc, 0xa0, 0x46, 0xd7, 0x2b, 0xce, 0x2a, 0xba, 0xca, 0x8a, 0xa3, 0x82,
0x38, 0xa3, 0x46, 0x47, 0x5d, 0x59, 0xa3, 0x2f, 0x9b, 0x28, 0x6e, 0xc2,
0xea, 0x83, 0x22, 0x04, 0x82, 0x78, 0x41, 0xf2, 0xb2, 0x04, 0xd4, 0x07,
0x49, 0x22, 0xc4, 0x07, 0xcd, 0x53, 0x30, 0x26, 0x1a, 0xa2, 0x08, 0xee,
0xaa, 0xa8, 0x71, 0xb2, 0x41, 0x11, 0xbc, 0xdf, 0xd0, 0x8d, 0x6e, 0x24,
0x82, 0xe3, 0x65, 0x32, 0x9b, 0x19, 0x1d, 0x47, 0xd7, 0xeb, 0xc9, 0xf7,
0xb5, 0xa7, 0x0f, 0xe7, 0x9c, 0xe9, 0x3e, 0xdd, 0xe7, 0x4c, 0x57, 0x77,
0x75, 0x75, 0xfd, 0xf0, 0x9f, 0xee, 0xae, 0xaa, 0xae, 0xfa, 0xff, 0xef,
0xfb, 0x4f, 0x9d, 0xee, 0xaa, 0xea, 0x3e, 0x65, 0x42, 0x4f, 0xe9, 0x0b,
0xb7, 0x46, 0x43, 0x2b, 0xd2, 0xdb, 0x91, 0xd8, 0xf6, 0x84, 0x96, 0x67,
0x69, 0xf7, 0xf4, 0x3e, 0x36, 0xa2, 0x19, 0xfa, 0x34, 0xbd, 0xe5, 0x3e,
0xf5, 0x31, 0xf4, 0xdf, 0xd0, 0x5b, 0xd0, 0x7f, 0xa5, 0xb7, 0x0d, 0xd8,
0x6a, 0x25, 0x65, 0x1a, 0x78, 0xd3, 0x1b, 0x3e, 0x54, 0x41, 0x67, 0x42,
0x2b, 0xa1, 0x24, 0x9d, 0x69, 0x32, 0xa4, 0x11, 0x95, 0x32, 0x18, 0xce,
0x42, 0x6b, 0xa1, 0x5f, 0x43, 0x99, 0x16, 0x5b, 0x89, 0x63, 0x00, 0x74,
0x03, 0xda, 0xb3, 0xa0, 0x24, 0xbc, 0x1a, 0x3a, 0x01, 0xda, 0x0e, 0x1a,
0x85, 0xbc, 0x45, 0xa3, 0x57, 0xa0, 0xff, 0x80, 0x32, 0x20, 0x4e, 0x42,
0x5b, 0xa0, 0x46, 0x02, 0x46, 0xa0, 0x3d, 0xea, 0xab, 0x81, 0xee, 0x86,
0xb2, 0xab, 0x4e, 0x29, 0xaa, 0xb4, 0x8d, 0x36, 0xd2, 0x56, 0xda, 0x6c,
0xa4, 0x8d, 0x08, 0x7c, 0x84, 0xf3, 0x7f, 0x03, 0x7d, 0x00, 0x55, 0x95,
0x74, 0x37, 0xbb, 0x68, 0x33, 0x6d, 0xa7, 0x0f, 0x46, 0x8a, 0x44, 0xe0,
0x63, 0x94, 0x3f, 0x0d, 0x75, 0x03, 0x37, 0x6e, 0xe9, 0xf4, 0x85, 0x3e,
0x19, 0x29, 0x80, 0x00, 0xbb, 0xcc, 0x9f, 0x40, 0xf9, 0x9b, 0x1a, 0x37,
0x82, 0xfd, 0xda, 0x4b, 0xdf, 0xe8, 0xa3, 0xf9, 0x79, 0x00, 0x08, 0xb6,
0xf0, 0x02, 0xee, 0x73, 0xe8, 0xb7, 0x50, 0xbf, 0x40, 0xc6, 0xbd, 0x1c,
0x7d, 0xa5, 0xcf, 0x51, 0x5d, 0xbc, 0xa2, 0x69, 0x35, 0x84, 0xb7, 0x6d,
0xe7, 0xa0, 0x71, 0x27, 0xb4, 0x54, 0xfb, 0xe9, 0x3b, 0x31, 0x48, 0x9c,
0x70, 0xa0, 0x66, 0x17, 0x94, 0xb7, 0x51, 0xa5, 0x82, 0xa7, 0xcb, 0x79,
0xc4, 0x80, 0x58, 0x10, 0x13, 0xed, 0x85, 0xe3, 0x0e, 0xab, 0xa0, 0xdf,
0x41, 0x75, 0x21, 0x30, 0x28, 0x3f, 0x88, 0x09, 0xb1, 0x89, 0xe3, 0xd8,
0x0c, 0xcc, 0xf6, 0x16, 0x46, 0xf8, 0x57, 0xd0, 0xa0, 0x00, 0xd3, 0xb5,
0x1e, 0x62, 0xa4, 0x5d, 0x6f, 0x50, 0x0d, 0xa7, 0xea, 0x0c, 0xf9, 0xbe,
0x83, 0x9f, 0x58, 0x11, 0xb3, 0xd8, 0x0b, 0x6f, 0x77, 0xbe, 0x80, 0xbe,
0x81, 0xea, 0xfa, 0x8d, 0x95, 0xe5, 0x17, 0x31, 0x23, 0x76, 0xb1, 0xbd,
0x65, 0xe4, 0x84, 0x4c, 0xad, 0x21, 0xbe, 0xcd, 0x81, 0x4f, 0x0c, 0x89,
0x65, 0xac, 0xe4, 0x7d, 0x58, 0x7b, 0x03, 0x2a, 0xeb, 0xdb, 0x91, 0xb4,
0x7a, 0x89, 0x25, 0x31, 0x8d, 0x85, 0x8c, 0x87, 0x95, 0xf7, 0xa0, 0x49,
0x23, 0x49, 0xb6, 0xbf, 0xc4, 0x94, 0xd8, 0x2a, 0x2d, 0xd5, 0xb0, 0xae,
0x09, 0x2a, 0x1b, 0x8c, 0xa4, 0xd6, 0x4f, 0x6c, 0x89, 0xb1, 0x92, 0xf2,
0x09, 0xac, 0xfa, 0x1e, 0x9a, 0x54, 0x72, 0xc2, 0xf2, 0x9b, 0x18, 0x13,
0x6b, 0xa5, 0xa4, 0x1a, 0xd6, 0x18, 0xf2, 0xc3, 0x0b, 0x7e, 0x62, 0x4d,
0xcc, 0x95, 0x90, 0x49, 0xb0, 0xe2, 0x09, 0x34, 0xac, 0x6f, 0x80, 0x69,
0xe7, 0x1d, 0xd6, 0xc4, 0x9c, 0xd8, 0x47, 0x2a, 0x5c, 0x78, 0x59, 0x0f,
0x35, 0xa4, 0x44, 0x83, 0x01, 0xb1, 0x27, 0x07, 0x91, 0xc8, 0x60, 0xb4,
0x7a, 0x17, 0x6a, 0xc8, 0x8f, 0x16, 0x03, 0x72, 0x40, 0x2e, 0x42, 0x95,
0x4e, 0x68, 0x2d, 0xc9, 0xd3, 0xb8, 0xaa, 0x05, 0x3d, 0xb9, 0x20, 0x27,
0x45, 0x4b, 0xa9, 0xc3, 0x8c, 0xdb, 0xd1, 0xd2, 0x8f, 0x8b, 0x6e, 0xcd,
0x9c, 0x20, 0x0b, 0x81, 0x41, 0xa8, 0xf8, 0x07, 0xd0, 0xc3, 0xb2, 0x1a,
0xc8, 0xae, 0xf7, 0x53, 0x1c, 0xa8, 0xf6, 0x0d, 0x30, 0xf6, 0xbc, 0xe3,
0x84, 0xdc, 0x48, 0x15, 0x5e, 0x70, 0x98, 0x2b, 0x7e, 0x75, 0xbf, 0x00,
0xe4, 0xa6, 0xa8, 0x8b, 0xc2, 0x62, 0xd6, 0xa4, 0xbd, 0x87, 0xca, 0xff,
0x06, 0x2d, 0x87, 0x1a, 0x51, 0x13, 0x01, 0x72, 0x43, 0x8e, 0xc8, 0x95,
0x2f, 0x29, 0x26, 0x00, 0x7e, 0x85, 0x1a, 0x95, 0x1f, 0x8b, 0xf6, 0xe3,
0xf5, 0x80, 0x01, 0x03, 0xc4, 0xee, 0xdd, 0xbb, 0x45, 0x5d, 0x5d, 0x9d,
0xa5, 0xdc, 0x67, 0x9a, 0x26, 0x42, 0x8e, 0xc8, 0x55, 0xa0, 0xf2, 0x01,
0x6a, 0x7b, 0x01, 0x8d, 0xfd, 0x6f, 0x6d, 0xbf, 0x7e, 0xfd, 0x52, 0x0f,
0x1f, 0x3e, 0x4c, 0xe5, 0x0b, 0xd3, 0x98, 0xa7, 0x83, 0x8f, 0x69, 0xae,
0xc8, 0x59, 0x60, 0x52, 0x8b, 0x9a, 0xb4, 0x00, 0x67, 0xd7, 0xae, 0x5d,
0xf9, 0xdc, 0x67, 0x8e, 0x99, 0xa7, 0x8b, 0x9f, 0xf0, 0x83, 0x9c, 0x05,
0x22, 0x9f, 0xa1, 0x16, 0x6d, 0x80, 0xb9, 0x7d, 0xfb, 0x76, 0x86, 0xf0,
0xfc, 0x1d, 0xe6, 0xe9, 0xe4, 0x2b, 0x7c, 0x21, 0x77, 0x05, 0xa5, 0xac,
0x60, 0xee, 0xbb, 0x95, 0x28, 0x7c, 0x1c, 0xba, 0x9f, 0x47, 0xb9, 0xd8,
0x64, 0xbf, 0x78, 0xf1, 0x42, 0x74, 0xea, 0xe4, 0x3c, 0x66, 0xf2, 0xf2,
0xe5, 0x4b, 0xd1, 0xb9, 0x73, 0xe7, 0xd8, 0xf8, 0xe2, 0xc3, 0xd0, 0xff,
0xa2, 0x4c, 0x05, 0xd4, 0xf5, 0x11, 0x76, 0xaf, 0x8b, 0xc0, 0x8d, 0x38,
0x59, 0x1b, 0xf2, 0x7d, 0x00, 0xa6, 0x5b, 0x11, 0x72, 0x47, 0x0e, 0x5d,
0xa5, 0x50, 0x0f, 0xc0, 0x91, 0x25, 0x8e, 0x33, 0xf3, 0x4d, 0x1a, 0xda,
0x48, 0xc2, 0x7a, 0x00, 0xf2, 0xf6, 0x14, 0x3a, 0x14, 0xfa, 0x3f, 0x1e,
0xe4, 0x4b, 0xa1, 0x1e, 0xe0, 0x97, 0x28, 0xac, 0x15, 0xf9, 0xf9, 0xce,
0x27, 0xe4, 0x98, 0x1c, 0x92, 0x4b, 0x47, 0x71, 0xeb, 0x01, 0x7a, 0xa1,
0xf4, 0x7f, 0xa0, 0x3d, 0x1d, 0xcf, 0x8a, 0x71, 0x62, 0x02, 0x7b, 0x00,
0xb2, 0xf5, 0x18, 0x3a, 0x0c, 0xca, 0x25, 0x65, 0x39, 0xe2, 0xd6, 0x03,
0xfc, 0x02, 0xa5, 0xb4, 0x23, 0x3f, 0xc7, 0xf3, 0x64, 0x1d, 0x90, 0x4b,
0x72, 0xda, 0x4a, 0x9c, 0x7a, 0x80, 0x2e, 0x28, 0xc5, 0x15, 0xa8, 0xbc,
0x06, 0xd0, 0x4e, 0x12, 0xda, 0x03, 0x90, 0x47, 0x5e, 0x03, 0x70, 0xdd,
0xc0, 0x73, 0x1e, 0xd8, 0xe2, 0xd4, 0x03, 0x70, 0x9a, 0x57, 0x4b, 0xf2,
0x6d, 0xa7, 0x13, 0xba, 0x25, 0xa7, 0xad, 0xa6, 0xf0, 0x3b, 0x38, 0x80,
0xf1, 0x73, 0x87, 0xb4, 0xc8, 0x93, 0x78, 0x7f, 0x3e, 0x67, 0xce, 0x1c,
0x31, 0x7d, 0xfa, 0x74, 0x31, 0x68, 0xd0, 0x20, 0xd1, 0xbb, 0x77, 0xef,
0x92, 0x6c, 0xea, 0xd8, 0xb1, 0xa3, 0xeb, 0x79, 0xcc, 0x3b, 0x74, 0xe8,
0x90, 0x6b, 0x7e, 0xa1, 0x8c, 0xc6, 0xc6, 0x46, 0x6b, 0x5e, 0xe1, 0xf4,
0xe9, 0xd3, 0xe2, 0xf8, 0xf1, 0xe3, 0x82, 0x3d, 0x8d, 0x82, 0xf2, 0x33,
0xd8, 0xf4, 0x97, 0x42, 0x76, 0xfd, 0x10, 0x99, 0x4a, 0x3d, 0xc7, 0xd7,
0xa5, 0x4b, 0x97, 0xd4, 0xa6, 0x4d, 0x9b, 0x52, 0x4d, 0x4d, 0x4d, 0xf9,
0x03, 0x77, 0xca, 0x1e, 0xd3, 0x56, 0xda, 0x4c, 0xdb, 0x81, 0xa7, 0x4a,
0x4a, 0x6e, 0xc9, 0xb1, 0xab, 0x6c, 0x40, 0x8e, 0x32, 0x06, 0x8f, 0x18,
0x31, 0x22, 0x75, 0xe3, 0xc6, 0x0d, 0x65, 0x89, 0xf6, 0x32, 0x8c, 0xb6,
0xd3, 0x07, 0x95, 0x30, 0x85, 0x2d, 0xe4, 0xd8, 0x55, 0x94, 0x79, 0x9e,
0x6f, 0xd4, 0xa8, 0x51, 0xa9, 0xfa, 0xfa, 0x7a, 0x2f, 0x8c, 0x95, 0xcf,
0xa7, 0x0f, 0xf4, 0x05, 0x88, 0xab, 0xa2, 0xe4, 0xd8, 0x51, 0x3e, 0x44,
0xaa, 0x12, 0x46, 0x76, 0xed, 0xda, 0x35, 0x75, 0xed, 0xda, 0x35, 0xe5,
0xc9, 0xf5, 0x6b, 0x20, 0x7d, 0xa1, 0x4f, 0xaa, 0xe0, 0x0b, 0x3b, 0xc8,
0xb5, 0x25, 0xd9, 0x77, 0x01, 0x3f, 0xb2, 0x13, 0xa3, 0xde, 0xae, 0x5d,
0xbb, 0x56, 0x8c, 0x1b, 0x37, 0x2e, 0x6a, 0x33, 0x02, 0x6b, 0x9f, 0xbe,
0xd0, 0x27, 0x85, 0xc4, 0x91, 0x6b, 0xbe, 0x9a, 0x24, 0xf2, 0x28, 0x2d,
0x2f, 0x2f, 0x8f, 0xd5, 0x05, 0x9f, 0xdf, 0x5e, 0x80, 0x17, 0x86, 0xf4,
0x4d, 0x05, 0x8c, 0x61, 0xc3, 0x97, 0x76, 0x30, 0xda, 0x3d, 0x00, 0x6f,
0x07, 0x67, 0xd8, 0x89, 0x51, 0x6e, 0x6b, 0x6a, 0x6a, 0x44, 0xcf, 0x9e,
0xfa, 0x0d, 0x42, 0xd2, 0x27, 0xfa, 0xa6, 0x88, 0x54, 0xc1, 0x0e, 0x6b,
0x08, 0xc0, 0x0e, 0x80, 0x4a, 0x24, 0x28, 0x31, 0xf1, 0x33, 0x77, 0xee,
0x5c, 0x45, 0x30, 0x0a, 0xde, 0x0c, 0x85, 0x7c, 0x23, 0xd7, 0xe4, 0x3c,
0xf3, 0xa6, 0xca, 0xd9, 0xc1, 0xbb, 0x5b, 0x5a, 0x8d, 0x43, 0x87, 0x0e,
0x2d, 0xed, 0xc4, 0x18, 0x9c, 0xa5, 0x98, 0x6f, 0x16, 0xe7, 0x76, 0x0f,
0x30, 0x5d, 0x15, 0xfc, 0xfa, 0xf7, 0xef, 0xaf, 0x8a, 0x29, 0x81, 0xdb,
0xa1, 0x98, 0x6f, 0x16, 0xe7, 0x76, 0x00, 0x8c, 0x09, 0xdc, 0xdb, 0x12,
0x2b, 0xec, 0xd0, 0xc1, 0xfa, 0x69, 0x2a, 0xf1, 0x6c, 0xb5, 0x4f, 0x53,
0xcc, 0x37, 0x8b, 0x73, 0x06, 0x40, 0x37, 0x28, 0x67, 0x89, 0x8c, 0x24,
0x0b, 0x01, 0x72, 0xde, 0x8d, 0x01, 0xc0, 0x45, 0x83, 0x4e, 0xd3, 0xc2,
0xc9, 0x82, 0x23, 0x79, 0xde, 0x92, 0xf3, 0x0a, 0x3b, 0x00, 0x92, 0xe7,
0xbe, 0xf1, 0x98, 0x08, 0x58, 0x01, 0x10, 0xe8, 0x13, 0x24, 0x51, 0xe0,
0x7a, 0xe2, 0xc4, 0x09, 0xb1, 0x62, 0xc5, 0x0a, 0x31, 0x71, 0xe2, 0x44,
0x31, 0x73, 0xe6, 0x4c, 0xb1, 0x7e, 0xfd, 0x7a, 0x71, 0xff, 0xfe, 0xfd,
0xc0, 0x4c, 0x39, 0x7b, 0xf6, 0xac, 0x58, 0xb9, 0x72, 0xa5, 0x98, 0x30,
0x61, 0x82, 0x98, 0x3f, 0x7f, 0xbe, 0xd8, 0xb6, 0x6d, 0x9b, 0x78, 0xf5,
0xea, 0x55, 0x60, 0xf5, 0x47, 0x58, 0x91, 0xc5, 0xfd, 0x9f, 0x61, 0x40,
0x28, 0x23, 0x54, 0x3d, 0x7a, 0xf4, 0xb0, 0x66, 0xc7, 0x38, 0x43, 0x46,
0x1d, 0x3c, 0x78, 0x70, 0xab, 0x76, 0x8b, 0x99, 0x03, 0x78, 0xfb, 0xf6,
0x6d, 0x6a, 0xf5, 0xea, 0xd5, 0xad, 0xea, 0xa0, 0x3f, 0x58, 0x2f, 0x90,
0x3a, 0x78, 0xf0, 0xa0, 0xdf, 0x81, 0x3a, 0xd7, 0x72, 0x3b, 0x76, 0xec,
0x48, 0x61, 0x9d, 0x40, 0xab, 0x36, 0x2a, 0x2b, 0x2b, 0x8b, 0x9e, 0xac,
0xa2, 0x6f, 0xf9, 0x58, 0x13, 0x03, 0x1b, 0x0f, 0x6e, 0x89, 0x51, 0x7e,
0x19, 0x89, 0xc7, 0xe4, 0x5e, 0x1c, 0x91, 0xd8, 0x80, 0xe5, 0xcc, 0xe4,
0xc9, 0x93, 0x53, 0x17, 0x2e, 0x5c, 0x48, 0x91, 0xb0, 0x6c, 0x71, 0x02,
0xa4, 0x98, 0x00, 0xd8, 0xbe, 0x7d, 0x7b, 0x41, 0xb0, 0xba, 0x77, 0xef,
0x9e, 0xba, 0x73, 0xe7, 0x4e, 0x76, 0x93, 0x45, 0xed, 0x5f, 0xb9, 0x72,
0x25, 0x85, 0x2b, 0x77, 0xd7, 0x36, 0x96, 0x2d, 0x5b, 0x56, 0x54, 0x7d,
0x7e, 0xfc, 0x25, 0x46, 0xc4, 0x8a, 0x98, 0xc9, 0xe6, 0x25, 0xcd, 0xbd,
0x38, 0x25, 0xb3, 0xa1, 0xb1, 0x63, 0xc7, 0xa6, 0xb0, 0x3a, 0xc6, 0x11,
0x28, 0x3f, 0x80, 0x38, 0x9e, 0x88, 0xc4, 0xe7, 0xcf, 0x9f, 0xfb, 0x9a,
0x61, 0x5b, 0xba, 0x74, 0xa9, 0x5b, 0x15, 0x9e, 0xe9, 0x4b, 0x96, 0x2c,
0xf1, 0x24, 0xe1, 0xe6, 0xcd, 0x9b, 0x9e, 0xf5, 0xd8, 0x05, 0x8a, 0xf1,
0x97, 0x98, 0x11, 0x3b, 0x99, 0xdc, 0xa0, 0xee, 0x53, 0xbc, 0x08, 0xec,
0x01, 0x95, 0x26, 0x5b, 0xb6, 0x6c, 0x71, 0x7d, 0x14, 0xab, 0x2d, 0x8d,
0xe2, 0xdb, 0x29, 0x9e, 0x3d, 0x7b, 0xe6, 0x59, 0xc5, 0x99, 0x33, 0x67,
0x3c, 0xcb, 0xb8, 0x15, 0x38, 0x77, 0x8e, 0xaf, 0xde, 0x29, 0x2c, 0xe7,
0xcf, 0x9f, 0x2f, 0x5c, 0xa0, 0xc4, 0x5c, 0x3e, 0xbe, 0x46, 0xec, 0x24,
0x4b, 0x0f, 0x06, 0x80, 0xd4, 0x17, 0x3e, 0x4c, 0x9d, 0x3a, 0x55, 0x8a,
0x0f, 0x77, 0xef, 0xde, 0xf5, 0x55, 0xef, 0xbd, 0x7b, 0xf7, 0x04, 0xba,
0x55, 0x5f, 0x65, 0xb3, 0x0b, 0xf1, 0x9c, 0x07, 0x0f, 0x1e, 0x64, 0x27,
0x39, 0xee, 0xb3, 0x7e, 0x59, 0x22, 0x0b, 0xbb, 0x2c, 0x7b, 0xcb, 0xa5,
0x07, 0x80, 0xac, 0xe1, 0xcf, 0xf1, 0xe3, 0xfd, 0xbd, 0xab, 0x02, 0xdd,
0xa8, 0x68, 0xd7, 0x8e, 0x6e, 0x16, 0x27, 0x3c, 0x67, 0xf4, 0xe8, 0xd1,
0x9e, 0x27, 0x8d, 0x19, 0x23, 0x6f, 0x10, 0x55, 0x16, 0x76, 0x59, 0x4e,
0xc9, 0x0f, 0x80, 0xac, 0xc6, 0x02, 0xdd, 0xad, 0xa8, 0xa8, 0x10, 0xc3,
0x87, 0x0f, 0xf7, 0xac, 0x73, 0xde, 0xbc, 0x79, 0x9e, 0x65, 0xdc, 0x0a,
0xcc, 0x9a, 0x35, 0xcb, 0x2d, 0xcb, 0x4a, 0xc7, 0xa2, 0x4f, 0x11, 0xc2,
0xb7, 0xb4, 0xa0, 0x0d, 0x6d, 0xcc, 0xb4, 0x7a, 0x7f, 0xa9, 0x6f, 0xfe,
0xb0, 0x2f, 0x80, 0x9c, 0xb6, 0xc5, 0x5c, 0x14, 0x39, 0x9d, 0x7f, 0xf2,
0xe4, 0xc9, 0x54, 0x59, 0x59, 0x99, 0xeb, 0x85, 0x12, 0xd7, 0xe2, 0xb5,
0xb4, 0xb4, 0x38, 0x9d, 0xea, 0x2b, 0xad, 0xa1, 0xa1, 0x21, 0xd5, 0xb7,
0x6f, 0x5f, 0xd7, 0xfa, 0x37, 0x6f, 0xde, 0xec, 0xab, 0x1e, 0xbb, 0x50,
0x29, 0xfe, 0x82, 0x60, 0xd7, 0xf6, 0x03, 0xc8, 0x7b, 0xc1, 0xbe, 0xb1,
0xb9, 0x8d, 0x51, 0x14, 0xd9, 0xe9, 0x1c, 0xf4, 0xd9, 0xbf, 0x7f, 0xbf,
0xc0, 0xab, 0x5d, 0x5a, 0xd9, 0x30, 0x63, 0xc6, 0x0c, 0x71, 0xec, 0xd8,
0x31, 0x81, 0xb5, 0x78, 0xad, 0xf2, 0xfc, 0x26, 0xf4, 0xe9, 0xd3, 0x47,
0x1c, 0x3d, 0x7a, 0x54, 0x8c, 0x1c, 0x39, 0x32, 0xe7, 0x14, 0x04, 0x9d,
0xd8, 0xb0, 0x61, 0x83, 0x58, 0xb7, 0x6e, 0x5d, 0x4e, 0x7a, 0x0c, 0x0f,
0x9a, 0x39, 0xf5, 0xc6, 0x00, 0xe8, 0x13, 0x43, 0xe3, 0x2d, 0x93, 0x17,
0x2f, 0x5e, 0x2c, 0xaa, 0xaa, 0xaa, 0xac, 0x87, 0x31, 0x2e, 0x5e, 0xbc,
0x28, 0x7a, 0xf5, 0xea, 0x25, 0x70, 0x0f, 0x2d, 0x66, 0xcf, 0x9e, 0x2d,
0x48, 0x54, 0x5b, 0x65, 0xd2, 0xa4, 0x49, 0xe2, 0xd2, 0xa5, 0x4b, 0xe2,
0xc8, 0x91, 0x23, 0x82, 0x57, 0xfc, 0x03, 0x07, 0x0e, 0x14, 0xfc, 0x69,
0xe0, 0xa8, 0xa0, 0x06, 0x62, 0x7d, 0xf9, 0xaf, 0xc2, 0x11, 0x69, 0xdd,
0x8c, 0xdd, 0xfd, 0x39, 0x6d, 0x4b, 0xe9, 0x12, 0x9d, 0xea, 0x89, 0x4b,
0x5a, 0x29, 0xfe, 0xca, 0xe4, 0x06, 0x75, 0x5f, 0xe5, 0x4f, 0xc0, 0x13,
0xa8, 0x91, 0x64, 0x22, 0xf0, 0x24, 0xd6, 0xd7, 0x00, 0xc9, 0xe4, 0x2c,
0x50, 0xaf, 0x9b, 0x19, 0x00, 0x8d, 0x81, 0x56, 0x69, 0x2a, 0x8b, 0x13,
0x02, 0x8d, 0x0c, 0x80, 0xdb, 0x71, 0xb2, 0xd8, 0xd8, 0x1a, 0x28, 0x02,
0xb7, 0x19, 0x00, 0xdf, 0x04, 0x5a, 0xa5, 0xa9, 0x2c, 0x4e, 0x08, 0x7c,
0xc3, 0x00, 0xe0, 0x7b, 0x00, 0x8d, 0x24, 0x13, 0x81, 0x4c, 0x00, 0xf0,
0x36, 0xd0, 0x48, 0xb2, 0x10, 0x20, 0xe7, 0xb7, 0xd8, 0x03, 0xb4, 0x40,
0xe5, 0x4d, 0x69, 0x25, 0x0b, 0xd4, 0x38, 0x79, 0x4b, 0xce, 0x5b, 0x18,
0x00, 0x94, 0x9b, 0xef, 0x36, 0xe6, 0x33, 0x41, 0x08, 0x58, 0x9c, 0x73,
0x28, 0x98, 0x72, 0x0a, 0x3a, 0xd7, 0xda, 0x8b, 0xd9, 0xc7, 0x9b, 0x37,
0x6f, 0x04, 0x46, 0x02, 0x5d, 0xad, 0xce, 0x7f, 0x18, 0x83, 0xf3, 0xfc,
0x85, 0xd6, 0x07, 0xb4, 0x6f, 0xdf, 0x3e, 0x67, 0x08, 0xf9, 0xf5, 0xeb,
0xd7, 0xae, 0x75, 0x73, 0xa8, 0x99, 0xe5, 0x63, 0x2a, 0xe4, 0x3c, 0xf3,
0x6c, 0xe0, 0xdf, 0x65, 0x39, 0xd1, 0xdc, 0x2c, 0x77, 0xae, 0x69, 0xf9,
0xf2, 0xe5, 0x82, 0x2f, 0x77, 0x72, 0xd3, 0xeb, 0xd7, 0xaf, 0xe7, 0xb8,
0xb6, 0x75, 0xeb, 0x56, 0xd7, 0xb2, 0xac, 0x63, 0xdf, 0xbe, 0x7d, 0x99,
0xf2, 0x3c, 0xd7, 0xad, 0x5e, 0xa6, 0xb3, 0x6d, 0x99, 0x22, 0x19, 0x3b,
0x8b, 0x73, 0xfb, 0x27, 0xe0, 0x2c, 0x1c, 0x91, 0xc2, 0xd4, 0xe5, 0xcb,
0x97, 0x65, 0x62, 0xa4, 0x75, 0xdd, 0x12, 0xb1, 0x23, 0xd7, 0xe4, 0x3c,
0xd3, 0x03, 0xb0, 0x9f, 0xfb, 0x27, 0x13, 0x82, 0x16, 0xcc, 0x99, 0x07,
0x5d, 0xa5, 0xd4, 0xfa, 0x0a, 0xfd, 0x9c, 0x48, 0x6d, 0xd8, 0xa1, 0x72,
0x89, 0xd8, 0x91, 0x6b, 0xeb, 0xb7, 0xcd, 0xee, 0x01, 0xd8, 0xbc, 0x94,
0x9f, 0x01, 0xce, 0xa7, 0xaf, 0x5a, 0xb5, 0xca, 0xd7, 0x02, 0x4e, 0x07,
0x0c, 0x42, 0x4f, 0x0a, 0x62, 0x0a, 0xb9, 0xad, 0x46, 0x73, 0xb1, 0x2b,
0x31, 0x23, 0x76, 0x92, 0x24, 0xc3, 0xb5, 0x7d, 0x11, 0xc8, 0x76, 0x32,
0x89, 0x41, 0x37, 0xba, 0x73, 0xe7, 0x4e, 0xb1, 0x77, 0xef, 0x5e, 0x6b,
0x9e, 0x3e, 0x7b, 0x9d, 0xdb, 0xe3, 0xc7, 0x7c, 0x87, 0x71, 0xdb, 0x64,
0xcd, 0x9a, 0x35, 0x62, 0xe1, 0xc2, 0x85, 0xae, 0x95, 0x0c, 0x19, 0x32,
0x24, 0x27, 0x6f, 0xd1, 0xa2, 0x45, 0x62, 0xd8, 0xb0, 0x61, 0x39, 0x69,
0xd9, 0x07, 0xd3, 0xa6, 0x4d, 0xcb, 0x1c, 0xf2, 0xdc, 0x3d, 0x7b, 0xf6,
0x64, 0x8e, 0xf3, 0x77, 0x82, 0x78, 0xde, 0x7f, 0xe3, 0xc6, 0x8d, 0x39,
0x6f, 0x44, 0xc1, 0x5b, 0xc5, 0x04, 0x57, 0x23, 0x07, 0x81, 0x4d, 0xbe,
0xbd, 0x59, 0xc7, 0xae, 0x5c, 0xdf, 0x40, 0x21, 0x69, 0x6b, 0x03, 0xfc,
0xd4, 0xcd, 0x39, 0x73, 0x5d, 0xc5, 0x69, 0x3d, 0x80, 0x1f, 0x4c, 0x02,
0x2e, 0x43, 0x8e, 0x33, 0x92, 0xfd, 0x13, 0xc0, 0xc4, 0x3f, 0x66, 0x72,
0xcc, 0x8e, 0xae, 0x08, 0xe4, 0x70, 0x9c, 0x1f, 0x00, 0x7f, 0x82, 0xd7,
0xc5, 0x2f, 0xa2, 0xd7, 0x15, 0x2a, 0xfd, 0xfc, 0x22, 0xb7, 0xe4, 0x38,
0x23, 0xf9, 0x01, 0x50, 0x87, 0x9c, 0xe3, 0x99, 0xdc, 0x08, 0x76, 0x0a,
0x0d, 0xbc, 0x44, 0x60, 0x4e, 0xa0, 0x4d, 0x2a, 0xf0, 0x44, 0x31, 0xb9,
0x25, 0xc7, 0x19, 0xc9, 0x0f, 0x00, 0x66, 0xe4, 0x74, 0x11, 0x99, 0x92,
0x21, 0xed, 0xe0, 0x0f, 0x1c, 0x43, 0x6a, 0x29, 0xfc, 0x66, 0x1e, 0x3d,
0x7a, 0x14, 0x7e, 0xa3, 0xb9, 0x2d, 0xb6, 0xe2, 0xd6, 0x29, 0x00, 0x0e,
0xe2, 0x1c, 0xfe, 0xb9, 0x40, 0x24, 0x22, 0x71, 0xf0, 0x23, 0x12, 0x7f,
0xb2, 0x1b, 0x8d, 0xd8, 0x37, 0x72, 0x4a, 0x6e, 0x73, 0xc4, 0x29, 0x00,
0x9e, 0xa3, 0xc4, 0xef, 0x72, 0x4a, 0x85, 0x78, 0x70, 0xe0, 0xc0, 0x81,
0x10, 0x5b, 0x0b, 0xb7, 0xa9, 0x88, 0x7d, 0x23, 0xa7, 0xe4, 0xd6, 0x97,
0xf4, 0x42, 0xa9, 0x26, 0x68, 0x24, 0xb7, 0x84, 0x18, 0x00, 0xd1, 0xee,
0x4e, 0x90, 0x3e, 0x45, 0x85, 0x67, 0x9a, 0x4b, 0x72, 0x5a, 0x94, 0xfc,
0x1a, 0xa5, 0x23, 0x31, 0x1a, 0x03, 0x35, 0x29, 0x3e, 0x96, 0xa5, 0x8b,
0xd0, 0x17, 0xfa, 0x14, 0x15, 0x9e, 0x68, 0x97, 0x5c, 0x16, 0x2d, 0xfc,
0x8f, 0x19, 0x4e, 0x1a, 0x44, 0x62, 0xf8, 0x94, 0x29, 0x53, 0x8a, 0x7e,
0x05, 0x8b, 0x8a, 0x01, 0xc3, 0xff, 0x0b, 0xa0, 0x2f, 0x51, 0xe1, 0x98,
0xe6, 0x90, 0x5c, 0x96, 0x24, 0x5b, 0x71, 0x56, 0x64, 0xc6, 0x63, 0x28,
0x36, 0x85, 0x21, 0xe4, 0x56, 0xaf, 0x96, 0x51, 0x91, 0xe8, 0x7c, 0x9b,
0xb0, 0xe6, 0xc0, 0xb2, 0x9d, 0x3e, 0x44, 0x89, 0x21, 0xda, 0x26, 0x87,
0xae, 0x52, 0xe6, 0x9a, 0xf3, 0x2e, 0x83, 0xff, 0xcc, 0xc4, 0x45, 0xa3,
0xad, 0x9f, 0xbe, 0xf4, 0x38, 0x31, 0xc8, 0x6c, 0xbc, 0x48, 0x49, 0x2c,
0x58, 0xb0, 0x40, 0xe0, 0x25, 0x4a, 0x25, 0xff, 0x59, 0x54, 0x90, 0xf6,
0x14, 0xaa, 0x8b, 0x7f, 0x1e, 0x85, 0xf7, 0x12, 0x89, 0xc3, 0x87, 0x0f,
0x0b, 0x99, 0x2f, 0x8f, 0x28, 0x64, 0x43, 0x56, 0x9e, 0xe7, 0x9f, 0x47,
0x67, 0x95, 0x75, 0xdd, 0xfd, 0x0c, 0x39, 0x51, 0x47, 0xb1, 0x69, 0xbf,
0x34, 0x0e, 0x96, 0xbb, 0xb2, 0x5a, 0x64, 0x46, 0xad, 0x09, 0x82, 0xd8,
0x7d, 0x09, 0xc8, 0x99, 0xa7, 0x78, 0xfd, 0x04, 0xd8, 0x15, 0x7c, 0x80,
0x9d, 0x2b, 0xd0, 0x4e, 0x76, 0x82, 0xd9, 0x2a, 0x8d, 0xc0, 0x4b, 0x58,
0xc7, 0xe7, 0xd7, 0x3d, 0x1f, 0xfa, 0xf1, 0xbb, 0xa2, 0xb1, 0x01, 0x95,
0xbd, 0x07, 0x9d, 0x01, 0x35, 0xa2, 0x3e, 0x02, 0xbc, 0xf0, 0xfb, 0x6b,
0xd0, 0x66, 0x32, 0x00, 0xa4, 0xbe, 0x4b, 0x00, 0xf5, 0x9b, 0xdf, 0xfa,
0xb6, 0x63, 0x40, 0x8e, 0xc8, 0x95, 0x14, 0xe1, 0x6b, 0xb3, 0x9e, 0x40,
0x0d, 0x51, 0x6a, 0x62, 0x40, 0x6e, 0xc8, 0x91, 0x54, 0xf9, 0x14, 0xb5,
0x9b, 0x00, 0x50, 0x13, 0x03, 0x72, 0x13, 0x8a, 0xfc, 0x1e, 0xad, 0x98,
0x20, 0x50, 0x0b, 0x03, 0x72, 0x12, 0x9a, 0xf0, 0x6e, 0x80, 0xef, 0x51,
0x35, 0x41, 0xa0, 0x06, 0x06, 0xe4, 0x22, 0xf4, 0x3b, 0x34, 0xfe, 0xe5,
0xc8, 0x5d, 0x13, 0x04, 0x91, 0x7f, 0x09, 0xc8, 0x01, 0xb9, 0x88, 0x44,
0x78, 0xc1, 0x51, 0x0f, 0x35, 0x3d, 0x41, 0x34, 0x18, 0x10, 0x7b, 0xe9,
0x17, 0x7d, 0x5e, 0x91, 0x35, 0x09, 0x05, 0xcc, 0x9d, 0x41, 0xf8, 0x01,
0x40, 0xcc, 0x89, 0xbd, 0x12, 0x52, 0x0d, 0x2b, 0xbe, 0x87, 0x9a, 0x9e,
0x20, 0x1c, 0x0c, 0x88, 0x35, 0x31, 0x57, 0x4a, 0x3e, 0x81, 0x35, 0x26,
0x08, 0xe4, 0x07, 0x00, 0x31, 0x26, 0xd6, 0x4a, 0x4a, 0x35, 0xac, 0x8a,
0x6c, 0x29, 0x19, 0xda, 0xd6, 0xbd, 0x07, 0x22, 0xb6, 0xc4, 0x58, 0x69,
0xe1, 0x8b, 0xfc, 0xef, 0x41, 0x75, 0x27, 0x23, 0x6c, 0xff, 0x88, 0xa9,
0xbf, 0x3f, 0x49, 0x50, 0x20, 0x3c, 0xde, 0x87, 0x0d, 0x91, 0x3f, 0x67,
0xa8, 0x51, 0x10, 0x12, 0x4b, 0x62, 0x1a, 0x2b, 0xe1, 0x6a, 0xa2, 0x5a,
0x68, 0xd8, 0xdf, 0x14, 0xdd, 0xda, 0x23, 0x86, 0xc4, 0x32, 0x96, 0xc2,
0xe9, 0xe6, 0x2f, 0xa0, 0x6f, 0xa0, 0xba, 0x11, 0x23, 0xdb, 0x1f, 0x62,
0x46, 0xec, 0xfc, 0x4e, 0xd9, 0xa3, 0xa8, 0xba, 0x52, 0x0d, 0xd3, 0xf8,
0x4c, 0x9a, 0x6c, 0xd0, 0x74, 0xa9, 0x9f, 0x58, 0x11, 0x33, 0xad, 0xa4,
0x2f, 0xbc, 0xf9, 0x0a, 0xaa, 0x0b, 0x49, 0xb2, 0xfc, 0x20, 0x46, 0xc4,
0x4a, 0x4b, 0x29, 0x83, 0x57, 0xab, 0xa0, 0xdf, 0x41, 0x65, 0x01, 0x18,
0xd7, 0x7a, 0x89, 0x09, 0xb1, 0x21, 0x46, 0xda, 0x0b, 0x23, 0xfc, 0x0f,
0xd0, 0xb7, 0xd0, 0xb8, 0x12, 0x16, 0x94, 0xdd, 0xc4, 0x80, 0x58, 0x68,
0xfb, 0xad, 0x87, 0x6f, 0xae, 0x52, 0x89, 0x9c, 0x24, 0x4f, 0x2b, 0xd3,
0x77, 0x62, 0x90, 0x68, 0xe1, 0x13, 0xca, 0x9f, 0x43, 0xbf, 0x85, 0x06,
0xf5, 0xad, 0x52, 0xbd, 0x1e, 0xfa, 0x4a, 0x9f, 0xe9, 0xbb, 0x91, 0x34,
0x02, 0xbc, 0xdd, 0xf9, 0x29, 0x94, 0xcb, 0xcf, 0x55, 0x27, 0xb0, 0x54,
0xfb, 0xe8, 0x1b, 0x7d, 0xd4, 0xe2, 0xd6, 0x0e, 0x7e, 0x48, 0x93, 0x8f,
0x51, 0xf3, 0x69, 0x68, 0xa9, 0x40, 0xab, 0x76, 0x1e, 0x7d, 0xa1, 0x4f,
0x46, 0x8a, 0x44, 0xe0, 0x23, 0x94, 0xff, 0x2d, 0x94, 0xff, 0xe2, 0xac,
0x1a, 0xa9, 0x5e, 0xf6, 0xd0, 0x66, 0xda, 0x4e, 0x1f, 0x8c, 0xb4, 0x11,
0x01, 0x76, 0x99, 0x35, 0xd0, 0xdd, 0xd0, 0xa7, 0x50, 0x2f, 0xf0, 0xa3,
0xca, 0xa7, 0x6d, 0xb4, 0x91, 0xb6, 0xc6, 0xa2, 0x9b, 0x8f, 0xe3, 0x3d,
0x67, 0x37, 0x80, 0x3b, 0x0b, 0x3a, 0x33, 0xad, 0x1f, 0x62, 0x1b, 0xd5,
0xc5, 0x14, 0x6f, 0xe1, 0xae, 0x42, 0x6b, 0xd3, 0x7a, 0x12, 0x5b, 0xfe,
0x01, 0x47, 0x6c, 0x24, 0x8e, 0x01, 0x90, 0x0f, 0x2e, 0x27, 0x4a, 0xaa,
0xa0, 0x0c, 0x08, 0xde, 0x52, 0x55, 0x40, 0x65, 0x4d, 0x9e, 0xf0, 0x2f,
0xf6, 0xf8, 0xb8, 0x3c, 0xdf, 0xb4, 0x4d, 0xd2, 0xbf, 0x86, 0x32, 0x2d,
0xb6, 0xa2, 0x43, 0x00, 0x38, 0x81, 0xcf, 0x41, 0x15, 0x06, 0x02, 0x17,
0x4c, 0x72, 0x3b, 0x02, 0xca, 0x77, 0xe4, 0x74, 0x87, 0x96, 0xa7, 0xd5,
0xde, 0xc7, 0xa1, 0xf5, 0x26, 0x14, 0x76, 0xdf, 0x7c, 0x23, 0x0a, 0x95,
0xfb, 0x5c, 0x7c, 0x71, 0x07, 0x4a, 0xc2, 0x6f, 0xa5, 0xb7, 0x7c, 0x46,
0x52, 0x2b, 0xf9, 0x3f, 0x92, 0xc9, 0x00, 0xb6, 0x61, 0xee, 0xab, 0xc9,
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
}

View File

@ -3,7 +3,9 @@
package model
import "github.com/mattermost/mattermost-server/v5/mlog"
import (
"github.com/mattermost/mattermost-server/v5/shared/mlog"
)
type BundleInfo struct {
Path string

View File

@ -34,23 +34,26 @@ const (
)
type Channel struct {
Id string `json:"id"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
TeamId string `json:"team_id"`
Type string `json:"type"`
DisplayName string `json:"display_name"`
Name string `json:"name"`
Header string `json:"header"`
Purpose string `json:"purpose"`
LastPostAt int64 `json:"last_post_at"`
TotalMsgCount int64 `json:"total_msg_count"`
ExtraUpdateAt int64 `json:"extra_update_at"`
CreatorId string `json:"creator_id"`
SchemeId *string `json:"scheme_id"`
Props map[string]interface{} `json:"props" db:"-"`
GroupConstrained *bool `json:"group_constrained"`
Id string `json:"id"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
TeamId string `json:"team_id"`
Type string `json:"type"`
DisplayName string `json:"display_name"`
Name string `json:"name"`
Header string `json:"header"`
Purpose string `json:"purpose"`
LastPostAt int64 `json:"last_post_at"`
TotalMsgCount int64 `json:"total_msg_count"`
ExtraUpdateAt int64 `json:"extra_update_at"`
CreatorId string `json:"creator_id"`
SchemeId *string `json:"scheme_id"`
Props map[string]interface{} `json:"props" db:"-"`
GroupConstrained *bool `json:"group_constrained"`
Shared *bool `json:"shared"`
TotalMsgCountRoot int64 `json:"total_msg_count_root"`
PolicyID *string `json:"policy_id" db:"-"`
}
type ChannelWithTeamData struct {
@ -120,18 +123,21 @@ type ChannelModeratedRolesPatch struct {
// PerPage number of results per page, if paginated.
//
type ChannelSearchOpts struct {
NotAssociatedToGroup string
ExcludeDefaultChannels bool
IncludeDeleted bool
Deleted bool
ExcludeChannelNames []string
TeamIds []string
GroupConstrained bool
ExcludeGroupConstrained bool
Public bool
Private bool
Page *int
PerPage *int
NotAssociatedToGroup string
ExcludeDefaultChannels bool
IncludeDeleted bool
Deleted bool
ExcludeChannelNames []string
TeamIds []string
GroupConstrained bool
ExcludeGroupConstrained bool
PolicyID string
ExcludePolicyConstrained bool
IncludePolicyID bool
Public bool
Private bool
Page *int
PerPage *int
}
type ChannelMemberCountByGroup struct {
@ -140,6 +146,14 @@ type ChannelMemberCountByGroup struct {
ChannelMemberTimezonesCount int64 `db:"-" json:"channel_member_timezones_count"`
}
type ChannelOption func(channel *Channel)
func WithID(ID string) ChannelOption {
return func(channel *Channel) {
channel.Id = ID
}
}
func (o *Channel) DeepCopy() *Channel {
copy := *o
if copy.SchemeId != nil {
@ -313,6 +327,10 @@ func (o *Channel) IsGroupConstrained() bool {
return o.GroupConstrained != nil && *o.GroupConstrained
}
func (o *Channel) IsShared() bool {
return o.Shared != nil && *o.Shared
}
func (o *Channel) GetOtherUserIdForDM(userId string) string {
if o.Type != CHANNEL_DIRECT {
return ""
@ -336,9 +354,8 @@ func (o *Channel) GetOtherUserIdForDM(userId string) string {
func GetDMNameFromIds(userId1, userId2 string) string {
if userId1 > userId2 {
return userId2 + "__" + userId1
} else {
return userId1 + "__" + userId2
}
return userId1 + "__" + userId2
}
func GetGroupDisplayNameFromUsers(users []*User, truncate bool) string {

View File

@ -14,11 +14,12 @@ import (
type ChannelCounts struct {
Counts map[string]int64 `json:"counts"`
CountsRoot map[string]int64 `json:"counts_root"`
UpdateTimes map[string]int64 `json:"update_times"`
}
func (o *ChannelCounts) Etag() string {
// we don't include CountsRoot in ETag calculation, since it's a deriviative
ids := []string{}
for id := range o.Counts {
ids = append(ids, id)

View File

@ -11,11 +11,11 @@ import (
type ChannelList []*Channel
func (o *ChannelList) ToJson() string {
if b, err := json.Marshal(o); err != nil {
b, err := json.Marshal(o)
if err != nil {
return "[]"
} else {
return string(b)
}
return string(b)
}
func (o *ChannelList) Etag() string {
@ -55,11 +55,11 @@ func ChannelSliceFromJson(data io.Reader) []*Channel {
type ChannelListWithTeamData []*ChannelWithTeamData
func (o *ChannelListWithTeamData) ToJson() string {
if b, err := json.Marshal(o); err != nil {
b, err := json.Marshal(o)
if err != nil {
return "[]"
} else {
return string(b)
}
return string(b)
}
func (o *ChannelListWithTeamData) Etag() string {

View File

@ -24,36 +24,42 @@ const (
)
type ChannelUnread struct {
TeamId string `json:"team_id"`
ChannelId string `json:"channel_id"`
MsgCount int64 `json:"msg_count"`
MentionCount int64 `json:"mention_count"`
NotifyProps StringMap `json:"-"`
TeamId string `json:"team_id"`
ChannelId string `json:"channel_id"`
MsgCount int64 `json:"msg_count"`
MentionCount int64 `json:"mention_count"`
MentionCountRoot int64 `json:"mention_count_root"`
MsgCountRoot int64 `json:"msg_count_root"`
NotifyProps StringMap `json:"-"`
}
type ChannelUnreadAt struct {
TeamId string `json:"team_id"`
UserId string `json:"user_id"`
ChannelId string `json:"channel_id"`
MsgCount int64 `json:"msg_count"`
MentionCount int64 `json:"mention_count"`
LastViewedAt int64 `json:"last_viewed_at"`
NotifyProps StringMap `json:"-"`
TeamId string `json:"team_id"`
UserId string `json:"user_id"`
ChannelId string `json:"channel_id"`
MsgCount int64 `json:"msg_count"`
MentionCount int64 `json:"mention_count"`
MentionCountRoot int64 `json:"mention_count_root"`
MsgCountRoot int64 `json:"msg_count_root"`
LastViewedAt int64 `json:"last_viewed_at"`
NotifyProps StringMap `json:"-"`
}
type ChannelMember struct {
ChannelId string `json:"channel_id"`
UserId string `json:"user_id"`
Roles string `json:"roles"`
LastViewedAt int64 `json:"last_viewed_at"`
MsgCount int64 `json:"msg_count"`
MentionCount int64 `json:"mention_count"`
NotifyProps StringMap `json:"notify_props"`
LastUpdateAt int64 `json:"last_update_at"`
SchemeGuest bool `json:"scheme_guest"`
SchemeUser bool `json:"scheme_user"`
SchemeAdmin bool `json:"scheme_admin"`
ExplicitRoles string `json:"explicit_roles"`
ChannelId string `json:"channel_id"`
UserId string `json:"user_id"`
Roles string `json:"roles"`
LastViewedAt int64 `json:"last_viewed_at"`
MsgCount int64 `json:"msg_count"`
MentionCount int64 `json:"mention_count"`
MentionCountRoot int64 `json:"mention_count_root"`
MsgCountRoot int64 `json:"msg_count_root"`
NotifyProps StringMap `json:"notify_props"`
LastUpdateAt int64 `json:"last_update_at"`
SchemeGuest bool `json:"scheme_guest"`
SchemeUser bool `json:"scheme_user"`
SchemeAdmin bool `json:"scheme_admin"`
ExplicitRoles string `json:"explicit_roles"`
}
type ChannelMembers []ChannelMember
@ -65,11 +71,11 @@ type ChannelMemberForExport struct {
}
func (o *ChannelMembers) ToJson() string {
if b, err := json.Marshal(o); err != nil {
b, err := json.Marshal(o)
if err != nil {
return "[]"
} else {
return string(b)
}
return string(b)
}
func (o *ChannelUnread) ToJson() string {

View File

@ -11,18 +11,19 @@ import (
const CHANNEL_SEARCH_DEFAULT_LIMIT = 50
type ChannelSearch struct {
Term string `json:"term"`
ExcludeDefaultChannels bool `json:"exclude_default_channels"`
NotAssociatedToGroup string `json:"not_associated_to_group"`
TeamIds []string `json:"team_ids"`
GroupConstrained bool `json:"group_constrained"`
ExcludeGroupConstrained bool `json:"exclude_group_constrained"`
Public bool `json:"public"`
Private bool `json:"private"`
IncludeDeleted bool `json:"include_deleted"`
Deleted bool `json:"deleted"`
Page *int `json:"page,omitempty"`
PerPage *int `json:"per_page,omitempty"`
Term string `json:"term"`
ExcludeDefaultChannels bool `json:"exclude_default_channels"`
NotAssociatedToGroup string `json:"not_associated_to_group"`
TeamIds []string `json:"team_ids"`
GroupConstrained bool `json:"group_constrained"`
ExcludeGroupConstrained bool `json:"exclude_group_constrained"`
ExcludePolicyConstrained bool `json:"exclude_policy_constrained"`
Public bool `json:"public"`
Private bool `json:"private"`
IncludeDeleted bool `json:"include_deleted"`
Deleted bool `json:"deleted"`
Page *int `json:"page,omitempty"`
PerPage *int `json:"per_page,omitempty"`
}
// ToJson convert a Channel to a json string

View File

@ -47,6 +47,7 @@ type SidebarCategory struct {
Type SidebarCategoryType `json:"type"`
DisplayName string `json:"display_name"`
Muted bool `json:"muted"`
Collapsed bool `json:"collapsed"`
}
// SidebarCategoryWithChannels combines data from SidebarCategory table with the Channel IDs that belong to that category
@ -97,19 +98,19 @@ func (o SidebarCategoryWithChannels) ToJson() []byte {
}
func SidebarCategoriesWithChannelsToJson(o []*SidebarCategoryWithChannels) []byte {
if b, err := json.Marshal(o); err != nil {
b, err := json.Marshal(o)
if err != nil {
return []byte("[]")
} else {
return b
}
return b
}
func (o OrderedSidebarCategories) ToJson() []byte {
if b, err := json.Marshal(o); err != nil {
b, err := json.Marshal(o)
if err != nil {
return []byte("[]")
} else {
return b
}
return b
}
var categoryIdPattern = regexp.MustCompile("(favorites|channels|direct_messages)_[a-z0-9]{26}_[a-z0-9]{26}")

View File

@ -9,8 +9,9 @@ import (
)
type ChannelView struct {
ChannelId string `json:"channel_id"`
PrevChannelId string `json:"prev_channel_id"`
ChannelId string `json:"channel_id"`
PrevChannelId string `json:"prev_channel_id"`
CollapsedThreadsSupported bool `json:"collapsed_threads_supported"`
}
func (o *ChannelView) ToJson() string {

View File

@ -19,25 +19,29 @@ import (
)
const (
HEADER_REQUEST_ID = "X-Request-ID"
HEADER_VERSION_ID = "X-Version-ID"
HEADER_CLUSTER_ID = "X-Cluster-ID"
HEADER_ETAG_SERVER = "ETag"
HEADER_ETAG_CLIENT = "If-None-Match"
HEADER_FORWARDED = "X-Forwarded-For"
HEADER_REAL_IP = "X-Real-IP"
HEADER_FORWARDED_PROTO = "X-Forwarded-Proto"
HEADER_TOKEN = "token"
HEADER_CSRF_TOKEN = "X-CSRF-Token"
HEADER_BEARER = "BEARER"
HEADER_AUTH = "Authorization"
HEADER_REQUESTED_WITH = "X-Requested-With"
HEADER_REQUESTED_WITH_XML = "XMLHttpRequest"
STATUS = "status"
STATUS_OK = "OK"
STATUS_FAIL = "FAIL"
STATUS_UNHEALTHY = "UNHEALTHY"
STATUS_REMOVE = "REMOVE"
HEADER_REQUEST_ID = "X-Request-ID"
HEADER_VERSION_ID = "X-Version-ID"
HEADER_CLUSTER_ID = "X-Cluster-ID"
HEADER_ETAG_SERVER = "ETag"
HEADER_ETAG_CLIENT = "If-None-Match"
HEADER_FORWARDED = "X-Forwarded-For"
HEADER_REAL_IP = "X-Real-IP"
HEADER_FORWARDED_PROTO = "X-Forwarded-Proto"
HEADER_TOKEN = "token"
HEADER_CSRF_TOKEN = "X-CSRF-Token"
HEADER_BEARER = "BEARER"
HEADER_AUTH = "Authorization"
HEADER_CLOUD_TOKEN = "X-Cloud-Token"
HEADER_REMOTECLUSTER_TOKEN = "X-RemoteCluster-Token"
HEADER_REMOTECLUSTER_ID = "X-RemoteCluster-Id"
HEADER_REQUESTED_WITH = "X-Requested-With"
HEADER_REQUESTED_WITH_XML = "XMLHttpRequest"
HEADER_RANGE = "Range"
STATUS = "status"
STATUS_OK = "OK"
STATUS_FAIL = "FAIL"
STATUS_UNHEALTHY = "UNHEALTHY"
STATUS_REMOVE = "REMOVE"
CLIENT_DIR = "client"
@ -93,9 +97,8 @@ func (c *Client4) boolString(value bool) string {
if value {
return "true"
} else {
return "false"
}
return "false"
}
func closeBody(r *http.Response) {
@ -189,12 +192,12 @@ func (c *Client4) GetUserRoute(userId string) string {
return fmt.Sprintf(c.GetUsersRoute()+"/%v", userId)
}
func (c *Client4) GetUserThreadsRoute(userId string) string {
return fmt.Sprintf(c.GetUsersRoute()+"/%v/threads", userId)
func (c *Client4) GetUserThreadsRoute(userID, teamID string) string {
return c.GetUserRoute(userID) + c.GetTeamRoute(teamID) + "/threads"
}
func (c *Client4) GetUserThreadRoute(userId, threadId string) string {
return fmt.Sprintf(c.GetUserThreadsRoute(userId)+"/%v", threadId)
func (c *Client4) GetUserThreadRoute(userId, teamId, threadId string) string {
return c.GetUserThreadsRoute(userId, teamId) + "/" + threadId
}
func (c *Client4) GetUserCategoryRoute(userID, teamID string) string {
@ -383,7 +386,11 @@ func (c *Client4) GetComplianceReportsRoute() string {
}
func (c *Client4) GetComplianceReportRoute(reportId string) string {
return fmt.Sprintf("/compliance/reports/%v", reportId)
return fmt.Sprintf("%s/%s", c.GetComplianceReportsRoute(), reportId)
}
func (c *Client4) GetComplianceReportDownloadRoute(reportId string) string {
return fmt.Sprintf("%s/%s/download", c.GetComplianceReportsRoute(), reportId)
}
func (c *Client4) GetOutgoingWebhooksRoute() string {
@ -422,6 +429,10 @@ func (c *Client4) GetDataRetentionRoute() string {
return "/data_retention"
}
func (c *Client4) GetDataRetentionPolicyRoute(policyID string) string {
return fmt.Sprintf(c.GetDataRetentionRoute()+"/policies/%v", policyID)
}
func (c *Client4) GetElasticsearchRoute() string {
return "/elasticsearch"
}
@ -542,6 +553,30 @@ func (c *Client4) GetGroupSyncablesRoute(groupID string, syncableType GroupSynca
return fmt.Sprintf("%s/%ss", c.GetGroupRoute(groupID), strings.ToLower(syncableType.String()))
}
func (c *Client4) GetImportsRoute() string {
return "/imports"
}
func (c *Client4) GetExportsRoute() string {
return "/exports"
}
func (c *Client4) GetExportRoute(name string) string {
return fmt.Sprintf(c.GetExportsRoute()+"/%v", name)
}
func (c *Client4) GetRemoteClusterRoute() string {
return "/remotecluster"
}
func (c *Client4) GetSharedChannelsRoute() string {
return "/sharedchannels"
}
func (c *Client4) GetPermissionsRoute() string {
return "/permissions"
}
func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) {
return c.DoApiRequest(http.MethodGet, c.ApiUrl+url, "", etag)
}
@ -550,6 +585,14 @@ func (c *Client4) DoApiPost(url string, data string) (*http.Response, *AppError)
return c.DoApiRequest(http.MethodPost, c.ApiUrl+url, data, "")
}
func (c *Client4) doApiDeleteBytes(url string, data []byte) (*http.Response, *AppError) {
return c.doApiRequestBytes(http.MethodDelete, c.ApiUrl+url, data, "")
}
func (c *Client4) doApiPatchBytes(url string, data []byte) (*http.Response, *AppError) {
return c.doApiRequestBytes(http.MethodPatch, c.ApiUrl+url, data, "")
}
func (c *Client4) doApiPostBytes(url string, data []byte) (*http.Response, *AppError) {
return c.doApiRequestBytes(http.MethodPost, c.ApiUrl+url, data, "")
}
@ -567,24 +610,28 @@ func (c *Client4) DoApiDelete(url string) (*http.Response, *AppError) {
}
func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response, *AppError) {
return c.doApiRequestReader(method, url, strings.NewReader(data), etag)
return c.doApiRequestReader(method, url, strings.NewReader(data), map[string]string{HEADER_ETAG_CLIENT: etag})
}
func (c *Client4) DoApiRequestWithHeaders(method, url, data string, headers map[string]string) (*http.Response, *AppError) {
return c.doApiRequestReader(method, url, strings.NewReader(data), headers)
}
func (c *Client4) doApiRequestBytes(method, url string, data []byte, etag string) (*http.Response, *AppError) {
return c.doApiRequestReader(method, url, bytes.NewReader(data), etag)
return c.doApiRequestReader(method, url, bytes.NewReader(data), map[string]string{HEADER_ETAG_CLIENT: etag})
}
func (c *Client4) doApiRequestReader(method, url string, data io.Reader, etag string) (*http.Response, *AppError) {
func (c *Client4) doApiRequestReader(method, url string, data io.Reader, headers map[string]string) (*http.Response, *AppError) {
rq, err := http.NewRequest(method, url, data)
if err != nil {
return nil, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)
}
if len(etag) > 0 {
rq.Header.Set(HEADER_ETAG_CLIENT, etag)
for k, v := range headers {
rq.Header.Set(k, v)
}
if len(c.AuthToken) > 0 {
if c.AuthToken != "" {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
}
@ -625,7 +672,7 @@ func (c *Client4) doUploadFile(url string, body io.Reader, contentType string, c
}
rq.Header.Set("Content-Type", contentType)
if len(c.AuthToken) > 0 {
if c.AuthToken != "" {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
}
@ -649,7 +696,7 @@ func (c *Client4) DoEmojiUploadFile(url string, data []byte, contentType string)
}
rq.Header.Set("Content-Type", contentType)
if len(c.AuthToken) > 0 {
if c.AuthToken != "" {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
}
@ -673,7 +720,7 @@ func (c *Client4) DoUploadImportTeam(url string, data []byte, contentType string
}
rq.Header.Set("Content-Type", contentType)
if len(c.AuthToken) > 0 {
if c.AuthToken != "" {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
}
@ -1393,14 +1440,20 @@ func (c *Client4) AttachDeviceId(deviceId string) (bool, *Response) {
// GetTeamsUnreadForUser will return an array with TeamUnread objects that contain the amount
// of unread messages and mentions the current user has for the teams it belongs to.
// An optional team ID can be set to exclude that team from the results. Must be authenticated.
func (c *Client4) GetTeamsUnreadForUser(userId, teamIdToExclude string) ([]*TeamUnread, *Response) {
var optional string
// An optional team ID can be set to exclude that team from the results.
// An optional boolean can be set to include collapsed thread unreads. Must be authenticated.
func (c *Client4) GetTeamsUnreadForUser(userId, teamIdToExclude string, includeCollapsedThreads bool) ([]*TeamUnread, *Response) {
query := url.Values{}
if teamIdToExclude != "" {
optional += fmt.Sprintf("?exclude_team=%s", url.QueryEscape(teamIdToExclude))
query.Set("exclude_team", teamIdToExclude)
}
r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams/unread"+optional, "")
if includeCollapsedThreads {
query.Set("include_collapsed_threads", "true")
}
r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams/unread?"+query.Encode(), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@ -1486,7 +1539,7 @@ func (c *Client4) SetProfileImage(userId string, data []byte) (bool, *Response)
}
rq.Header.Set("Content-Type", writer.FormDataContentType())
if len(c.AuthToken) > 0 {
if c.AuthToken != "" {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
}
@ -1735,7 +1788,7 @@ func (c *Client4) SetBotIconImage(botUserId string, data []byte) (bool, *Respons
}
rq.Header.Set("Content-Type", writer.FormDataContentType())
if len(c.AuthToken) > 0 {
if c.AuthToken != "" {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
}
@ -1822,6 +1875,18 @@ func (c *Client4) GetAllTeamsWithTotalCount(etag string, page int, perPage int)
return teamsListWithCount.Teams, teamsListWithCount.TotalCount, BuildResponse(r)
}
// GetAllTeamsExcludePolicyConstrained returns all teams which are not part of a data retention policy.
// Must be a system administrator.
func (c *Client4) GetAllTeamsExcludePolicyConstrained(etag string, page int, perPage int) ([]*Team, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&exclude_policy_constrained=%v", page, perPage, true)
r, err := c.DoApiGet(c.GetTeamsRoute()+query, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return TeamListFromJson(r.Body), BuildResponse(r)
}
// GetTeamByName returns a team based on the provided team name string.
func (c *Client4) GetTeamByName(name, etag string) (*Team, *Response) {
r, err := c.DoApiGet(c.GetTeamByNameRoute(name), etag)
@ -2269,7 +2334,7 @@ func (c *Client4) SetTeamIcon(teamId string, data []byte) (bool, *Response) {
}
rq.Header.Set("Content-Type", writer.FormDataContentType())
if len(c.AuthToken) > 0 {
if c.AuthToken != "" {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
}
@ -2316,16 +2381,23 @@ func (c *Client4) RemoveTeamIcon(teamId string) (bool, *Response) {
// GetAllChannels get all the channels. Must be a system administrator.
func (c *Client4) GetAllChannels(page int, perPage int, etag string) (*ChannelListWithTeamData, *Response) {
return c.getAllChannels(page, perPage, etag, false)
return c.getAllChannels(page, perPage, etag, ChannelSearchOpts{})
}
// GetAllChannelsIncludeDeleted get all the channels. Must be a system administrator.
func (c *Client4) GetAllChannelsIncludeDeleted(page int, perPage int, etag string) (*ChannelListWithTeamData, *Response) {
return c.getAllChannels(page, perPage, etag, true)
return c.getAllChannels(page, perPage, etag, ChannelSearchOpts{IncludeDeleted: true})
}
func (c *Client4) getAllChannels(page int, perPage int, etag string, includeDeleted bool) (*ChannelListWithTeamData, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&include_deleted=%v", page, perPage, includeDeleted)
// GetAllChannelsExcludePolicyConstrained gets all channels which are not part of a data retention policy.
// Must be a system administrator.
func (c *Client4) GetAllChannelsExcludePolicyConstrained(page, perPage int, etag string) (*ChannelListWithTeamData, *Response) {
return c.getAllChannels(page, perPage, etag, ChannelSearchOpts{ExcludePolicyConstrained: true})
}
func (c *Client4) getAllChannels(page int, perPage int, etag string, opts ChannelSearchOpts) (*ChannelListWithTeamData, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&include_deleted=%v&exclude_policy_constrained=%v",
page, perPage, opts.IncludeDeleted, opts.ExcludePolicyConstrained)
r, err := c.DoApiGet(c.GetChannelsRoute()+query, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
@ -2849,8 +2921,9 @@ func (c *Client4) PatchPost(postId string, patch *PostPatch) (*Post, *Response)
}
// SetPostUnread marks channel where post belongs as unread on the time of the provided post.
func (c *Client4) SetPostUnread(userId string, postId string) *Response {
r, err := c.DoApiPost(c.GetUserRoute(userId)+c.GetPostRoute(postId)+"/set_unread", "")
func (c *Client4) SetPostUnread(userId string, postId string, collapsedThreadsSupported bool) *Response {
b, _ := json.Marshal(map[string]bool{"collapsed_threads_supported": collapsedThreadsSupported})
r, err := c.DoApiPost(c.GetUserRoute(userId)+c.GetPostRoute(postId)+"/set_unread", string(b))
if err != nil {
return BuildErrorResponse(r, err)
}
@ -2899,8 +2972,12 @@ func (c *Client4) DeletePost(postId string) (bool, *Response) {
}
// GetPostThread gets a post with all the other posts in the same thread.
func (c *Client4) GetPostThread(postId string, etag string) (*PostList, *Response) {
r, err := c.DoApiGet(c.GetPostRoute(postId)+"/thread", etag)
func (c *Client4) GetPostThread(postId string, etag string, collapsedThreads bool) (*PostList, *Response) {
url := c.GetPostRoute(postId) + "/thread"
if collapsedThreads {
url += "?collapsedThreads=true"
}
r, err := c.DoApiGet(url, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@ -2909,8 +2986,11 @@ func (c *Client4) GetPostThread(postId string, etag string) (*PostList, *Respons
}
// GetPostsForChannel gets a page of posts with an array for ordering for a channel.
func (c *Client4) GetPostsForChannel(channelId string, page, perPage int, etag string) (*PostList, *Response) {
func (c *Client4) GetPostsForChannel(channelId string, page, perPage int, etag string, collapsedThreads bool) (*PostList, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
if collapsedThreads {
query += "&collapsedThreads=true"
}
r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
@ -2961,8 +3041,11 @@ func (c *Client4) GetFlaggedPostsForUserInChannel(userId string, channelId strin
}
// GetPostsSince gets posts created after a specified time as Unix time in milliseconds.
func (c *Client4) GetPostsSince(channelId string, time int64) (*PostList, *Response) {
func (c *Client4) GetPostsSince(channelId string, time int64, collapsedThreads bool) (*PostList, *Response) {
query := fmt.Sprintf("?since=%v", time)
if collapsedThreads {
query += "&collapsedThreads=true"
}
r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
@ -2972,8 +3055,11 @@ func (c *Client4) GetPostsSince(channelId string, time int64) (*PostList, *Respo
}
// GetPostsAfter gets a page of posts that were posted after the post provided.
func (c *Client4) GetPostsAfter(channelId, postId string, page, perPage int, etag string) (*PostList, *Response) {
func (c *Client4) GetPostsAfter(channelId, postId string, page, perPage int, etag string, collapsedThreads bool) (*PostList, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&after=%v", page, perPage, postId)
if collapsedThreads {
query += "&collapsedThreads=true"
}
r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
@ -2983,8 +3069,11 @@ func (c *Client4) GetPostsAfter(channelId, postId string, page, perPage int, eta
}
// GetPostsBefore gets a page of posts that were posted before the post provided.
func (c *Client4) GetPostsBefore(channelId, postId string, page, perPage int, etag string) (*PostList, *Response) {
func (c *Client4) GetPostsBefore(channelId, postId string, page, perPage int, etag string, collapsedThreads bool) (*PostList, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&before=%v", page, perPage, postId)
if collapsedThreads {
query += "&collapsedThreads=true"
}
r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
@ -2994,14 +3083,36 @@ func (c *Client4) GetPostsBefore(channelId, postId string, page, perPage int, et
}
// GetPostsAroundLastUnread gets a list of posts around last unread post by a user in a channel.
func (c *Client4) GetPostsAroundLastUnread(userId, channelId string, limitBefore, limitAfter int) (*PostList, *Response) {
func (c *Client4) GetPostsAroundLastUnread(userId, channelId string, limitBefore, limitAfter int, collapsedThreads bool) (*PostList, *Response) {
query := fmt.Sprintf("?limit_before=%v&limit_after=%v", limitBefore, limitAfter)
if r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetChannelRoute(channelId)+"/posts/unread"+query, ""); err != nil {
return nil, BuildErrorResponse(r, err)
} else {
defer closeBody(r)
return PostListFromJson(r.Body), BuildResponse(r)
if collapsedThreads {
query += "&collapsedThreads=true"
}
r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetChannelRoute(channelId)+"/posts/unread"+query, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return PostListFromJson(r.Body), BuildResponse(r)
}
// SearchFiles returns any posts with matching terms string.
func (c *Client4) SearchFiles(teamId string, terms string, isOrSearch bool) (*FileInfoList, *Response) {
params := SearchParameter{
Terms: &terms,
IsOrSearch: &isOrSearch,
}
return c.SearchFilesWithParams(teamId, &params)
}
// SearchFilesWithParams returns any posts with matching terms string.
func (c *Client4) SearchFilesWithParams(teamId string, params *SearchParameter) (*FileInfoList, *Response) {
r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/files/search", params.SearchParameterToJson())
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return FileInfoListFromJson(r.Body), BuildResponse(r)
}
// SearchPosts returns any posts with matching terms string.
@ -3251,6 +3362,21 @@ func (c *Client4) GetFileInfosForPost(postId string, etag string) ([]*FileInfo,
// General/System Section
// GenerateSupportPacket downloads the generated support packet
func (c *Client4) GenerateSupportPacket() ([]byte, *Response) {
r, appErr := c.DoApiGet(c.GetSystemRoute()+"/support_packet", "")
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
data, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, NewAppError("GetFile", "model.client.read_job_result_file.app_error", nil, err.Error(), r.StatusCode))
}
return data, BuildResponse(r)
}
// GetPing will return ok if the running goRoutines are below the threshold and unhealthy for above.
func (c *Client4) GetPing() (string, *Response) {
r, err := c.DoApiGet(c.GetSystemRoute()+"/ping", "")
@ -3448,7 +3574,7 @@ func (c *Client4) UploadLicenseFile(data []byte) (bool, *Response) {
}
rq.Header.Set("Content-Type", writer.FormDataContentType())
if len(c.AuthToken) > 0 {
if c.AuthToken != "" {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
}
@ -3815,6 +3941,28 @@ func (c *Client4) GetSamlMetadataFromIdp(samlMetadataURL string) (*SamlMetadataR
return SamlMetadataResponseFromJson(r.Body), BuildResponse(r)
}
// ResetSamlAuthDataToEmail resets the AuthData field of SAML users to their Email.
func (c *Client4) ResetSamlAuthDataToEmail(includeDeleted bool, dryRun bool, userIDs []string) (int64, *Response) {
params := map[string]interface{}{
"include_deleted": includeDeleted,
"dry_run": dryRun,
"user_ids": userIDs,
}
b, _ := json.Marshal(params)
r, err := c.doApiPostBytes(c.GetSamlRoute()+"/reset_auth_data", b)
if err != nil {
return 0, BuildErrorResponse(r, err)
}
defer closeBody(r)
respBody := map[string]int64{}
jsonErr := json.NewDecoder(r.Body).Decode(&respBody)
if jsonErr != nil {
appErr := NewAppError("Api4.ResetSamlAuthDataToEmail", "api.marshal_error", nil, err.Error(), http.StatusInternalServerError)
return 0, BuildErrorResponse(r, appErr)
}
return respBody["num_affected"], BuildResponse(r)
}
// Compliance Section
// CreateComplianceReport creates an incoming webhook for a channel.
@ -3850,12 +3998,12 @@ func (c *Client4) GetComplianceReport(reportId string) (*Compliance, *Response)
// DownloadComplianceReport returns a full compliance report as a file.
func (c *Client4) DownloadComplianceReport(reportId string) ([]byte, *Response) {
rq, err := http.NewRequest("GET", c.ApiUrl+c.GetComplianceReportRoute(reportId), nil)
rq, err := http.NewRequest("GET", c.ApiUrl+c.GetComplianceReportDownloadRoute(reportId), nil)
if err != nil {
return nil, &Response{Error: NewAppError("DownloadComplianceReport", "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)}
}
if len(c.AuthToken) > 0 {
if c.AuthToken != "" {
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
}
@ -3892,8 +4040,13 @@ func (c *Client4) GetClusterStatus() ([]*ClusterInfo, *Response) {
// LDAP Section
// SyncLdap will force a sync with the configured LDAP server.
func (c *Client4) SyncLdap() (bool, *Response) {
r, err := c.DoApiPost(c.GetLdapRoute()+"/sync", "")
// If includeRemovedMembers is true, then group members who left or were removed from a
// synced team/channel will be re-joined; otherwise, they will be excluded.
func (c *Client4) SyncLdap(includeRemovedMembers bool) (bool, *Response) {
reqBody, _ := json.Marshal(map[string]interface{}{
"include_removed_members": includeRemovedMembers,
})
r, err := c.doApiPostBytes(c.GetLdapRoute()+"/sync", reqBody)
if err != nil {
return false, BuildErrorResponse(r, err)
}
@ -4225,7 +4378,7 @@ func (c *Client4) UploadBrandImage(data []byte) (bool, *Response) {
}
rq.Header.Set("Content-Type", writer.FormDataContentType())
if len(c.AuthToken) > 0 {
if c.AuthToken != "" {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
}
@ -4380,7 +4533,7 @@ func (c *Client4) GetOAuthAccessToken(data url.Values) (*AccessResponse, *Respon
}
rq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if len(c.AuthToken) > 0 {
if c.AuthToken != "" {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
}
@ -4434,14 +4587,236 @@ func (c *Client4) PurgeBleveIndexes() (bool, *Response) {
// Data Retention Section
// GetDataRetentionPolicy will get the current server data retention policy details.
func (c *Client4) GetDataRetentionPolicy() (*DataRetentionPolicy, *Response) {
// GetDataRetentionPolicy will get the current global data retention policy details.
func (c *Client4) GetDataRetentionPolicy() (*GlobalRetentionPolicy, *Response) {
r, err := c.DoApiGet(c.GetDataRetentionRoute()+"/policy", "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return DataRetentionPolicyFromJson(r.Body), BuildResponse(r)
return GlobalRetentionPolicyFromJson(r.Body), BuildResponse(r)
}
// GetDataRetentionPolicyByID will get the details for the granular data retention policy with the specified ID.
func (c *Client4) GetDataRetentionPolicyByID(policyID string) (*RetentionPolicyWithTeamAndChannelCounts, *Response) {
r, appErr := c.DoApiGet(c.GetDataRetentionPolicyRoute(policyID), "")
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
policy, err := RetentionPolicyWithTeamAndChannelCountsFromJson(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.GetDataRetentionPolicyByID", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode))
}
return policy, BuildResponse(r)
}
// GetDataRetentionPoliciesCount will get the total number of granular data retention policies.
func (c *Client4) GetDataRetentionPoliciesCount() (int64, *Response) {
type CountBody struct {
TotalCount int64 `json:"total_count"`
}
r, appErr := c.DoApiGet(c.GetDataRetentionRoute()+"/policies_count", "")
if appErr != nil {
return 0, BuildErrorResponse(r, appErr)
}
var countObj CountBody
jsonErr := json.NewDecoder(r.Body).Decode(&countObj)
if jsonErr != nil {
return 0, BuildErrorResponse(r, NewAppError("Client4.GetDataRetentionPoliciesCount", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode))
}
return countObj.TotalCount, BuildResponse(r)
}
// GetDataRetentionPolicies will get the current granular data retention policies' details.
func (c *Client4) GetDataRetentionPolicies(page, perPage int) (*RetentionPolicyWithTeamAndChannelCountsList, *Response) {
query := fmt.Sprintf("?page=%d&per_page=%d", page, perPage)
r, appErr := c.DoApiGet(c.GetDataRetentionRoute()+"/policies"+query, "")
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
policies, err := RetentionPolicyWithTeamAndChannelCountsListFromJson(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.GetDataRetentionPolicies", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode))
}
return policies, BuildResponse(r)
}
// CreateDataRetentionPolicy will create a new granular data retention policy which will be applied to
// the specified teams and channels. The Id field of `policy` must be empty.
func (c *Client4) CreateDataRetentionPolicy(policy *RetentionPolicyWithTeamAndChannelIDs) (*RetentionPolicyWithTeamAndChannelCounts, *Response) {
r, appErr := c.doApiPostBytes(c.GetDataRetentionRoute()+"/policies", policy.ToJson())
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
newPolicy, err := RetentionPolicyWithTeamAndChannelCountsFromJson(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.CreateDataRetentionPolicy", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode))
}
return newPolicy, BuildResponse(r)
}
// DeleteDataRetentionPolicy will delete the granular data retention policy with the specified ID.
func (c *Client4) DeleteDataRetentionPolicy(policyID string) *Response {
r, appErr := c.DoApiDelete(c.GetDataRetentionPolicyRoute(policyID))
if appErr != nil {
return BuildErrorResponse(r, appErr)
}
defer closeBody(r)
return BuildResponse(r)
}
// PatchDataRetentionPolicy will patch the granular data retention policy with the specified ID.
// The Id field of `patch` must be non-empty.
func (c *Client4) PatchDataRetentionPolicy(patch *RetentionPolicyWithTeamAndChannelIDs) (*RetentionPolicyWithTeamAndChannelCounts, *Response) {
r, appErr := c.doApiPatchBytes(c.GetDataRetentionPolicyRoute(patch.ID), patch.ToJson())
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
policy, err := RetentionPolicyWithTeamAndChannelCountsFromJson(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.PatchDataRetentionPolicy", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode))
}
return policy, BuildResponse(r)
}
// GetTeamsForRetentionPolicy will get the teams to which the specified policy is currently applied.
func (c *Client4) GetTeamsForRetentionPolicy(policyID string, page, perPage int) (*TeamsWithCount, *Response) {
query := fmt.Sprintf("?page=%d&per_page=%d", page, perPage)
r, appErr := c.DoApiGet(c.GetDataRetentionPolicyRoute(policyID)+"/teams"+query, "")
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
var teams *TeamsWithCount
jsonErr := json.NewDecoder(r.Body).Decode(&teams)
if jsonErr != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.GetTeamsForRetentionPolicy", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode))
}
return teams, BuildResponse(r)
}
// SearchTeamsForRetentionPolicy will search the teams to which the specified policy is currently applied.
func (c *Client4) SearchTeamsForRetentionPolicy(policyID string, term string) ([]*Team, *Response) {
body, _ := json.Marshal(map[string]interface{}{"term": term})
r, appErr := c.doApiPostBytes(c.GetDataRetentionPolicyRoute(policyID)+"/teams/search", body)
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
var teams []*Team
jsonErr := json.NewDecoder(r.Body).Decode(&teams)
if jsonErr != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.SearchTeamsForRetentionPolicy", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode))
}
return teams, BuildResponse(r)
}
// AddTeamsToRetentionPolicy will add the specified teams to the granular data retention policy
// with the specified ID.
func (c *Client4) AddTeamsToRetentionPolicy(policyID string, teamIDs []string) *Response {
body, _ := json.Marshal(teamIDs)
r, appErr := c.doApiPostBytes(c.GetDataRetentionPolicyRoute(policyID)+"/teams", body)
if appErr != nil {
return BuildErrorResponse(r, appErr)
}
defer closeBody(r)
return BuildResponse(r)
}
// RemoveTeamsFromRetentionPolicy will remove the specified teams from the granular data retention policy
// with the specified ID.
func (c *Client4) RemoveTeamsFromRetentionPolicy(policyID string, teamIDs []string) *Response {
body, _ := json.Marshal(teamIDs)
r, appErr := c.doApiDeleteBytes(c.GetDataRetentionPolicyRoute(policyID)+"/teams", body)
if appErr != nil {
return BuildErrorResponse(r, appErr)
}
defer closeBody(r)
return BuildResponse(r)
}
// GetChannelsForRetentionPolicy will get the channels to which the specified policy is currently applied.
func (c *Client4) GetChannelsForRetentionPolicy(policyID string, page, perPage int) (*ChannelsWithCount, *Response) {
query := fmt.Sprintf("?page=%d&per_page=%d", page, perPage)
r, appErr := c.DoApiGet(c.GetDataRetentionPolicyRoute(policyID)+"/channels"+query, "")
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
var channels *ChannelsWithCount
jsonErr := json.NewDecoder(r.Body).Decode(&channels)
if jsonErr != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.GetChannelsForRetentionPolicy", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode))
}
return channels, BuildResponse(r)
}
// SearchChannelsForRetentionPolicy will search the channels to which the specified policy is currently applied.
func (c *Client4) SearchChannelsForRetentionPolicy(policyID string, term string) (ChannelListWithTeamData, *Response) {
body, _ := json.Marshal(map[string]interface{}{"term": term})
r, appErr := c.doApiPostBytes(c.GetDataRetentionPolicyRoute(policyID)+"/channels/search", body)
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
var channels ChannelListWithTeamData
jsonErr := json.NewDecoder(r.Body).Decode(&channels)
if jsonErr != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.SearchChannelsForRetentionPolicy", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode))
}
return channels, BuildResponse(r)
}
// AddChannelsToRetentionPolicy will add the specified channels to the granular data retention policy
// with the specified ID.
func (c *Client4) AddChannelsToRetentionPolicy(policyID string, channelIDs []string) *Response {
body, _ := json.Marshal(channelIDs)
r, appErr := c.doApiPostBytes(c.GetDataRetentionPolicyRoute(policyID)+"/channels", body)
if appErr != nil {
return BuildErrorResponse(r, appErr)
}
defer closeBody(r)
return BuildResponse(r)
}
// RemoveChannelsFromRetentionPolicy will remove the specified channels from the granular data retention policy
// with the specified ID.
func (c *Client4) RemoveChannelsFromRetentionPolicy(policyID string, channelIDs []string) *Response {
body, _ := json.Marshal(channelIDs)
r, appErr := c.doApiDeleteBytes(c.GetDataRetentionPolicyRoute(policyID)+"/channels", body)
if appErr != nil {
return BuildErrorResponse(r, appErr)
}
defer closeBody(r)
return BuildResponse(r)
}
// GetTeamPoliciesForUser will get the data retention policies for the teams to which a user belongs.
func (c *Client4) GetTeamPoliciesForUser(userID string, offset, limit int) (*RetentionPolicyForTeamList, *Response) {
r, appErr := c.DoApiGet(c.GetUserRoute(userID)+"/data_retention/team_policies", "")
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
var teams RetentionPolicyForTeamList
jsonErr := json.NewDecoder(r.Body).Decode(&teams)
if jsonErr != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.GetTeamPoliciesForUser", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode))
}
return &teams, BuildResponse(r)
}
// GetChannelPoliciesForUser will get the data retention policies for the channels to which a user belongs.
func (c *Client4) GetChannelPoliciesForUser(userID string, offset, limit int) (*RetentionPolicyForChannelList, *Response) {
r, appErr := c.DoApiGet(c.GetUserRoute(userID)+"/data_retention/channel_policies", "")
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
var channels RetentionPolicyForChannelList
jsonErr := json.NewDecoder(r.Body).Decode(&channels)
if jsonErr != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.GetChannelPoliciesForUser", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode))
}
return &channels, BuildResponse(r)
}
// Commands Section
@ -5019,7 +5394,7 @@ func (c *Client4) uploadPlugin(file io.Reader, force bool) (*Manifest, *Response
}
rq.Header.Set("Content-Type", writer.FormDataContentType())
if len(c.AuthToken) > 0 {
if c.AuthToken != "" {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
}
@ -5436,7 +5811,7 @@ func (c *Client4) GetChannelMemberCountsByGroup(channelID string, includeTimezon
// RequestTrialLicense will request a trial license and install it in the server
func (c *Client4) RequestTrialLicense(users int) (bool, *Response) {
b, _ := json.Marshal(map[string]int{"users": users})
b, _ := json.Marshal(map[string]interface{}{"users": users, "terms_accepted": true})
r, err := c.DoApiPost("/trial-license", string(b))
if err != nil {
return false, BuildErrorResponse(r, err)
@ -5626,7 +6001,7 @@ func (c *Client4) GetUploadsForUser(userId string) ([]*UploadSession, *Response)
// a FileInfo object.
func (c *Client4) UploadData(uploadId string, data io.Reader) (*FileInfo, *Response) {
url := c.GetUploadRoute(uploadId)
r, err := c.doApiRequestReader("POST", c.ApiUrl+url, data, "")
r, err := c.doApiRequestReader("POST", c.ApiUrl+url, data, nil)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@ -5710,6 +6085,18 @@ func (c *Client4) GetSubscription() (*Subscription, *Response) {
return subscription, BuildResponse(r)
}
func (c *Client4) GetSubscriptionStats() (*SubscriptionStats, *Response) {
r, appErr := c.DoApiGet(c.GetCloudRoute()+"/subscription/stats", "")
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
var stats *SubscriptionStats
json.NewDecoder(r.Body).Decode(&stats)
return stats, BuildResponse(r)
}
func (c *Client4) GetInvoicesForSubscription() ([]*Invoice, *Response) {
r, appErr := c.DoApiGet(c.GetCloudRoute()+"/subscription/invoices", "")
if appErr != nil {
@ -5753,13 +6140,62 @@ func (c *Client4) UpdateCloudCustomerAddress(address *Address) (*CloudCustomer,
return customer, BuildResponse(r)
}
func (c *Client4) GetUserThreads(userId string, options GetUserThreadsOpts) (*Threads, *Response) {
func (c *Client4) ListImports() ([]string, *Response) {
r, err := c.DoApiGet(c.GetImportsRoute(), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return ArrayFromJson(r.Body), BuildResponse(r)
}
func (c *Client4) ListExports() ([]string, *Response) {
r, err := c.DoApiGet(c.GetExportsRoute(), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return ArrayFromJson(r.Body), BuildResponse(r)
}
func (c *Client4) DeleteExport(name string) (bool, *Response) {
r, err := c.DoApiDelete(c.GetExportRoute(name))
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
func (c *Client4) DownloadExport(name string, wr io.Writer, offset int64) (int64, *Response) {
var headers map[string]string
if offset > 0 {
headers = map[string]string{
HEADER_RANGE: fmt.Sprintf("bytes=%d-", offset),
}
}
r, appErr := c.DoApiRequestWithHeaders(http.MethodGet, c.ApiUrl+c.GetExportRoute(name), "", headers)
if appErr != nil {
return 0, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
n, err := io.Copy(wr, r.Body)
if err != nil {
return n, BuildErrorResponse(r, NewAppError("DownloadExport", "model.client.copy.app_error", nil, err.Error(), r.StatusCode))
}
return n, BuildResponse(r)
}
func (c *Client4) GetUserThreads(userId, teamId string, options GetUserThreadsOpts) (*Threads, *Response) {
v := url.Values{}
if options.Since != 0 {
v.Set("since", fmt.Sprintf("%d", options.Since))
}
if options.Page != 0 {
v.Set("page", fmt.Sprintf("%d", options.Page))
if options.Before != "" {
v.Set("before", options.Before)
}
if options.After != "" {
v.Set("after", options.After)
}
if options.PageSize != 0 {
v.Set("pageSize", fmt.Sprintf("%d", options.PageSize))
@ -5770,8 +6206,10 @@ func (c *Client4) GetUserThreads(userId string, options GetUserThreadsOpts) (*Th
if options.Deleted {
v.Set("deleted", "true")
}
url := c.GetUserThreadsRoute(userId)
if options.Unread {
v.Set("unread", "true")
}
url := c.GetUserThreadsRoute(userId, teamId)
if len(v) > 0 {
url += "?" + v.Encode()
}
@ -5788,8 +6226,25 @@ func (c *Client4) GetUserThreads(userId string, options GetUserThreadsOpts) (*Th
return &threads, BuildResponse(r)
}
func (c *Client4) UpdateThreadsReadForUser(userId string, timestamp int64) *Response {
r, appErr := c.DoApiPut(fmt.Sprintf("%s/read/%d", c.GetUserThreadsRoute(userId), timestamp), "")
func (c *Client4) GetUserThread(userId, teamId, threadId string, extended bool) (*ThreadResponse, *Response) {
url := c.GetUserThreadRoute(userId, teamId, threadId)
if extended {
url += "?extended=true"
}
r, appErr := c.DoApiGet(url, "")
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
var thread ThreadResponse
json.NewDecoder(r.Body).Decode(&thread)
return &thread, BuildResponse(r)
}
func (c *Client4) UpdateThreadsReadForUser(userId, teamId string) *Response {
r, appErr := c.DoApiPut(fmt.Sprintf("%s/read", c.GetUserThreadsRoute(userId, teamId)), "")
if appErr != nil {
return BuildErrorResponse(r, appErr)
}
@ -5798,23 +6253,25 @@ func (c *Client4) UpdateThreadsReadForUser(userId string, timestamp int64) *Resp
return BuildResponse(r)
}
func (c *Client4) UpdateThreadReadForUser(userId, threadId string, timestamp int64) *Response {
r, appErr := c.DoApiPut(fmt.Sprintf("%s/read/%d", c.GetUserThreadRoute(userId, threadId), timestamp), "")
func (c *Client4) UpdateThreadReadForUser(userId, teamId, threadId string, timestamp int64) (*ThreadResponse, *Response) {
r, appErr := c.DoApiPut(fmt.Sprintf("%s/read/%d", c.GetUserThreadRoute(userId, teamId, threadId), timestamp), "")
if appErr != nil {
return BuildErrorResponse(r, appErr)
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
var thread ThreadResponse
json.NewDecoder(r.Body).Decode(&thread)
return BuildResponse(r)
return &thread, BuildResponse(r)
}
func (c *Client4) UpdateThreadFollowForUser(userId, threadId string, state bool) *Response {
func (c *Client4) UpdateThreadFollowForUser(userId, teamId, threadId string, state bool) *Response {
var appErr *AppError
var r *http.Response
if state {
r, appErr = c.DoApiPut(c.GetUserThreadRoute(userId, threadId)+"/following", "")
r, appErr = c.DoApiPut(c.GetUserThreadRoute(userId, teamId, threadId)+"/following", "")
} else {
r, appErr = c.DoApiDelete(c.GetUserThreadRoute(userId, threadId) + "/following")
r, appErr = c.DoApiDelete(c.GetUserThreadRoute(userId, teamId, threadId) + "/following")
}
if appErr != nil {
return BuildErrorResponse(r, appErr)
@ -5823,3 +6280,64 @@ func (c *Client4) UpdateThreadFollowForUser(userId, threadId string, state bool)
return BuildResponse(r)
}
func (c *Client4) SendAdminUpgradeRequestEmail() *Response {
r, appErr := c.DoApiPost(c.GetCloudRoute()+"/subscription/limitreached/invite", "")
if appErr != nil {
return BuildErrorResponse(r, appErr)
}
defer closeBody(r)
return BuildResponse(r)
}
func (c *Client4) SendAdminUpgradeRequestEmailOnJoin() *Response {
r, appErr := c.DoApiPost(c.GetCloudRoute()+"/subscription/limitreached/join", "")
if appErr != nil {
return BuildErrorResponse(r, appErr)
}
defer closeBody(r)
return BuildResponse(r)
}
func (c *Client4) GetAllSharedChannels(teamID string, page, perPage int) ([]*SharedChannel, *Response) {
url := fmt.Sprintf("%s/%s?page=%d&per_page=%d", c.GetSharedChannelsRoute(), teamID, page, perPage)
r, appErr := c.DoApiGet(url, "")
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
var channels []*SharedChannel
json.NewDecoder(r.Body).Decode(&channels)
return channels, BuildResponse(r)
}
func (c *Client4) GetRemoteClusterInfo(remoteID string) (RemoteClusterInfo, *Response) {
url := fmt.Sprintf("%s/remote_info/%s", c.GetSharedChannelsRoute(), remoteID)
r, appErr := c.DoApiGet(url, "")
if appErr != nil {
return RemoteClusterInfo{}, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
var rci RemoteClusterInfo
json.NewDecoder(r.Body).Decode(&rci)
return rci, BuildResponse(r)
}
func (c *Client4) GetAncillaryPermissions(subsectionPermissions []string) ([]string, *Response) {
var returnedPermissions []string
url := fmt.Sprintf("%s/ancillary?subsection_permissions=%s", c.GetPermissionsRoute(), strings.Join(subsectionPermissions, ","))
r, appErr := c.DoApiGet(url, "")
if appErr != nil {
return returnedPermissions, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
json.NewDecoder(r.Body).Decode(&returnedPermissions)
return returnedPermissions, BuildResponse(r)
}

View File

@ -3,13 +3,53 @@
package model
import "strings"
const (
EventTypeFailedPayment = "failed-payment"
EventTypeFailedPaymentNoCard = "failed-payment-no-card"
EventTypeSendAdminWelcomeEmail = "send-admin-welcome-email"
EventTypeTrialWillEnd = "trial-will-end"
EventTypeTrialEnded = "trial-ended"
JoinLimitation = "join"
InviteLimitation = "invite"
)
var MockCWS string
type BillingScheme string
const (
BillingSchemePerSeat = BillingScheme("per_seat")
BillingSchemeFlatFee = BillingScheme("flat_fee")
)
type RecurringInterval string
const (
RecurringIntervalYearly = RecurringInterval("year")
RecurringIntervalMonthly = RecurringInterval("month")
)
type SubscriptionFamily string
const (
SubscriptionFamilyCloud = SubscriptionFamily("cloud")
SubscriptionFamilyOnPrem = SubscriptionFamily("on-prem")
)
// Product model represents a product on the cloud system.
type Product struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
PricePerSeat float64 `json:"price_per_seat"`
AddOns []*AddOn `json:"add_ons"`
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
PricePerSeat float64 `json:"price_per_seat"`
AddOns []*AddOn `json:"add_ons"`
SKU string `json:"sku"`
PriceID string `json:"price_id"`
Family SubscriptionFamily `json:"product_family"`
RecurringInterval RecurringInterval `json:"recurring_interval"`
BillingScheme BillingScheme `json:"billing_scheme"`
}
// AddOn represents an addon to a product.
@ -85,6 +125,13 @@ type Subscription struct {
DNS string `json:"dns"`
IsPaidTier string `json:"is_paid_tier"`
LastInvoice *Invoice `json:"last_invoice"`
IsFreeTrial string `json:"is_free_trial"`
TrialEndAt int64 `json:"trial_end_at"`
}
// GetWorkSpaceNameFromDNS returns the work space name. For example from test.mattermost.cloud.com, it returns test
func (s *Subscription) GetWorkSpaceNameFromDNS() string {
return strings.Split(s.DNS, ".")[0]
}
// Invoice model represents a cloud invoice
@ -112,3 +159,30 @@ type InvoiceLineItem struct {
Type string `json:"type"`
Metadata map[string]interface{} `json:"metadata"`
}
type CWSWebhookPayload struct {
Event string `json:"event"`
FailedPayment *FailedPayment `json:"failed_payment"`
CloudWorkspaceOwner *CloudWorkspaceOwner `json:"cloud_workspace_owner"`
SubscriptionTrialEndUnixTimeStamp int64 `json:"trial_end_time_stamp"`
}
type FailedPayment struct {
CardBrand string `json:"card_brand"`
LastFour int `json:"last_four"`
FailureMessage string `json:"failure_message"`
}
// CloudWorkspaceOwner is part of the CWS Webhook payload that contains information about the user that created the workspace from the CWS
type CloudWorkspaceOwner struct {
UserName string `json:"username"`
}
type SubscriptionStats struct {
RemainingSeats int `json:"remaining_seats"`
IsPaidTier string `json:"is_paid_tier"`
IsFreeTrial string `json:"is_free_trial"`
}
type SubscriptionChange struct {
ProductID string `json:"product_id"`
}

View File

@ -39,7 +39,7 @@ func (o *ClusterDiscovery) PreSave() {
func (o *ClusterDiscovery) AutoFillHostname() {
// attempt to set the hostname from the OS
if len(o.Hostname) == 0 {
if o.Hostname == "" {
if hn, err := os.Hostname(); err == nil {
o.Hostname = hn
}
@ -48,8 +48,8 @@ func (o *ClusterDiscovery) AutoFillHostname() {
func (o *ClusterDiscovery) AutoFillIpAddress(iface string, ipAddress string) {
// attempt to set the hostname to the first non-local IP address
if len(o.Hostname) == 0 {
if len(ipAddress) > 0 {
if o.Hostname == "" {
if ipAddress != "" {
o.Hostname = ipAddress
} else {
o.Hostname = GetServerIpAddress(iface)
@ -93,15 +93,15 @@ func (o *ClusterDiscovery) IsValid() *AppError {
return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.id.app_error", nil, "", http.StatusBadRequest)
}
if len(o.ClusterName) == 0 {
if o.ClusterName == "" {
return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.name.app_error", nil, "", http.StatusBadRequest)
}
if len(o.Type) == 0 {
if o.Type == "" {
return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.type.app_error", nil, "", http.StatusBadRequest)
}
if len(o.Hostname) == 0 {
if o.Hostname == "" {
return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.hostname.app_error", nil, "", http.StatusBadRequest)
}

View File

@ -16,15 +16,15 @@ type ClusterInfo struct {
Hostname string `json:"hostname"`
}
func (me *ClusterInfo) ToJson() string {
b, _ := json.Marshal(me)
func (ci *ClusterInfo) ToJson() string {
b, _ := json.Marshal(ci)
return string(b)
}
func ClusterInfoFromJson(data io.Reader) *ClusterInfo {
var me *ClusterInfo
json.NewDecoder(data).Decode(&me)
return me
var ci *ClusterInfo
json.NewDecoder(data).Decode(&ci)
return ci
}
func ClusterInfosToJson(objmap []*ClusterInfo) string {
@ -38,7 +38,6 @@ func ClusterInfosFromJson(data io.Reader) []*ClusterInfo {
var objmap []*ClusterInfo
if err := decoder.Decode(&objmap); err != nil {
return make([]*ClusterInfo, 0)
} else {
return objmap
}
return objmap
}

View File

@ -40,6 +40,7 @@ const (
CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_ALL_USERS = "inv_all_user_sessions"
CLUSTER_EVENT_INSTALL_PLUGIN = "install_plugin"
CLUSTER_EVENT_REMOVE_PLUGIN = "remove_plugin"
CLUSTER_EVENT_PLUGIN_EVENT = "plugin_event"
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_TERMS_OF_SERVICE = "inv_terms_of_service"
CLUSTER_EVENT_BUSY_STATE_CHANGED = "busy_state_change"

View File

@ -15,13 +15,13 @@ type ClusterStats struct {
TotalMasterDbConnections int `json:"total_master_db_connections"`
}
func (me *ClusterStats) ToJson() string {
b, _ := json.Marshal(me)
func (cs *ClusterStats) ToJson() string {
b, _ := json.Marshal(cs)
return string(b)
}
func ClusterStatsFromJson(data io.Reader) *ClusterStats {
var me *ClusterStats
json.NewDecoder(data).Decode(&me)
return me
var cs *ClusterStats
json.NewDecoder(data).Decode(&cs)
return cs
}

View File

@ -105,7 +105,7 @@ func (o *Command) IsValid() *AppError {
return NewAppError("Command.IsValid", "model.command.is_valid.trigger.app_error", nil, "", http.StatusBadRequest)
}
if len(o.URL) == 0 || len(o.URL) > 1024 {
if o.URL == "" || len(o.URL) > 1024 {
return NewAppError("Command.IsValid", "model.command.is_valid.url.app_error", nil, "", http.StatusBadRequest)
}

View File

@ -7,21 +7,21 @@ import (
"encoding/json"
"io"
goi18n "github.com/mattermost/go-i18n/i18n"
"github.com/mattermost/mattermost-server/v5/shared/i18n"
)
type CommandArgs struct {
UserId string `json:"user_id"`
ChannelId string `json:"channel_id"`
TeamId string `json:"team_id"`
RootId string `json:"root_id"`
ParentId string `json:"parent_id"`
TriggerId string `json:"trigger_id,omitempty"`
Command string `json:"command"`
SiteURL string `json:"-"`
T goi18n.TranslateFunc `json:"-"`
UserMentions UserMentionMap `json:"-"`
ChannelMentions ChannelMentionMap `json:"-"`
UserId string `json:"user_id"`
ChannelId string `json:"channel_id"`
TeamId string `json:"team_id"`
RootId string `json:"root_id"`
ParentId string `json:"parent_id"`
TriggerId string `json:"trigger_id,omitempty"`
Command string `json:"command"`
SiteURL string `json:"-"`
T i18n.TranslateFunc `json:"-"`
UserMentions UserMentionMap `json:"-"`
ChannelMentions ChannelMentionMap `json:"-"`
// DO NOT USE Session field is deprecated. MM-26398
Session Session `json:"-"`

View File

@ -52,7 +52,7 @@ type AutocompleteArg struct {
HelpText string
// Type of the argument
Type AutocompleteArgType
// Required determins if argument is optional or not.
// Required determines if argument is optional or not.
Required bool
// Actual data of the argument (depends on the Type)
Data interface{}

View File

@ -62,7 +62,7 @@ func CommandResponseFromJson(data io.Reader) (*CommandResponse, error) {
var o CommandResponse
err = json.Unmarshal(b, &o)
if err != nil {
return nil, jsonutils.HumanizeJsonError(err, b)
return nil, jsonutils.HumanizeJSONError(err, b)
}
o.Attachments = StringifySlackFieldValue(o.Attachments)

View File

@ -53,11 +53,11 @@ func (o *CommandWebhook) IsValid() *AppError {
return NewAppError("CommandWebhook.IsValid", "model.command_hook.channel_id.app_error", nil, "", http.StatusBadRequest)
}
if len(o.RootId) != 0 && !IsValidId(o.RootId) {
if o.RootId != "" && !IsValidId(o.RootId) {
return NewAppError("CommandWebhook.IsValid", "model.command_hook.root_id.app_error", nil, "", http.StatusBadRequest)
}
if len(o.ParentId) != 0 && !IsValidId(o.ParentId) {
if o.ParentId != "" && !IsValidId(o.ParentId) {
return NewAppError("CommandWebhook.IsValid", "model.command_hook.parent_id.app_error", nil, "", http.StatusBadRequest)
}

View File

@ -37,6 +37,19 @@ type Compliance struct {
type Compliances []Compliance
// ComplianceExportCursor is used for paginated iteration of posts
// for compliance export.
// We need to keep track of the last post ID in addition to the last post
// CreateAt to break ties when two posts have the same CreateAt.
type ComplianceExportCursor struct {
LastChannelsQueryPostCreateAt int64
LastChannelsQueryPostID string
ChannelsQueryCompleted bool
LastDirectMessagesQueryPostCreateAt int64
LastDirectMessagesQueryPostID string
DirectMessagesQueryCompleted bool
}
func (c *Compliance) ToJson() string {
b, _ := json.Marshal(c)
return string(b)
@ -58,6 +71,11 @@ func (c *Compliance) PreSave() {
c.CreateAt = GetMillis()
}
func (c *Compliance) DeepCopy() *Compliance {
copy := *c
return &copy
}
func (c *Compliance) JobName() string {
jobName := c.Type
if c.Type == COMPLIANCE_TYPE_DAILY {
@ -79,7 +97,7 @@ func (c *Compliance) IsValid() *AppError {
return NewAppError("Compliance.IsValid", "model.compliance.is_valid.create_at.app_error", nil, "", http.StatusBadRequest)
}
if len(c.Desc) > 512 || len(c.Desc) == 0 {
if len(c.Desc) > 512 || c.Desc == "" {
return NewAppError("Compliance.IsValid", "model.compliance.is_valid.desc.app_error", nil, "", http.StatusBadRequest)
}
@ -105,11 +123,11 @@ func ComplianceFromJson(data io.Reader) *Compliance {
}
func (c Compliances) ToJson() string {
if b, err := json.Marshal(c); err != nil {
b, err := json.Marshal(c)
if err != nil {
return "[]"
} else {
return string(b)
}
return string(b)
}
func CompliancesFromJson(data io.Reader) Compliances {

View File

@ -53,6 +53,7 @@ func CompliancePostHeader() []string {
"UserUsername",
"UserEmail",
"UserNickname",
"UserType",
"PostId",
"PostCreateAt",
@ -66,61 +67,58 @@ func CompliancePostHeader() []string {
"PostProps",
"PostHashtags",
"PostFileIds",
"UserType",
}
}
func cleanComplianceStrings(in string) string {
if matched, _ := regexp.MatchString("^\\s*(=|\\+|\\-)", in); matched {
return "'" + in
} else {
return in
}
return in
}
func (me *CompliancePost) Row() []string {
func (cp *CompliancePost) Row() []string {
postDeleteAt := ""
if me.PostDeleteAt > 0 {
postDeleteAt = time.Unix(0, me.PostDeleteAt*int64(1000*1000)).Format(time.RFC3339)
if cp.PostDeleteAt > 0 {
postDeleteAt = time.Unix(0, cp.PostDeleteAt*int64(1000*1000)).Format(time.RFC3339)
}
postUpdateAt := ""
if me.PostUpdateAt != me.PostCreateAt {
postUpdateAt = time.Unix(0, me.PostUpdateAt*int64(1000*1000)).Format(time.RFC3339)
if cp.PostUpdateAt != cp.PostCreateAt {
postUpdateAt = time.Unix(0, cp.PostUpdateAt*int64(1000*1000)).Format(time.RFC3339)
}
userType := "user"
if me.IsBot {
if cp.IsBot {
userType = "bot"
}
return []string{
cleanComplianceStrings(me.TeamName),
cleanComplianceStrings(me.TeamDisplayName),
cleanComplianceStrings(cp.TeamName),
cleanComplianceStrings(cp.TeamDisplayName),
cleanComplianceStrings(me.ChannelName),
cleanComplianceStrings(me.ChannelDisplayName),
cleanComplianceStrings(me.ChannelType),
cleanComplianceStrings(cp.ChannelName),
cleanComplianceStrings(cp.ChannelDisplayName),
cleanComplianceStrings(cp.ChannelType),
cleanComplianceStrings(me.UserUsername),
cleanComplianceStrings(me.UserEmail),
cleanComplianceStrings(me.UserNickname),
cleanComplianceStrings(cp.UserUsername),
cleanComplianceStrings(cp.UserEmail),
cleanComplianceStrings(cp.UserNickname),
userType,
me.PostId,
time.Unix(0, me.PostCreateAt*int64(1000*1000)).Format(time.RFC3339),
cp.PostId,
time.Unix(0, cp.PostCreateAt*int64(1000*1000)).Format(time.RFC3339),
postUpdateAt,
postDeleteAt,
me.PostRootId,
me.PostParentId,
me.PostOriginalId,
cleanComplianceStrings(me.PostMessage),
me.PostType,
me.PostProps,
me.PostHashtags,
me.PostFileIds,
cp.PostRootId,
cp.PostParentId,
cp.PostOriginalId,
cleanComplianceStrings(cp.PostMessage),
cp.PostType,
cp.PostProps,
cp.PostHashtags,
cp.PostFileIds,
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,141 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"encoding/json"
"fmt"
"io"
"time"
)
const (
UserPropsKeyCustomStatus = "customStatus"
CustomStatusTextMaxRunes = 100
MaxRecentCustomStatuses = 5
DefaultCustomStatusEmoji = "speech_balloon"
)
var validCustomStatusDuration = map[string]bool{
"thirty_minutes": true,
"one_hour": true,
"four_hours": true,
"today": true,
"this_week": true,
"date_and_time": true,
}
type CustomStatus struct {
Emoji string `json:"emoji"`
Text string `json:"text"`
Duration string `json:"duration"`
ExpiresAt time.Time `json:"expires_at"`
}
func (cs *CustomStatus) PreSave() {
if cs.Emoji == "" {
cs.Emoji = DefaultCustomStatusEmoji
}
if cs.Duration == "" && !cs.ExpiresAt.Before(time.Now()) {
cs.Duration = "date_and_time"
}
runes := []rune(cs.Text)
if len(runes) > CustomStatusTextMaxRunes {
cs.Text = string(runes[:CustomStatusTextMaxRunes])
}
}
func (cs *CustomStatus) ToJson() string {
csCopy := *cs
b, _ := json.Marshal(csCopy)
return string(b)
}
func (cs *CustomStatus) AreDurationAndExpirationTimeValid() bool {
if cs.Duration == "" && (cs.ExpiresAt.IsZero() || !cs.ExpiresAt.Before(time.Now())) {
return true
}
if validCustomStatusDuration[cs.Duration] && !cs.ExpiresAt.Before(time.Now()) {
return true
}
return false
}
func CustomStatusFromJson(data io.Reader) *CustomStatus {
var cs *CustomStatus
_ = json.NewDecoder(data).Decode(&cs)
return cs
}
func RuneToHexadecimalString(r rune) string {
return fmt.Sprintf("%04x", r)
}
type RecentCustomStatuses []CustomStatus
func (rcs *RecentCustomStatuses) Contains(cs *CustomStatus) bool {
var csJSON = cs.ToJson()
// status is empty
if cs == nil || csJSON == "" || (cs.Emoji == "" && cs.Text == "") {
return false
}
for _, status := range *rcs {
if status.ToJson() == csJSON {
return true
}
}
return false
}
func (rcs *RecentCustomStatuses) Add(cs *CustomStatus) *RecentCustomStatuses {
newRCS := (*rcs)[:0]
// if same `text` exists in existing recent custom statuses, modify existing status
for _, status := range *rcs {
if status.Text != cs.Text {
newRCS = append(newRCS, status)
}
}
newRCS = append(RecentCustomStatuses{*cs}, newRCS...)
if len(newRCS) > MaxRecentCustomStatuses {
newRCS = newRCS[:MaxRecentCustomStatuses]
}
return &newRCS
}
func (rcs *RecentCustomStatuses) Remove(cs *CustomStatus) *RecentCustomStatuses {
var csJSON = cs.ToJson()
if csJSON == "" || (cs.Emoji == "" && cs.Text == "") {
return rcs
}
newRCS := (*rcs)[:0]
for _, status := range *rcs {
if status.ToJson() != csJSON {
newRCS = append(newRCS, status)
}
}
return &newRCS
}
func (rcs *RecentCustomStatuses) ToJson() string {
rcsCopy := *rcs
b, _ := json.Marshal(rcsCopy)
return string(b)
}
func RecentCustomStatusesFromJson(data io.Reader) *RecentCustomStatuses {
var rcs *RecentCustomStatuses
_ = json.NewDecoder(data).Decode(&rcs)
return rcs
}

View File

@ -8,20 +8,125 @@ import (
"io"
)
type DataRetentionPolicy struct {
type GlobalRetentionPolicy struct {
MessageDeletionEnabled bool `json:"message_deletion_enabled"`
FileDeletionEnabled bool `json:"file_deletion_enabled"`
MessageRetentionCutoff int64 `json:"message_retention_cutoff"`
FileRetentionCutoff int64 `json:"file_retention_cutoff"`
}
func (me *DataRetentionPolicy) ToJson() string {
b, _ := json.Marshal(me)
return string(b)
type RetentionPolicy struct {
ID string `db:"Id" json:"id"`
DisplayName string `json:"display_name"`
PostDuration *int64 `json:"post_duration"`
}
func DataRetentionPolicyFromJson(data io.Reader) *DataRetentionPolicy {
var me *DataRetentionPolicy
json.NewDecoder(data).Decode(&me)
return me
type RetentionPolicyWithTeamAndChannelIDs struct {
RetentionPolicy
TeamIDs []string `json:"team_ids"`
ChannelIDs []string `json:"channel_ids"`
}
type RetentionPolicyWithTeamAndChannelCounts struct {
RetentionPolicy
ChannelCount int64 `json:"channel_count"`
TeamCount int64 `json:"team_count"`
}
type RetentionPolicyChannel struct {
PolicyID string `db:"PolicyId"`
ChannelID string `db:"ChannelId"`
}
type RetentionPolicyTeam struct {
PolicyID string `db:"PolicyId"`
TeamID string `db:"TeamId"`
}
type RetentionPolicyWithTeamAndChannelCountsList struct {
Policies []*RetentionPolicyWithTeamAndChannelCounts `json:"policies"`
TotalCount int64 `json:"total_count"`
}
type RetentionPolicyForTeam struct {
TeamID string `db:"Id" json:"team_id"`
PostDuration int64 `json:"post_duration"`
}
type RetentionPolicyForTeamList struct {
Policies []*RetentionPolicyForTeam `json:"policies"`
TotalCount int64 `json:"total_count"`
}
type RetentionPolicyForChannel struct {
ChannelID string `db:"Id" json:"channel_id"`
PostDuration int64 `json:"post_duration"`
}
type RetentionPolicyForChannelList struct {
Policies []*RetentionPolicyForChannel `json:"policies"`
TotalCount int64 `json:"total_count"`
}
type RetentionPolicyCursor struct {
ChannelPoliciesDone bool
TeamPoliciesDone bool
GlobalPoliciesDone bool
}
func (rp *GlobalRetentionPolicy) ToJson() []byte {
b, _ := json.Marshal(rp)
return b
}
func GlobalRetentionPolicyFromJson(data io.Reader) *GlobalRetentionPolicy {
var grp *GlobalRetentionPolicy
json.NewDecoder(data).Decode(&grp)
return grp
}
func RetentionPolicyWithTeamAndChannelCountsFromJson(data io.Reader) (*RetentionPolicyWithTeamAndChannelCounts, error) {
var rp RetentionPolicyWithTeamAndChannelCounts
err := json.NewDecoder(data).Decode(&rp)
return &rp, err
}
func (rp *RetentionPolicyWithTeamAndChannelCounts) ToJson() []byte {
b, _ := json.Marshal(rp)
return b
}
func RetentionPolicyWithTeamAndChannelCountsListFromJson(data io.Reader) (*RetentionPolicyWithTeamAndChannelCountsList, error) {
var rpList *RetentionPolicyWithTeamAndChannelCountsList
err := json.NewDecoder(data).Decode(&rpList)
if err != nil {
return nil, err
}
return rpList, nil
}
func (rpList *RetentionPolicyWithTeamAndChannelCountsList) ToJson() []byte {
b, _ := json.Marshal(rpList)
return b
}
func RetentionPolicyWithTeamAndChannelIdsFromJson(data io.Reader) (*RetentionPolicyWithTeamAndChannelIDs, error) {
var rp *RetentionPolicyWithTeamAndChannelIDs
err := json.NewDecoder(data).Decode(&rp)
return rp, err
}
func (rp *RetentionPolicyWithTeamAndChannelIDs) ToJson() []byte {
b, _ := json.Marshal(rp)
return b
}
func (lst *RetentionPolicyForTeamList) ToJson() []byte {
b, _ := json.Marshal(lst)
return b
}
func (lst *RetentionPolicyForChannelList) ToJson() []byte {
b, _ := json.Marshal(lst)
return b
}

View File

@ -8,6 +8,7 @@ import (
"io"
"net/http"
"regexp"
"sort"
)
const (
@ -15,7 +16,9 @@ const (
EMOJI_SORT_BY_NAME = "name"
)
var EMOJI_PATTERN = regexp.MustCompile(`:[a-zA-Z0-9_-]+:`)
var EMOJI_PATTERN = regexp.MustCompile(`:[a-zA-Z0-9_+-]+:`)
var ReverseSystemEmojisMap = makeReverseEmojiMap()
type Emoji struct {
Id string `json:"id"`
@ -36,6 +39,26 @@ func GetSystemEmojiId(emojiName string) (string, bool) {
return id, found
}
func makeReverseEmojiMap() map[string][]string {
reverseEmojiMap := make(map[string][]string)
for key, value := range SystemEmojis {
emojiNames := reverseEmojiMap[value]
emojiNames = append(emojiNames, key)
sort.Strings(emojiNames)
reverseEmojiMap[value] = emojiNames
}
return reverseEmojiMap
}
func GetEmojiNameFromUnicode(unicode string) (emojiName string, count int) {
if emojiNames, found := ReverseSystemEmojisMap[unicode]; found {
return emojiNames[0], len(emojiNames)
}
return "", 0
}
func (emoji *Emoji) IsValid() *AppError {
if !IsValidId(emoji.Id) {
return NewAppError("Emoji.IsValid", "model.emoji.id.app_error", nil, "", http.StatusBadRequest)
@ -57,7 +80,7 @@ func (emoji *Emoji) IsValid() *AppError {
}
func IsValidEmojiName(name string) *AppError {
if len(name) == 0 || len(name) > EMOJI_NAME_MAX_LENGTH || !IsValidAlphaNumHyphenUnderscore(name, false) || inSystemEmoji(name) {
if name == "" || len(name) > EMOJI_NAME_MAX_LENGTH || !IsValidAlphaNumHyphenUnderscorePlus(name) || inSystemEmoji(name) {
return NewAppError("Emoji.IsValid", "model.emoji.name.app_error", nil, "", http.StatusBadRequest)
}

File diff suppressed because one or more lines are too long

View File

@ -3,16 +3,92 @@
package model
import (
"reflect"
"strconv"
)
type FeatureFlags struct {
// Exists only for unit and manual testing.
// When set to a value, will be returned by the ping endpoint.
TestFeature string
// Exists only for testing bool functionality. Boolean feature flags interpret "on" or "true" as true and
// all other values as false.
TestBoolFeature bool
// Toggle on and off scheduled jobs for cloud user limit emails see MM-29999
CloudDelinquentEmailJobsEnabled bool
// Toggle on and off support for Collapsed Threads
CollapsedThreads bool
// Enable the remote cluster service for shared channels.
EnableRemoteClusterService bool
// AppsEnabled toggle the Apps framework functionalities both in server and client side
AppsEnabled bool
// Feature flags to control plugin versions
PluginIncidentManagement string `plugin_id:"com.mattermost.plugin-incident-management"`
PluginApps string `plugin_id:"com.mattermost.apps"`
PluginFocalboard string `plugin_id:"focalboard"`
// Enable timed dnd support for user status
TimedDND bool
}
func (f *FeatureFlags) SetDefaults() {
f.TestFeature = "off"
f.TestBoolFeature = false
f.CloudDelinquentEmailJobsEnabled = false
f.CollapsedThreads = true
f.EnableRemoteClusterService = false
f.AppsEnabled = false
f.PluginIncidentManagement = "1.16.1"
f.PluginApps = ""
f.PluginFocalboard = ""
f.TimedDND = false
}
func (f *FeatureFlags) Plugins() map[string]string {
rFFVal := reflect.ValueOf(f).Elem()
rFFType := reflect.TypeOf(f).Elem()
pluginVersions := make(map[string]string)
for i := 0; i < rFFVal.NumField(); i++ {
rFieldVal := rFFVal.Field(i)
rFieldType := rFFType.Field(i)
pluginId, hasPluginId := rFieldType.Tag.Lookup("plugin_id")
if !hasPluginId {
continue
}
pluginVersions[pluginId] = rFieldVal.String()
}
return pluginVersions
}
// ToMap returns the feature flags as a map[string]string
// Supports boolean and string feature flags.
func (f *FeatureFlags) ToMap() map[string]string {
refStructVal := reflect.ValueOf(*f)
refStructType := reflect.TypeOf(*f)
ret := make(map[string]string)
for i := 0; i < refStructVal.NumField(); i++ {
refFieldVal := refStructVal.Field(i)
if !refFieldVal.IsValid() {
continue
}
refFieldType := refStructType.Field(i)
switch refFieldType.Type.Kind() {
case reflect.Bool:
ret[refFieldType.Name] = strconv.FormatBool(refFieldVal.Bool())
default:
ret[refFieldType.Name] = refFieldVal.String()
}
}
return ret
}

View File

@ -12,11 +12,6 @@ const (
MaxImageSize = int64(6048 * 4032) // 24 megapixels, roughly 36MB as a raw image
)
var (
IMAGE_EXTENSIONS = [7]string{".jpg", ".jpeg", ".gif", ".bmp", ".png", ".tiff", "tif"}
IMAGE_MIME_TYPES = map[string]string{".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".bmp": "image/bmp", ".png": "image/png", ".tiff": "image/tiff", ".tif": "image/tif"}
)
type FileUploadResponse struct {
FileInfos []*FileInfo `json:"file_infos"`
ClientIds []string `json:"client_ids"`

View File

@ -4,19 +4,14 @@
package model
import (
"bytes"
"encoding/json"
"image"
"image/gif"
"image/jpeg"
"io"
"mime"
"net/http"
"path/filepath"
"strings"
"github.com/disintegration/imaging"
"github.com/mattermost/mattermost-server/v5/mlog"
)
const (
@ -44,6 +39,7 @@ type FileInfo struct {
Id string `json:"id"`
CreatorId string `json:"user_id"`
PostId string `json:"post_id,omitempty"`
ChannelId string `db:"-" json:"channel_id"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
@ -59,6 +55,7 @@ type FileInfo struct {
HasPreviewImage bool `json:"has_preview_image,omitempty"`
MiniPreview *[]byte `json:"mini_preview"` // declared as *[]byte to avoid postgres/mysql differences in deserialization
Content string `json:"-"`
RemoteId *string `json:"remote_id"`
}
func (fi *FileInfo) ToJson() string {
@ -72,9 +69,8 @@ func FileInfoFromJson(data io.Reader) *FileInfo {
var fi FileInfo
if err := decoder.Decode(&fi); err != nil {
return nil
} else {
return &fi
}
return &fi
}
func FileInfosToJson(infos []*FileInfo) string {
@ -88,9 +84,8 @@ func FileInfosFromJson(data io.Reader) []*FileInfo {
var infos []*FileInfo
if err := decoder.Decode(&infos); err != nil {
return nil
} else {
return infos
}
return infos
}
func (fi *FileInfo) PreSave() {
@ -105,6 +100,10 @@ func (fi *FileInfo) PreSave() {
if fi.UpdateAt < fi.CreateAt {
fi.UpdateAt = fi.CreateAt
}
if fi.RemoteId == nil {
fi.RemoteId = NewString("")
}
}
func (fi *FileInfo) IsValid() *AppError {
@ -116,7 +115,7 @@ func (fi *FileInfo) IsValid() *AppError {
return NewAppError("FileInfo.IsValid", "model.file_info.is_valid.user_id.app_error", nil, "id="+fi.Id, http.StatusBadRequest)
}
if len(fi.PostId) != 0 && !IsValidId(fi.PostId) {
if fi.PostId != "" && !IsValidId(fi.PostId) {
return NewAppError("FileInfo.IsValid", "model.file_info.is_valid.post_id.app_error", nil, "id="+fi.Id, http.StatusBadRequest)
}
@ -157,19 +156,6 @@ func NewInfo(name string) *FileInfo {
return info
}
func GenerateMiniPreviewImage(img image.Image) *[]byte {
preview := imaging.Resize(img, 16, 16, imaging.Lanczos)
buf := new(bytes.Buffer)
if err := jpeg.Encode(buf, preview, &jpeg.Options{Quality: 90}); err != nil {
mlog.Error("Unable to encode image as mini preview jpg", mlog.Err(err))
return nil
}
data := buf.Bytes()
return &data
}
func GetInfoForBytes(name string, data io.ReadSeeker, size int) (*FileInfo, *AppError) {
info := &FileInfo{
Name: name,
@ -196,13 +182,13 @@ func GetInfoForBytes(name string, data io.ReadSeeker, size int) (*FileInfo, *App
if info.MimeType == "image/gif" {
// Just show the gif itself instead of a preview image for animated gifs
data.Seek(0, io.SeekStart)
if gifConfig, err := gif.DecodeAll(data); err != nil {
gifConfig, err := gif.DecodeAll(data)
if err != nil {
// Still return the rest of the info even though it doesn't appear to be an actual gif
info.HasPreviewImage = true
return info, NewAppError("GetInfoForBytes", "model.file_info.get.gif.app_error", nil, err.Error(), http.StatusBadRequest)
} else {
info.HasPreviewImage = len(gifConfig.Image) == 1
}
info.HasPreviewImage = len(gifConfig.Image) == 1
} else {
info.HasPreviewImage = true
}

View File

@ -0,0 +1,128 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"encoding/json"
"io"
"sort"
)
type FileInfoList struct {
Order []string `json:"order"`
FileInfos map[string]*FileInfo `json:"file_infos"`
NextFileInfoId string `json:"next_file_info_id"`
PrevFileInfoId string `json:"prev_file_info_id"`
}
func NewFileInfoList() *FileInfoList {
return &FileInfoList{
Order: make([]string, 0),
FileInfos: make(map[string]*FileInfo),
NextFileInfoId: "",
PrevFileInfoId: "",
}
}
func (o *FileInfoList) ToSlice() []*FileInfo {
var fileInfos []*FileInfo
for _, id := range o.Order {
fileInfos = append(fileInfos, o.FileInfos[id])
}
return fileInfos
}
func (o *FileInfoList) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func (o *FileInfoList) MakeNonNil() {
if o.Order == nil {
o.Order = make([]string, 0)
}
if o.FileInfos == nil {
o.FileInfos = make(map[string]*FileInfo)
}
}
func (o *FileInfoList) AddOrder(id string) {
if o.Order == nil {
o.Order = make([]string, 0, 128)
}
o.Order = append(o.Order, id)
}
func (o *FileInfoList) AddFileInfo(fileInfo *FileInfo) {
if o.FileInfos == nil {
o.FileInfos = make(map[string]*FileInfo)
}
o.FileInfos[fileInfo.Id] = fileInfo
}
func (o *FileInfoList) UniqueOrder() {
keys := make(map[string]bool)
order := []string{}
for _, fileInfoId := range o.Order {
if _, value := keys[fileInfoId]; !value {
keys[fileInfoId] = true
order = append(order, fileInfoId)
}
}
o.Order = order
}
func (o *FileInfoList) Extend(other *FileInfoList) {
for fileInfoId := range other.FileInfos {
o.AddFileInfo(other.FileInfos[fileInfoId])
}
for _, fileInfoId := range other.Order {
o.AddOrder(fileInfoId)
}
o.UniqueOrder()
}
func (o *FileInfoList) SortByCreateAt() {
sort.Slice(o.Order, func(i, j int) bool {
return o.FileInfos[o.Order[i]].CreateAt > o.FileInfos[o.Order[j]].CreateAt
})
}
func (o *FileInfoList) Etag() string {
id := "0"
var t int64 = 0
for _, v := range o.FileInfos {
if v.UpdateAt > t {
t = v.UpdateAt
id = v.Id
} else if v.UpdateAt == t && v.Id > id {
t = v.UpdateAt
id = v.Id
}
}
orderId := ""
if len(o.Order) > 0 {
orderId = o.Order[0]
}
return Etag(orderId, id, t)
}
func FileInfoListFromJson(data io.Reader) *FileInfoList {
var o *FileInfoList
json.NewDecoder(data).Decode(&o)
return o
}

View File

@ -0,0 +1,37 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"encoding/json"
"io"
)
type FileInfoSearchMatches map[string][]string
type FileInfoSearchResults struct {
*FileInfoList
Matches FileInfoSearchMatches `json:"matches"`
}
func MakeFileInfoSearchResults(fileInfos *FileInfoList, matches FileInfoSearchMatches) *FileInfoSearchResults {
return &FileInfoSearchResults{
fileInfos,
matches,
}
}
func (o *FileInfoSearchResults) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
}
return string(b)
}
func FileInfoSearchResultsFromJson(data io.Reader) *FileInfoSearchResults {
var o *FileInfoSearchResults
json.NewDecoder(data).Decode(&o)
return o
}

View File

@ -139,7 +139,7 @@ func (group *Group) IsValidForCreate() *AppError {
return NewAppError("Group.IsValidForCreate", "model.group.source.app_error", nil, "", http.StatusBadRequest)
}
if len(group.RemoteId) > GroupRemoteIDMaxLength || (len(group.RemoteId) == 0 && group.requiresRemoteId()) {
if len(group.RemoteId) > GroupRemoteIDMaxLength || (group.RemoteId == "" && group.requiresRemoteId()) {
return NewAppError("Group.IsValidForCreate", "model.group.remote_id.app_error", nil, "", http.StatusBadRequest)
}

View File

@ -60,14 +60,14 @@ func (syncable *GroupSyncable) UnmarshalJSON(b []byte) error {
if err != nil {
return err
}
var channelId string
var teamId string
for key, value := range kvp {
switch key {
case "team_id":
syncable.SyncableId = value.(string)
syncable.Type = GroupSyncableTypeTeam
teamId = value.(string)
case "channel_id":
syncable.SyncableId = value.(string)
syncable.Type = GroupSyncableTypeChannel
channelId = value.(string)
case "group_id":
syncable.GroupId = value.(string)
case "auto_add":
@ -75,30 +75,40 @@ func (syncable *GroupSyncable) UnmarshalJSON(b []byte) error {
default:
}
}
if channelId != "" {
syncable.TeamID = teamId
syncable.SyncableId = channelId
syncable.Type = GroupSyncableTypeChannel
} else {
syncable.SyncableId = teamId
syncable.Type = GroupSyncableTypeTeam
}
return nil
}
func (syncable *GroupSyncable) MarshalJSON() ([]byte, error) {
type Alias GroupSyncable
switch syncable.Type {
case GroupSyncableTypeTeam:
return json.Marshal(&struct {
TeamID string `json:"team_id"`
TeamDisplayName string `json:"team_display_name,omitempty"`
TeamType string `json:"team_type,omitempty"`
TeamID string `json:"team_id"`
TeamDisplayName string `json:"team_display_name,omitempty"`
TeamType string `json:"team_type,omitempty"`
Type GroupSyncableType `json:"type,omitempty"`
*Alias
}{
TeamDisplayName: syncable.TeamDisplayName,
TeamType: syncable.TeamType,
TeamID: syncable.SyncableId,
Type: syncable.Type,
Alias: (*Alias)(syncable),
})
case GroupSyncableTypeChannel:
return json.Marshal(&struct {
ChannelID string `json:"channel_id"`
ChannelDisplayName string `json:"channel_display_name,omitempty"`
ChannelType string `json:"channel_type,omitempty"`
ChannelID string `json:"channel_id"`
ChannelDisplayName string `json:"channel_display_name,omitempty"`
ChannelType string `json:"channel_type,omitempty"`
Type GroupSyncableType `json:"type,omitempty"`
TeamID string `json:"team_id,omitempty"`
TeamDisplayName string `json:"team_display_name,omitempty"`
@ -109,6 +119,7 @@ func (syncable *GroupSyncable) MarshalJSON() ([]byte, error) {
ChannelID: syncable.SyncableId,
ChannelDisplayName: syncable.ChannelDisplayName,
ChannelType: syncable.ChannelType,
Type: syncable.Type,
TeamID: syncable.TeamID,
TeamDisplayName: syncable.TeamDisplayName,

View File

@ -23,7 +23,7 @@ func (i *GuestsInvite) IsValid() *AppError {
}
for _, email := range i.Emails {
if len(email) > USER_EMAIL_MAX_LENGTH || len(email) == 0 || !IsValidEmail(email) {
if len(email) > USER_EMAIL_MAX_LENGTH || email == "" || !IsValidEmail(email) {
return NewAppError("GuestsInvite.IsValid", "model.guest.is_valid.email.app_error", nil, "email="+email, http.StatusBadRequest)
}
}

View File

@ -182,9 +182,8 @@ func decodeIncomingWebhookRequest(by []byte) (*IncomingWebhookRequest, error) {
err := decoder.Decode(&o)
if err == nil {
return &o, nil
} else {
return nil, err
}
return nil, err
}
func IncomingWebhookRequestFromJson(data io.Reader) (*IncomingWebhookRequest, *AppError) {
@ -211,7 +210,6 @@ func (o *IncomingWebhookRequest) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
return string(b)
}

View File

@ -18,13 +18,13 @@ type InitialLoad struct {
NoAccounts bool `json:"no_accounts"`
}
func (me *InitialLoad) ToJson() string {
b, _ := json.Marshal(me)
func (il *InitialLoad) ToJson() string {
b, _ := json.Marshal(il)
return string(b)
}
func InitialLoadFromJson(data io.Reader) *InitialLoad {
var o *InitialLoad
json.NewDecoder(data).Decode(&o)
return o
var il *InitialLoad
json.NewDecoder(data).Decode(&il)
return il
}

View File

@ -394,7 +394,7 @@ func (o *Post) StripActionIntegrations() {
func (o *Post) GetAction(id string) *PostAction {
for _, attachment := range o.Attachments() {
for _, action := range attachment.Actions {
if action.Id == id {
if action != nil && action.Id == id {
return action
}
}
@ -409,7 +409,7 @@ func (o *Post) GenerateActionIds() {
if attachments, ok := o.GetProp("attachments").([]*SlackAttachment); ok {
for _, attachment := range attachments {
for _, action := range attachment.Actions {
if action.Id == "" {
if action != nil && action.Id == "" {
action.Id = NewId()
}
}

View File

@ -22,7 +22,12 @@ const (
JOB_TYPE_EXPIRY_NOTIFY = "expiry_notify"
JOB_TYPE_PRODUCT_NOTICES = "product_notices"
JOB_TYPE_ACTIVE_USERS = "active_users"
JOB_TYPE_IMPORT_PROCESS = "import_process"
JOB_TYPE_IMPORT_DELETE = "import_delete"
JOB_TYPE_EXPORT_PROCESS = "export_process"
JOB_TYPE_EXPORT_DELETE = "export_delete"
JOB_TYPE_CLOUD = "cloud"
JOB_TYPE_RESEND_INVITATION_EMAIL = "resend_invitation_email"
JOB_STATUS_PENDING = "pending"
JOB_STATUS_IN_PROGRESS = "in_progress"
@ -33,6 +38,25 @@ const (
JOB_STATUS_WARNING = "warning"
)
var ALL_JOB_TYPES = [...]string{
JOB_TYPE_DATA_RETENTION,
JOB_TYPE_MESSAGE_EXPORT,
JOB_TYPE_ELASTICSEARCH_POST_INDEXING,
JOB_TYPE_ELASTICSEARCH_POST_AGGREGATION,
JOB_TYPE_BLEVE_POST_INDEXING,
JOB_TYPE_LDAP_SYNC,
JOB_TYPE_MIGRATIONS,
JOB_TYPE_PLUGINS,
JOB_TYPE_EXPIRY_NOTIFY,
JOB_TYPE_PRODUCT_NOTICES,
JOB_TYPE_ACTIVE_USERS,
JOB_TYPE_IMPORT_PROCESS,
JOB_TYPE_IMPORT_DELETE,
JOB_TYPE_EXPORT_PROCESS,
JOB_TYPE_EXPORT_DELETE,
JOB_TYPE_CLOUD,
}
type Job struct {
Id string `json:"id"`
Type string `json:"type"`
@ -66,7 +90,12 @@ func (j *Job) IsValid() *AppError {
case JOB_TYPE_PRODUCT_NOTICES:
case JOB_TYPE_EXPIRY_NOTIFY:
case JOB_TYPE_ACTIVE_USERS:
case JOB_TYPE_IMPORT_PROCESS:
case JOB_TYPE_IMPORT_DELETE:
case JOB_TYPE_EXPORT_PROCESS:
case JOB_TYPE_EXPORT_DELETE:
case JOB_TYPE_CLOUD:
case JOB_TYPE_RESEND_INVITATION_EMAIL:
default:
return NewAppError("Job.IsValid", "model.job.is_valid.type.app_error", nil, "id="+j.Id, http.StatusBadRequest)
}
@ -94,9 +123,8 @@ func JobFromJson(data io.Reader) *Job {
var job Job
if err := json.NewDecoder(data).Decode(&job); err == nil {
return &job
} else {
return nil
}
return nil
}
func JobsToJson(jobs []*Job) string {
@ -108,9 +136,8 @@ func JobsFromJson(data io.Reader) []*Job {
var jobs []*Job
if err := json.NewDecoder(data).Decode(&jobs); err == nil {
return jobs
} else {
return nil
}
return nil
}
func (j *Job) DataToJson() string {

View File

@ -5,8 +5,10 @@ package model
import (
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
const (
@ -16,6 +18,22 @@ const (
LICENSE_RENEWAL_LINK = "https://mattermost.com/renew/"
)
const (
SIXTY_DAYS = 60
FIFTY_EIGHT = 58
LICENSE_UP_FOR_RENEWAL_EMAIL_SENT = "LicenseUpForRenewalEmailSent"
)
var (
trialDuration = 30*(time.Hour*24) + (time.Hour * 8) // 720 hours (30 days) + 8 hours is trial license duration
adminTrialDuration = 30*(time.Hour*24) + (time.Hour * 23) + (time.Minute * 59) + (time.Second * 59) // 720 hours (30 days) + 23 hours, 59 mins and 59 seconds
// a sanctioned trial's duration is either more than the upper bound,
// or less than the lower bound
sanctionedTrialDurationLowerBound = 31*(time.Hour*24) + (time.Hour * 23) + (time.Minute * 59) + (time.Second * 59) // 744 hours (31 days) + 23 hours, 59 mins and 59 seconds
sanctionedTrialDurationUpperBound = 29*(time.Hour*24) + (time.Hour * 23) + (time.Minute * 59) + (time.Second * 59) // 696 hours (29 days) + 23 hours, 59 mins and 59 seconds
)
type LicenseRecord struct {
Id string `json:"id"`
CreateAt int64 `json:"create_at"`
@ -31,6 +49,7 @@ type License struct {
Features *Features `json:"features"`
SkuName string `json:"sku_name"`
SkuShortName string `json:"sku_short_name"`
IsTrial bool `json:"is_trial"`
}
type Customer struct {
@ -63,6 +82,7 @@ type Features struct {
MFA *bool `json:"mfa"`
GoogleOAuth *bool `json:"google_oauth"`
Office365OAuth *bool `json:"office365_oauth"`
OpenId *bool `json:"openid"`
Compliance *bool `json:"compliance"`
Cluster *bool `json:"cluster"`
Metrics *bool `json:"metrics"`
@ -83,6 +103,8 @@ type Features struct {
EnterprisePlugins *bool `json:"enterprise_plugins"`
AdvancedLogging *bool `json:"advanced_logging"`
Cloud *bool `json:"cloud"`
SharedChannels *bool `json:"shared_channels"`
RemoteClusterService *bool `json:"remote_cluster_service"`
// after we enabled more features we'll need to control them with this
FutureFeatures *bool `json:"future_features"`
@ -95,6 +117,7 @@ func (f *Features) ToMap() map[string]interface{} {
"mfa": *f.MFA,
"google": *f.GoogleOAuth,
"office365": *f.Office365OAuth,
"openid": *f.OpenId,
"compliance": *f.Compliance,
"cluster": *f.Cluster,
"metrics": *f.Metrics,
@ -112,6 +135,8 @@ func (f *Features) ToMap() map[string]interface{} {
"enterprise_plugins": *f.EnterprisePlugins,
"advanced_logging": *f.AdvancedLogging,
"cloud": *f.Cloud,
"shared_channels": *f.SharedChannels,
"remote_cluster_service": *f.RemoteClusterService,
"future": *f.FutureFeatures,
}
}
@ -145,6 +170,10 @@ func (f *Features) SetDefaults() {
f.Office365OAuth = NewBool(*f.FutureFeatures)
}
if f.OpenId == nil {
f.OpenId = NewBool(*f.FutureFeatures)
}
if f.Compliance == nil {
f.Compliance = NewBool(*f.FutureFeatures)
}
@ -224,6 +253,14 @@ func (f *Features) SetDefaults() {
if f.Cloud == nil {
f.Cloud = NewBool(false)
}
if f.SharedChannels == nil {
f.SharedChannels = NewBool(*f.FutureFeatures)
}
if f.RemoteClusterService == nil {
f.RemoteClusterService = NewBool(*f.FutureFeatures)
}
}
func (l *License) IsExpired() bool {
@ -235,6 +272,18 @@ func (l *License) IsPastGracePeriod() bool {
return timeDiff > LICENSE_GRACE_PERIOD
}
func (l *License) IsWithinExpirationPeriod() bool {
days := l.DaysToExpiration()
return days <= SIXTY_DAYS && days >= FIFTY_EIGHT
}
func (l *License) DaysToExpiration() int {
dif := l.ExpiresAt - GetMillis()
d, _ := time.ParseDuration(fmt.Sprint(dif) + "ms")
days := d.Hours() / 24
return int(days)
}
func (l *License) IsStarted() bool {
return l.StartsAt < GetMillis()
}
@ -244,6 +293,17 @@ func (l *License) ToJson() string {
return string(b)
}
func (l *License) IsTrialLicense() bool {
return l.IsTrial || (l.ExpiresAt-l.StartsAt) == trialDuration.Milliseconds() || (l.ExpiresAt-l.StartsAt) == adminTrialDuration.Milliseconds()
}
func (l *License) IsSanctionedTrial() bool {
duration := l.ExpiresAt - l.StartsAt
return l.IsTrialLicense() &&
(duration >= sanctionedTrialDurationLowerBound.Milliseconds() || duration <= sanctionedTrialDurationUpperBound.Milliseconds())
}
// NewTestLicense returns a license that expires in the future and has the given features.
func NewTestLicense(features ...string) *License {
ret := &License{
@ -278,7 +338,7 @@ func (lr *LicenseRecord) IsValid() *AppError {
return NewAppError("LicenseRecord.IsValid", "model.license_record.is_valid.create_at.app_error", nil, "", http.StatusBadRequest)
}
if len(lr.Bytes) == 0 || len(lr.Bytes) > 10000 {
if lr.Bytes == "" || len(lr.Bytes) > 10000 {
return NewAppError("LicenseRecord.IsValid", "model.license_record.is_valid.create_at.app_error", nil, "", http.StatusBadRequest)
}

View File

@ -147,7 +147,7 @@ type Manifest struct {
Id string `json:"id" yaml:"id"`
// The name to be displayed for the plugin.
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Name string `json:"name" yaml:"name"`
// A description of what your plugin is and does.
Description string `json:"description,omitempty" yaml:"description,omitempty"`
@ -196,9 +196,21 @@ type Manifest struct {
}
type ManifestServer struct {
// Executables are the paths to your executable binaries, specifying multiple entry points
// for different platforms when bundled together in a single plugin.
Executables *ManifestExecutables `json:"executables,omitempty" yaml:"executables,omitempty"`
// AllExecutables are the paths to your executable binaries, specifying multiple entry
// points for different platforms when bundled together in a single plugin.
AllExecutables map[string]string `json:"executables,omitempty" yaml:"executables,omitempty"`
// Executables is a legacy field populated with a subset of supported platform executables.
// When unmarshalling, Executables is authoritative for the platform executable paths it
// contains, overriding any values in AllExecutables. When marshalling, AllExecutables
// is authoritative.
//
// Code duplication is avoided when (un)marshalling by leveraging type aliases in the
// various (Un)Marshal(JSON|YAML) methods, since aliases don't inherit the aliased type's
// methods.
//
// In v6.0, we should remove this field and rename AllExecutables back to Executables.
Executables *ManifestExecutables `json:"-" yaml:"-"`
// Executable is the path to your executable binary. This should be relative to the root
// of your bundle and the location of the manifest file.
@ -210,6 +222,79 @@ type ManifestServer struct {
Executable string `json:"executable" yaml:"executable"`
}
func (ms *ManifestServer) MarshalJSON() ([]byte, error) {
type auxManifestServer ManifestServer
// Populate AllExecutables from Executables, if it exists.
if ms.Executables != nil {
if ms.AllExecutables == nil {
ms.AllExecutables = make(map[string]string)
}
ms.AllExecutables["linux-amd64"] = ms.Executables.LinuxAmd64
ms.AllExecutables["darwin-amd64"] = ms.Executables.DarwinAmd64
ms.AllExecutables["windows-amd64"] = ms.Executables.WindowsAmd64
}
return json.Marshal((*auxManifestServer)(ms))
}
func (ms *ManifestServer) UnmarshalJSON(data []byte) error {
type auxManifestServer ManifestServer
aux := (*auxManifestServer)(ms)
if err := json.Unmarshal(data, aux); err != nil {
return err
}
if len(aux.AllExecutables) > 0 {
ms.Executables = &ManifestExecutables{
LinuxAmd64: aux.AllExecutables["linux-amd64"],
DarwinAmd64: aux.AllExecutables["darwin-amd64"],
WindowsAmd64: aux.AllExecutables["windows-amd64"],
}
}
return nil
}
func (ms *ManifestServer) MarshalYAML() ([]byte, error) {
type auxManifestServer ManifestServer
// Populate AllExecutables from Executables, if it exists.
if ms.Executables != nil {
if ms.AllExecutables == nil {
ms.AllExecutables = make(map[string]string)
}
ms.AllExecutables["linux-amd64"] = ms.Executables.LinuxAmd64
ms.AllExecutables["darwin-amd64"] = ms.Executables.DarwinAmd64
ms.AllExecutables["windows-amd64"] = ms.Executables.WindowsAmd64
}
return yaml.Marshal((*auxManifestServer)(ms))
}
func (ms *ManifestServer) UnmarshalYAML(unmarshal func(interface{}) error) error {
type auxManifestServer ManifestServer
aux := (*auxManifestServer)(ms)
if err := unmarshal(&aux); err != nil {
return err
}
if len(aux.AllExecutables) > 0 {
ms.Executables = &ManifestExecutables{
LinuxAmd64: aux.AllExecutables["linux-amd64"],
DarwinAmd64: aux.AllExecutables["darwin-amd64"],
WindowsAmd64: aux.AllExecutables["windows-amd64"],
}
}
return nil
}
// ManifestExecutables is a legacy structure capturing a subet of the known platform executables.
type ManifestExecutables struct {
// LinuxAmd64 is the path to your executable binary for the corresponding platform
LinuxAmd64 string `json:"linux-amd64,omitempty" yaml:"linux-amd64,omitempty"`
@ -287,14 +372,9 @@ func (m *Manifest) GetExecutableForRuntime(goOs, goArch string) string {
}
var executable string
if server.Executables != nil {
if goOs == "linux" && goArch == "amd64" {
executable = server.Executables.LinuxAmd64
} else if goOs == "darwin" && goArch == "amd64" {
executable = server.Executables.DarwinAmd64
} else if goOs == "windows" && goArch == "amd64" {
executable = server.Executables.WindowsAmd64
}
if len(server.AllExecutables) > 0 {
osArch := fmt.Sprintf("%s-%s", goOs, goArch)
executable = server.AllExecutables[osArch]
}
if executable == "" {
@ -329,6 +409,10 @@ func (m *Manifest) IsValid() error {
return errors.New("invalid plugin ID")
}
if strings.TrimSpace(m.Name) == "" {
return errors.New("a plugin name is needed")
}
if m.HomepageURL != "" && !IsValidHttpUrl(m.HomepageURL) {
return errors.New("invalid HomepageURL")
}

View File

@ -20,8 +20,12 @@ type BaseMarketplacePlugin struct {
IconData string `json:"icon_data"`
DownloadURL string `json:"download_url"`
ReleaseNotesURL string `json:"release_notes_url"`
Labels []MarketplaceLabel `json:"labels"`
Signature string `json:"signature"` // Signature represents a signature of a plugin saved in base64 encoding.
Labels []MarketplaceLabel `json:"labels,omitempty"`
Hosting string `json:"hosting"` // Indicated if the plugin is limited to a certain hosting type
AuthorType string `json:"author_type"` // The maintainer of the plugin
ReleaseStage string `json:"release_stage"` // The stage in the software release cycle that the plugin is in
Enterprise bool `json:"enterprise"` // Indicated if the plugin is an enterprise plugin
Signature string `json:"signature"` // Signature represents a signature of a plugin saved in base64 encoding.
Manifest *Manifest `json:"manifest"`
}
@ -83,6 +87,8 @@ type MarketplacePluginFilter struct {
Cloud bool
LocalOnly bool
Platform string
PluginId string
ReturnAllVersions bool
}
// ApplyToURL modifies the given url to include query string parameters for the request.
@ -99,6 +105,8 @@ func (filter *MarketplacePluginFilter) ApplyToURL(u *url.URL) {
q.Add("cloud", strconv.FormatBool(filter.Cloud))
q.Add("local_only", strconv.FormatBool(filter.LocalOnly))
q.Add("platform", filter.Platform)
q.Add("plugin_id", filter.PluginId)
q.Add("return_all_versions", strconv.FormatBool(filter.ReturnAllVersions))
u.RawQuery = q.Encode()
}

View File

@ -29,3 +29,8 @@ type MessageExport struct {
PostOriginalId *string
PostFileIds StringArray
}
type MessageExportCursor struct {
LastPostUpdateAt int64
LastPostId string
}

View File

@ -13,13 +13,13 @@ type MfaSecret struct {
QRCode string `json:"qr_code"`
}
func (me *MfaSecret) ToJson() string {
b, _ := json.Marshal(me)
func (mfa *MfaSecret) ToJson() string {
b, _ := json.Marshal(mfa)
return string(b)
}
func MfaSecretFromJson(data io.Reader) *MfaSecret {
var me *MfaSecret
json.NewDecoder(data).Decode(&me)
return me
var mfa *MfaSecret
json.NewDecoder(data).Decode(&mfa)
return mfa
}

View File

@ -22,5 +22,17 @@ const (
MIGRATION_KEY_SIDEBAR_CATEGORIES_PHASE_2 = "migration_sidebar_categories_phase_2"
MIGRATION_KEY_ADD_CONVERT_CHANNEL_PERMISSIONS = "add_convert_channel_permissions"
MIGRATION_KEY_ADD_SYSTEM_ROLES_PERMISSIONS = "add_system_roles_permissions"
MIGRATION_KEY_ADD_BILLING_PERMISSIONS = "add_billing_permissions"
MIGRATION_KEY_ADD_MANAGE_SHARED_CHANNEL_PERMISSIONS = "manage_shared_channel_permissions"
MIGRATION_KEY_ADD_MANAGE_SECURE_CONNECTIONS_PERMISSIONS = "manage_secure_connections_permissions"
MIGRATION_KEY_ADD_DOWNLOAD_COMPLIANCE_EXPORT_RESULTS = "download_compliance_export_results"
MIGRATION_KEY_ADD_COMPLIANCE_SUBSECTION_PERMISSIONS = "compliance_subsection_permissions"
MIGRATION_KEY_ADD_EXPERIMENTAL_SUBSECTION_PERMISSIONS = "experimental_subsection_permissions"
MIGRATION_KEY_ADD_AUTHENTICATION_SUBSECTION_PERMISSIONS = "authentication_subsection_permissions"
MIGRATION_KEY_ADD_SITE_SUBSECTION_PERMISSIONS = "site_subsection_permissions"
MIGRATION_KEY_ADD_ENVIRONMENT_SUBSECTION_PERMISSIONS = "environment_subsection_permissions"
MIGRATION_KEY_ADD_REPORTING_SUBSECTION_PERMISSIONS = "reporting_subsection_permissions"
MIGRATION_KEY_ADD_TEST_EMAIL_ANCILLARY_PERMISSION = "test_email_ancillary_permission"
MIGRATION_KEY_ADD_ABOUT_SUBSECTION_PERMISSIONS = "about_subsection_permissions"
MIGRATION_KEY_ADD_INTEGRATIONS_SUBSECTION_PERMISSIONS = "integrations_subsection_permissions"
)

View File

@ -53,11 +53,11 @@ func (a *OAuthApp) IsValid() *AppError {
return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.creator_id.app_error", nil, "app_id="+a.Id, http.StatusBadRequest)
}
if len(a.ClientSecret) == 0 || len(a.ClientSecret) > 128 {
if a.ClientSecret == "" || len(a.ClientSecret) > 128 {
return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.client_secret.app_error", nil, "app_id="+a.Id, http.StatusBadRequest)
}
if len(a.Name) == 0 || len(a.Name) > 64 {
if a.Name == "" || len(a.Name) > 64 {
return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.name.app_error", nil, "app_id="+a.Id, http.StatusBadRequest)
}
@ -71,7 +71,7 @@ func (a *OAuthApp) IsValid() *AppError {
}
}
if len(a.Homepage) == 0 || len(a.Homepage) > 256 || !IsValidHttpUrl(a.Homepage) {
if a.Homepage == "" || len(a.Homepage) > 256 || !IsValidHttpUrl(a.Homepage) {
return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.homepage.app_error", nil, "app_id="+a.Id, http.StatusBadRequest)
}
@ -79,7 +79,7 @@ func (a *OAuthApp) IsValid() *AppError {
return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.description.app_error", nil, "app_id="+a.Id, http.StatusBadRequest)
}
if len(a.IconURL) > 0 {
if a.IconURL != "" {
if len(a.IconURL) > 512 || !IsValidHttpUrl(a.IconURL) {
return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.icon_url.app_error", nil, "app_id="+a.Id, http.StatusBadRequest)
}

View File

@ -140,7 +140,7 @@ func (o *OutgoingWebhook) IsValid() *AppError {
return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.user_id.app_error", nil, "", http.StatusBadRequest)
}
if len(o.ChannelId) != 0 && !IsValidId(o.ChannelId) {
if o.ChannelId != "" && !IsValidId(o.ChannelId) {
return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.channel_id.app_error", nil, "", http.StatusBadRequest)
}
@ -154,7 +154,7 @@ func (o *OutgoingWebhook) IsValid() *AppError {
if len(o.TriggerWords) != 0 {
for _, triggerWord := range o.TriggerWords {
if len(triggerWord) == 0 {
if triggerWord == "" {
return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.trigger_words.app_error", nil, "", http.StatusBadRequest)
}
}
@ -215,7 +215,7 @@ func (o *OutgoingWebhook) PreUpdate() {
}
func (o *OutgoingWebhook) TriggerWordExactMatch(word string) bool {
if len(word) == 0 {
if word == "" {
return false
}
@ -229,7 +229,7 @@ func (o *OutgoingWebhook) TriggerWordExactMatch(word string) bool {
}
func (o *OutgoingWebhook) TriggerWordStartsWith(word string) bool {
if len(word) == 0 {
if word == "" {
return false
}
@ -243,7 +243,7 @@ func (o *OutgoingWebhook) TriggerWordStartsWith(word string) bool {
}
func (o *OutgoingWebhook) GetTriggerWord(word string, isExactMatch bool) (triggerWord string) {
if len(word) == 0 {
if word == "" {
return
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
const (
PluginClusterEventSendTypeReliable = CLUSTER_SEND_RELIABLE
PluginClusterEventSendTypeBestEffort = CLUSTER_SEND_BEST_EFFORT
)
// PluginClusterEvent is used to allow intra-cluster plugin communication.
type PluginClusterEvent struct {
// Id is the unique identifier for the event.
Id string
// Data is the event payload.
Data []byte
}
// PluginClusterEventSendOptions defines some properties that apply when sending
// plugin events across a cluster.
type PluginClusterEventSendOptions struct {
// SendType defines the type of communication channel used to send the event.
SendType string
// TargetId identifies the cluster node to which the event should be sent.
// It should match the cluster id of the receiving instance.
// If empty, the event gets broadcasted to all other nodes.
TargetId string
}

View File

@ -21,11 +21,11 @@ type PluginKeyValue struct {
}
func (kv *PluginKeyValue) IsValid() *AppError {
if len(kv.PluginId) == 0 || utf8.RuneCountInString(kv.PluginId) > KEY_VALUE_PLUGIN_ID_MAX_RUNES {
if kv.PluginId == "" || utf8.RuneCountInString(kv.PluginId) > KEY_VALUE_PLUGIN_ID_MAX_RUNES {
return NewAppError("PluginKeyValue.IsValid", "model.plugin_key_value.is_valid.plugin_id.app_error", map[string]interface{}{"Max": KEY_VALUE_KEY_MAX_RUNES, "Min": 0}, "key="+kv.Key, http.StatusBadRequest)
}
if len(kv.Key) == 0 || utf8.RuneCountInString(kv.Key) > KEY_VALUE_KEY_MAX_RUNES {
if kv.Key == "" || utf8.RuneCountInString(kv.Key) > KEY_VALUE_KEY_MAX_RUNES {
return NewAppError("PluginKeyValue.IsValid", "model.plugin_key_value.is_valid.key.app_error", map[string]interface{}{"Max": KEY_VALUE_KEY_MAX_RUNES, "Min": 0}, "key="+kv.Key, http.StatusBadRequest)
}

View File

@ -14,7 +14,7 @@ import (
"sync"
"unicode/utf8"
"github.com/mattermost/mattermost-server/v5/utils/markdown"
"github.com/mattermost/mattermost-server/v5/shared/markdown"
)
const (
@ -45,7 +45,7 @@ const (
POST_EPHEMERAL = "system_ephemeral"
POST_CHANGE_CHANNEL_PRIVACY = "system_change_chan_privacy"
POST_ADD_BOT_TEAMS_CHANNELS = "add_bot_teams_channels"
POST_FILEIDS_MAX_RUNES = 150
POST_FILEIDS_MAX_RUNES = 300
POST_FILENAMES_MAX_RUNES = 4000
POST_HASHTAGS_MAX_RUNES = 1000
POST_MESSAGE_MAX_RUNES_V1 = 4000
@ -96,10 +96,14 @@ type Post struct {
FileIds StringArray `json:"file_ids,omitempty"`
PendingPostId string `json:"pending_post_id" db:"-"`
HasReactions bool `json:"has_reactions,omitempty"`
RemoteId *string `json:"remote_id,omitempty"`
// Transient data populated before sending a post to the client
ReplyCount int64 `json:"reply_count" db:"-"`
Metadata *PostMetadata `json:"metadata,omitempty" db:"-"`
ReplyCount int64 `json:"reply_count" db:"-"`
LastReplyAt int64 `json:"last_reply_at" db:"-"`
Participants []*User `json:"participants" db:"-"`
IsFollowing *bool `json:"is_following,omitempty" db:"-"` // for root posts in collapsed thread mode indicates if the current user is following this thread
Metadata *PostMetadata `json:"metadata,omitempty" db:"-"`
}
type PostEphemeral struct {
@ -163,6 +167,12 @@ type PostForIndexing struct {
ParentCreateAt *int64 `json:"parent_create_at"`
}
type FileForIndexing struct {
FileInfo
ChannelId string `json:"channel_id"`
Content string `json:"content"`
}
// ShallowCopy is an utility function to shallow copy a Post to the given
// destination without touching the internal RWMutex.
func (o *Post) ShallowCopy(dst *Post) error {
@ -194,7 +204,13 @@ func (o *Post) ShallowCopy(dst *Post) error {
dst.PendingPostId = o.PendingPostId
dst.HasReactions = o.HasReactions
dst.ReplyCount = o.ReplyCount
dst.Participants = o.Participants
dst.LastReplyAt = o.LastReplyAt
dst.Metadata = o.Metadata
if o.IsFollowing != nil {
dst.IsFollowing = NewBool(*o.IsFollowing)
}
dst.RemoteId = o.RemoteId
return nil
}
@ -218,17 +234,35 @@ func (o *Post) ToUnsanitizedJson() string {
}
type GetPostsSinceOptions struct {
ChannelId string
Time int64
SkipFetchThreads bool
UserId string
ChannelId string
Time int64
SkipFetchThreads bool
CollapsedThreads bool
CollapsedThreadsExtended bool
SortAscending bool
}
type GetPostsSinceForSyncCursor struct {
LastPostUpdateAt int64
LastPostId string
}
type GetPostsSinceForSyncOptions struct {
ChannelId string
ExcludeRemoteId string
IncludeDeleted bool
}
type GetPostsOptions struct {
ChannelId string
PostId string
Page int
PerPage int
SkipFetchThreads bool
UserId string
ChannelId string
PostId string
Page int
PerPage int
SkipFetchThreads bool
CollapsedThreads bool
CollapsedThreadsExtended bool
}
func PostFromJson(data io.Reader) *Post {
@ -262,19 +296,19 @@ func (o *Post) IsValid(maxPostSize int) *AppError {
return NewAppError("Post.IsValid", "model.post.is_valid.channel_id.app_error", nil, "", http.StatusBadRequest)
}
if !(IsValidId(o.RootId) || len(o.RootId) == 0) {
if !(IsValidId(o.RootId) || o.RootId == "") {
return NewAppError("Post.IsValid", "model.post.is_valid.root_id.app_error", nil, "", http.StatusBadRequest)
}
if !(IsValidId(o.ParentId) || len(o.ParentId) == 0) {
if !(IsValidId(o.ParentId) || o.ParentId == "") {
return NewAppError("Post.IsValid", "model.post.is_valid.parent_id.app_error", nil, "", http.StatusBadRequest)
}
if len(o.ParentId) == 26 && len(o.RootId) == 0 {
if len(o.ParentId) == 26 && o.RootId == "" {
return NewAppError("Post.IsValid", "model.post.is_valid.root_parent.app_error", nil, "", http.StatusBadRequest)
}
if !(len(o.OriginalId) == 26 || len(o.OriginalId) == 0) {
if !(len(o.OriginalId) == 26 || o.OriginalId == "") {
return NewAppError("Post.IsValid", "model.post.is_valid.original_id.app_error", nil, "", http.StatusBadRequest)
}
@ -337,6 +371,9 @@ func (o *Post) IsValid(maxPostSize int) *AppError {
}
func (o *Post) SanitizeProps() {
if o == nil {
return
}
membersToSanitize := []string{
PROPS_ADD_CHANNEL_MEMBER,
}
@ -346,6 +383,9 @@ func (o *Post) SanitizeProps() {
o.DelProp(member)
}
}
for _, p := range o.Participants {
p.Sanitize(map[string]bool{})
}
}
func (o *Post) PreSave() {
@ -432,6 +472,19 @@ func (o *Post) IsSystemMessage() bool {
return len(o.Type) >= len(POST_SYSTEM_MESSAGE_PREFIX) && o.Type[:len(POST_SYSTEM_MESSAGE_PREFIX)] == POST_SYSTEM_MESSAGE_PREFIX
}
// IsRemote returns true if the post originated on a remote cluster.
func (o *Post) IsRemote() bool {
return o.RemoteId != nil && *o.RemoteId != ""
}
// GetRemoteID safely returns the remoteID or empty string if not remote.
func (o *Post) GetRemoteID() string {
if o.RemoteId != nil {
return *o.RemoteId
}
return ""
}
func (o *Post) IsJoinLeaveMessage() bool {
return o.Type == POST_JOIN_LEAVE ||
o.Type == POST_ADD_REMOVE ||
@ -552,6 +605,25 @@ func (o *Post) Attachments() []*SlackAttachment {
if enc, err := json.Marshal(attachment); err == nil {
var decoded SlackAttachment
if json.Unmarshal(enc, &decoded) == nil {
// Ignoring nil actions
i := 0
for _, action := range decoded.Actions {
if action != nil {
decoded.Actions[i] = action
i++
}
}
decoded.Actions = decoded.Actions[:i]
// Ignoring nil fields
i = 0
for _, field := range decoded.Fields {
if field != nil {
decoded.Fields[i] = field
i++
}
}
decoded.Fields = decoded.Fields[:i]
ret = append(ret, &decoded)
}
}
@ -665,3 +737,15 @@ func RewriteImageURLs(message string, f func(string) string) string {
return string(result)
}
func (o *Post) IsFromOAuthBot() bool {
props := o.GetProps()
return props["from_webhook"] == "true" && props["override_username"] != ""
}
func (o *Post) ToNilIfInvalid() *Post {
if o.Id == "" {
return nil
}
return o
}

View File

@ -27,6 +27,11 @@ func NewPostList() *PostList {
func (o *PostList) ToSlice() []*Post {
var posts []*Post
if l := len(o.Posts); l > 0 {
posts = make([]*Post, 0, l)
}
for _, id := range o.Order {
posts = append(posts, o.Posts[id])
}
@ -58,9 +63,8 @@ func (o *PostList) ToJson() string {
b, err := json.Marshal(&copy)
if err != nil {
return ""
} else {
return string(b)
}
return string(b)
}
func (o *PostList) MakeNonNil() {

View File

@ -28,9 +28,8 @@ func (o *PostSearchResults) ToJson() string {
b, err := json.Marshal(&copy)
if err != nil {
return ""
} else {
return string(b)
}
return string(b)
}
func PostSearchResultsFromJson(data io.Reader) *PostSearchResults {

View File

@ -21,12 +21,14 @@ const (
PREFERENCE_CATEGORY_FAVORITE_CHANNEL = "favorite_channel"
PREFERENCE_CATEGORY_SIDEBAR_SETTINGS = "sidebar_settings"
PREFERENCE_CATEGORY_DISPLAY_SETTINGS = "display_settings"
PREFERENCE_NAME_CHANNEL_DISPLAY_MODE = "channel_display_mode"
PREFERENCE_NAME_COLLAPSE_SETTING = "collapse_previews"
PREFERENCE_NAME_MESSAGE_DISPLAY = "message_display"
PREFERENCE_NAME_NAME_FORMAT = "name_format"
PREFERENCE_NAME_USE_MILITARY_TIME = "use_military_time"
PREFERENCE_CATEGORY_DISPLAY_SETTINGS = "display_settings"
PREFERENCE_NAME_COLLAPSED_THREADS_ENABLED = "collapsed_reply_threads"
PREFERENCE_NAME_CHANNEL_DISPLAY_MODE = "channel_display_mode"
PREFERENCE_NAME_COLLAPSE_SETTING = "collapse_previews"
PREFERENCE_NAME_MESSAGE_DISPLAY = "message_display"
PREFERENCE_NAME_NAME_FORMAT = "name_format"
PREFERENCE_NAME_USE_MILITARY_TIME = "use_military_time"
PREFERENCE_RECOMMENDED_NEXT_STEPS = "recommended_next_steps"
PREFERENCE_CATEGORY_THEME = "theme"
// the name for theme props is the team id
@ -38,6 +40,12 @@ const (
PREFERENCE_NAME_LAST_CHANNEL = "channel"
PREFERENCE_NAME_LAST_TEAM = "team"
PREFERENCE_CATEGORY_CUSTOM_STATUS = "custom_status"
PREFERENCE_NAME_RECENT_CUSTOM_STATUSES = "recent_custom_statuses"
PREFERENCE_NAME_CUSTOM_STATUS_TUTORIAL_STATE = "custom_status_tutorial_state"
PREFERENCE_CUSTOM_STATUS_MODAL_VIEWED = "custom_status_modal_viewed"
PREFERENCE_CATEGORY_NOTIFICATIONS = "notifications"
PREFERENCE_NAME_EMAIL_INTERVAL = "email_interval"
@ -73,7 +81,7 @@ func (o *Preference) IsValid() *AppError {
return NewAppError("Preference.IsValid", "model.preference.is_valid.id.app_error", nil, "user_id="+o.UserId, http.StatusBadRequest)
}
if len(o.Category) == 0 || len(o.Category) > 32 {
if o.Category == "" || len(o.Category) > 32 {
return NewAppError("Preference.IsValid", "model.preference.is_valid.category.app_error", nil, "category="+o.Category, http.StatusBadRequest)
}

View File

@ -19,9 +19,8 @@ func PreferencesFromJson(data io.Reader) (Preferences, error) {
decoder := json.NewDecoder(data)
var o Preferences
err := decoder.Decode(&o)
if err == nil {
return o, nil
} else {
if err != nil {
return nil, err
}
return o, nil
}

View File

@ -5,8 +5,9 @@ package model
import (
"encoding/json"
"github.com/pkg/errors"
"io"
"github.com/pkg/errors"
)
type ProductNotices []ProductNotice
@ -39,18 +40,19 @@ func (n *ProductNotice) TeamAdminOnly() bool {
}
type Conditions struct {
Audience *NoticeAudience `json:"audience,omitempty"`
ClientType *NoticeClientType `json:"clientType,omitempty"` // Only show the notice on specific clients. Defaults to 'all'
DesktopVersion []string `json:"desktopVersion,omitempty"` // What desktop client versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"]
DisplayDate *string `json:"displayDate,omitempty"` // When to display the notice.; Examples:; "2020-03-01T00:00:00Z" - show on specified date; ">= 2020-03-01T00:00:00Z" - show after specified date; "< 2020-03-01T00:00:00Z" - show before the specified date; "> 2020-03-01T00:00:00Z <= 2020-04-01T00:00:00Z" - show only between the specified dates
InstanceType *NoticeInstanceType `json:"instanceType,omitempty"`
MobileVersion []string `json:"mobileVersion,omitempty"` // What mobile client versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"]
NumberOfPosts *int64 `json:"numberOfPosts,omitempty"` // Only show the notice when server has more than specified number of posts
NumberOfUsers *int64 `json:"numberOfUsers,omitempty"` // Only show the notice when server has more than specified number of users
ServerConfig map[string]interface{} `json:"serverConfig,omitempty"` // Map of mattermost server config paths and their values. Notice will be displayed only if; the values match the target server config; Example: serverConfig: { "PluginSettings.Enable": true, "GuestAccountsSettings.Enable":; false }
ServerVersion []string `json:"serverVersion,omitempty"` // What server versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"]
Sku *NoticeSKU `json:"sku,omitempty"`
UserConfig map[string]interface{} `json:"userConfig,omitempty"` // Map of user's settings and their values. Notice will be displayed only if the values; match the viewing users' config; Example: userConfig: { "new_sidebar.disabled": true }
Audience *NoticeAudience `json:"audience,omitempty"`
ClientType *NoticeClientType `json:"clientType,omitempty"` // Only show the notice on specific clients. Defaults to 'all'
DesktopVersion []string `json:"desktopVersion,omitempty"` // What desktop client versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"]
DisplayDate *string `json:"displayDate,omitempty"` // When to display the notice.; Examples:; "2020-03-01T00:00:00Z" - show on specified date; ">= 2020-03-01T00:00:00Z" - show after specified date; "< 2020-03-01T00:00:00Z" - show before the specified date; "> 2020-03-01T00:00:00Z <= 2020-04-01T00:00:00Z" - show only between the specified dates
InstanceType *NoticeInstanceType `json:"instanceType,omitempty"`
MobileVersion []string `json:"mobileVersion,omitempty"` // What mobile client versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"]
NumberOfPosts *int64 `json:"numberOfPosts,omitempty"` // Only show the notice when server has more than specified number of posts
NumberOfUsers *int64 `json:"numberOfUsers,omitempty"` // Only show the notice when server has more than specified number of users
ServerConfig map[string]interface{} `json:"serverConfig,omitempty"` // Map of mattermost server config paths and their values. Notice will be displayed only if; the values match the target server config; Example: serverConfig: { "PluginSettings.Enable": true, "GuestAccountsSettings.Enable":; false }
ServerVersion []string `json:"serverVersion,omitempty"` // What server versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"]
Sku *NoticeSKU `json:"sku,omitempty"`
UserConfig map[string]interface{} `json:"userConfig,omitempty"` // Map of user's settings and their values. Notice will be displayed only if the values; match the viewing users' config; Example: userConfig: { "new_sidebar.disabled": true }
DeprecatingDependency *ExternalDependency `json:"deprecating_dependency,omitempty"` // External dependency which is going to be deprecated
}
type NoticeMessageInternal struct {
@ -211,3 +213,8 @@ type ProductNoticeViewState struct {
Viewed int32
Timestamp int64
}
type ExternalDependency struct {
Name string `json:"name"`
MinimumVersion string `json:"minimum_version"`
}

View File

@ -70,23 +70,23 @@ type PushNotification struct {
IsIdLoaded bool `json:"is_id_loaded"`
}
func (me *PushNotification) ToJson() string {
b, _ := json.Marshal(me)
func (pn *PushNotification) ToJson() string {
b, _ := json.Marshal(pn)
return string(b)
}
func (me *PushNotification) DeepCopy() *PushNotification {
copy := *me
func (pn *PushNotification) DeepCopy() *PushNotification {
copy := *pn
return &copy
}
func (me *PushNotification) SetDeviceIdAndPlatform(deviceId string) {
func (pn *PushNotification) SetDeviceIdAndPlatform(deviceId string) {
index := strings.Index(deviceId, ":")
if index > -1 {
me.Platform = deviceId[:index]
me.DeviceId = deviceId[index+1:]
pn.Platform = deviceId[:index]
pn.DeviceId = deviceId[index+1:]
}
}
@ -94,11 +94,11 @@ func PushNotificationFromJson(data io.Reader) (*PushNotification, error) {
if data == nil {
return nil, errors.New("push notification data can't be nil")
}
var me *PushNotification
if err := json.NewDecoder(data).Decode(&me); err != nil {
var pn *PushNotification
if err := json.NewDecoder(data).Decode(&pn); err != nil {
return nil, err
}
return me, nil
return pn, nil
}
func PushNotificationAckFromJson(data io.Reader) (*PushNotificationAck, error) {

View File

@ -37,8 +37,8 @@ func NewErrorPushResponse(message string) PushResponse {
return m
}
func (me *PushResponse) ToJson() string {
b, _ := json.Marshal(me)
func (pr *PushResponse) ToJson() string {
b, _ := json.Marshal(pr)
return string(b)
}
@ -48,7 +48,6 @@ func PushResponseFromJson(data io.Reader) PushResponse {
var objmap PushResponse
if err := decoder.Decode(&objmap); err != nil {
return make(map[string]string)
} else {
return objmap
}
return objmap
}

View File

@ -11,10 +11,13 @@ import (
)
type Reaction struct {
UserId string `json:"user_id"`
PostId string `json:"post_id"`
EmojiName string `json:"emoji_name"`
CreateAt int64 `json:"create_at"`
UserId string `json:"user_id"`
PostId string `json:"post_id"`
EmojiName string `json:"emoji_name"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
RemoteId *string `json:"remote_id"`
}
func (o *Reaction) ToJson() string {
@ -27,9 +30,8 @@ func ReactionFromJson(data io.Reader) *Reaction {
if err := json.NewDecoder(data).Decode(&o); err != nil {
return nil
} else {
return &o
}
return &o
}
func ReactionsToJson(o []*Reaction) string {
@ -48,9 +50,8 @@ func MapPostIdToReactionsFromJson(data io.Reader) map[string][]*Reaction {
var objmap map[string][]*Reaction
if err := decoder.Decode(&objmap); err != nil {
return make(map[string][]*Reaction)
} else {
return objmap
}
return objmap
}
func ReactionsFromJson(data io.Reader) []*Reaction {
@ -58,9 +59,8 @@ func ReactionsFromJson(data io.Reader) []*Reaction {
if err := json.NewDecoder(data).Decode(&o); err != nil {
return nil
} else {
return o
}
return o
}
func (o *Reaction) IsValid() *AppError {
@ -74,7 +74,7 @@ func (o *Reaction) IsValid() *AppError {
validName := regexp.MustCompile(`^[a-zA-Z0-9\-\+_]+$`)
if len(o.EmojiName) == 0 || len(o.EmojiName) > EMOJI_NAME_MAX_LENGTH || !validName.MatchString(o.EmojiName) {
if o.EmojiName == "" || len(o.EmojiName) > EMOJI_NAME_MAX_LENGTH || !validName.MatchString(o.EmojiName) {
return NewAppError("Reaction.IsValid", "model.reaction.is_valid.emoji_name.app_error", nil, "emoji_name="+o.EmojiName, http.StatusBadRequest)
}
@ -82,6 +82,10 @@ func (o *Reaction) IsValid() *AppError {
return NewAppError("Reaction.IsValid", "model.reaction.is_valid.create_at.app_error", nil, "", http.StatusBadRequest)
}
if o.UpdateAt == 0 {
return NewAppError("Reaction.IsValid", "model.reaction.is_valid.update_at.app_error", nil, "", http.StatusBadRequest)
}
return nil
}
@ -89,4 +93,18 @@ func (o *Reaction) PreSave() {
if o.CreateAt == 0 {
o.CreateAt = GetMillis()
}
o.UpdateAt = GetMillis()
o.DeleteAt = 0
if o.RemoteId == nil {
o.RemoteId = NewString("")
}
}
func (o *Reaction) PreUpdate() {
o.UpdateAt = GetMillis()
if o.RemoteId == nil {
o.RemoteId = NewString("")
}
}

View File

@ -0,0 +1,355 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/json"
"errors"
"io"
"net/http"
"regexp"
"strings"
"golang.org/x/crypto/scrypt"
)
const (
RemoteOfflineAfterMillis = 1000 * 60 * 5 // 5 minutes
RemoteNameMinLength = 1
RemoteNameMaxLength = 64
)
var (
validRemoteNameChars = regexp.MustCompile(`^[a-zA-Z0-9\.\-\_]+$`)
)
type RemoteCluster struct {
RemoteId string `json:"remote_id"`
RemoteTeamId string `json:"remote_team_id"`
Name string `json:"name"`
DisplayName string `json:"display_name"`
SiteURL string `json:"site_url"`
CreateAt int64 `json:"create_at"`
LastPingAt int64 `json:"last_ping_at"`
Token string `json:"token"`
RemoteToken string `json:"remote_token"`
Topics string `json:"topics"`
CreatorId string `json:"creator_id"`
}
func (rc *RemoteCluster) PreSave() {
if rc.RemoteId == "" {
rc.RemoteId = NewId()
}
if rc.DisplayName == "" {
rc.DisplayName = rc.Name
}
rc.Name = SanitizeUnicode(rc.Name)
rc.DisplayName = SanitizeUnicode(rc.DisplayName)
rc.Name = NormalizeRemoteName(rc.Name)
if rc.Token == "" {
rc.Token = NewId()
}
if rc.CreateAt == 0 {
rc.CreateAt = GetMillis()
}
rc.fixTopics()
}
func (rc *RemoteCluster) IsValid() *AppError {
if !IsValidId(rc.RemoteId) {
return NewAppError("RemoteCluster.IsValid", "model.cluster.is_valid.id.app_error", nil, "id="+rc.RemoteId, http.StatusBadRequest)
}
if !IsValidRemoteName(rc.Name) {
return NewAppError("RemoteCluster.IsValid", "model.cluster.is_valid.name.app_error", nil, "name="+rc.Name, http.StatusBadRequest)
}
if rc.CreateAt == 0 {
return NewAppError("RemoteCluster.IsValid", "model.cluster.is_valid.create_at.app_error", nil, "create_at=0", http.StatusBadRequest)
}
if !IsValidId(rc.CreatorId) {
return NewAppError("RemoteCluster.IsValid", "model.cluster.is_valid.id.app_error", nil, "creator_id="+rc.CreatorId, http.StatusBadRequest)
}
return nil
}
func IsValidRemoteName(s string) bool {
if len(s) < RemoteNameMinLength || len(s) > RemoteNameMaxLength {
return false
}
return validRemoteNameChars.MatchString(s)
}
func (rc *RemoteCluster) PreUpdate() {
if rc.DisplayName == "" {
rc.DisplayName = rc.Name
}
rc.Name = SanitizeUnicode(rc.Name)
rc.DisplayName = SanitizeUnicode(rc.DisplayName)
rc.Name = NormalizeRemoteName(rc.Name)
rc.fixTopics()
}
func (rc *RemoteCluster) IsOnline() bool {
return rc.LastPingAt > GetMillis()-RemoteOfflineAfterMillis
}
// fixTopics ensures all topics are separated by one, and only one, space.
func (rc *RemoteCluster) fixTopics() {
trimmed := strings.TrimSpace(rc.Topics)
if trimmed == "" || trimmed == "*" {
rc.Topics = trimmed
return
}
var sb strings.Builder
sb.WriteString(" ")
ss := strings.Split(rc.Topics, " ")
for _, c := range ss {
cc := strings.TrimSpace(c)
if cc != "" {
sb.WriteString(cc)
sb.WriteString(" ")
}
}
rc.Topics = sb.String()
}
func (rc *RemoteCluster) ToJSON() (string, error) {
b, err := json.Marshal(rc)
if err != nil {
return "", err
}
return string(b), nil
}
func (rc *RemoteCluster) ToRemoteClusterInfo() RemoteClusterInfo {
return RemoteClusterInfo{
Name: rc.Name,
DisplayName: rc.DisplayName,
CreateAt: rc.CreateAt,
LastPingAt: rc.LastPingAt,
}
}
func NormalizeRemoteName(name string) string {
return strings.ToLower(name)
}
func RemoteClusterFromJSON(data io.Reader) (*RemoteCluster, *AppError) {
var rc RemoteCluster
err := json.NewDecoder(data).Decode(&rc)
if err != nil {
return nil, NewAppError("RemoteClusterFromJSON", "model.utils.decode_json.app_error", nil, err.Error(), http.StatusBadRequest)
}
return &rc, nil
}
// RemoteClusterInfo provides a subset of RemoteCluster fields suitable for sending to clients.
type RemoteClusterInfo struct {
Name string `json:"name"`
DisplayName string `json:"display_name"`
CreateAt int64 `json:"create_at"`
LastPingAt int64 `json:"last_ping_at"`
}
// RemoteClusterFrame wraps a `RemoteClusterMsg` with credentials specific to a remote cluster.
type RemoteClusterFrame struct {
RemoteId string `json:"remote_id"`
Msg RemoteClusterMsg `json:"msg"`
}
func (f *RemoteClusterFrame) IsValid() *AppError {
if !IsValidId(f.RemoteId) {
return NewAppError("RemoteClusterFrame.IsValid", "api.remote_cluster.invalid_id.app_error", nil, "RemoteId="+f.RemoteId, http.StatusBadRequest)
}
if err := f.Msg.IsValid(); err != nil {
return err
}
return nil
}
func RemoteClusterFrameFromJSON(data io.Reader) (*RemoteClusterFrame, *AppError) {
var frame RemoteClusterFrame
err := json.NewDecoder(data).Decode(&frame)
if err != nil {
return nil, NewAppError("RemoteClusterFrameFromJSON", "model.utils.decode_json.app_error", nil, err.Error(), http.StatusBadRequest)
}
return &frame, nil
}
// RemoteClusterMsg represents a message that is sent and received between clusters.
// These are processed and routed via the RemoteClusters service.
type RemoteClusterMsg struct {
Id string `json:"id"`
Topic string `json:"topic"`
CreateAt int64 `json:"create_at"`
Payload json.RawMessage `json:"payload"`
}
func NewRemoteClusterMsg(topic string, payload json.RawMessage) RemoteClusterMsg {
return RemoteClusterMsg{
Id: NewId(),
Topic: topic,
CreateAt: GetMillis(),
Payload: payload,
}
}
func (m RemoteClusterMsg) IsValid() *AppError {
if !IsValidId(m.Id) {
return NewAppError("RemoteClusterMsg.IsValid", "api.remote_cluster.invalid_id.app_error", nil, "Id="+m.Id, http.StatusBadRequest)
}
if m.Topic == "" {
return NewAppError("RemoteClusterMsg.IsValid", "api.remote_cluster.invalid_topic.app_error", nil, "Topic empty", http.StatusBadRequest)
}
if len(m.Payload) == 0 {
return NewAppError("RemoteClusterMsg.IsValid", "api.context.invalid_body_param.app_error", map[string]interface{}{"Name": "PayLoad"}, "", http.StatusBadRequest)
}
return nil
}
func RemoteClusterMsgFromJSON(data io.Reader) (RemoteClusterMsg, *AppError) {
var msg RemoteClusterMsg
err := json.NewDecoder(data).Decode(&msg)
if err != nil {
return RemoteClusterMsg{}, NewAppError("RemoteClusterMsgFromJSON", "model.utils.decode_json.app_error", nil, err.Error(), http.StatusBadRequest)
}
return msg, nil
}
// RemoteClusterPing represents a ping that is sent and received between clusters
// to indicate a connection is alive. This is the payload for a `RemoteClusterMsg`.
type RemoteClusterPing struct {
SentAt int64 `json:"sent_at"`
RecvAt int64 `json:"recv_at"`
}
func RemoteClusterPingFromRawJSON(raw json.RawMessage) (RemoteClusterPing, *AppError) {
var ping RemoteClusterPing
err := json.Unmarshal(raw, &ping)
if err != nil {
return RemoteClusterPing{}, NewAppError("RemoteClusterPingFromRawJSON", "model.utils.decode_json.app_error", nil, err.Error(), http.StatusBadRequest)
}
return ping, nil
}
// RemoteClusterInvite represents an invitation to establish a simple trust with a remote cluster.
type RemoteClusterInvite struct {
RemoteId string `json:"remote_id"`
RemoteTeamId string `json:"remote_team_id"`
SiteURL string `json:"site_url"`
Token string `json:"token"`
}
func RemoteClusterInviteFromRawJSON(raw json.RawMessage) (*RemoteClusterInvite, *AppError) {
var invite RemoteClusterInvite
err := json.Unmarshal(raw, &invite)
if err != nil {
return nil, NewAppError("RemoteClusterInviteFromRawJSON", "model.utils.decode_json.app_error", nil, err.Error(), http.StatusBadRequest)
}
return &invite, nil
}
func (rci *RemoteClusterInvite) Encrypt(password string) ([]byte, error) {
raw, err := json.Marshal(&rci)
if err != nil {
return nil, err
}
// create random salt to be prepended to the blob.
salt := make([]byte, 16)
if _, err = io.ReadFull(rand.Reader, salt); err != nil {
return nil, err
}
key, err := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key[:])
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
// create random nonce
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
// prefix the nonce to the cyphertext so we don't need to keep track of it.
sealed := gcm.Seal(nonce, nonce, raw, nil)
return append(salt, sealed...), nil
}
func (rci *RemoteClusterInvite) Decrypt(encrypted []byte, password string) error {
if len(encrypted) <= 16 {
return errors.New("invalid length")
}
// first 16 bytes is the salt that was used to derive a key
salt := encrypted[:16]
encrypted = encrypted[16:]
key, err := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32)
if err != nil {
return err
}
block, err := aes.NewCipher(key[:])
if err != nil {
return err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return err
}
// nonce was prefixed to the cyphertext when encrypting so we need to extract it.
nonceSize := gcm.NonceSize()
nonce, cyphertext := encrypted[:nonceSize], encrypted[nonceSize:]
plain, err := gcm.Open(nil, nonce, cyphertext, nil)
if err != nil {
return err
}
// try to unmarshall the decrypted JSON to this invite struct.
return json.Unmarshal(plain, &rci)
}
// RemoteClusterQueryFilter provides filter criteria for RemoteClusterStore.GetAll
type RemoteClusterQueryFilter struct {
ExcludeOffline bool
InChannel string
NotInChannel string
Topic string
CreatorId string
OnlyConfirmed bool
}

View File

@ -47,6 +47,12 @@ func init() {
// When updating the values here, the values in mattermost-redux must also be updated.
SysconsoleAncillaryPermissions = map[string][]*Permission{
PERMISSION_SYSCONSOLE_READ_ABOUT_EDITION_AND_LICENSE.Id: {
PERMISSION_READ_LICENSE_INFORMATION,
},
PERMISSION_SYSCONSOLE_WRITE_ABOUT_EDITION_AND_LICENSE.Id: {
PERMISSION_MANAGE_LICENSE_INFORMATION,
},
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS.Id: {
PERMISSION_READ_PUBLIC_CHANNEL,
PERMISSION_READ_CHANNEL,
@ -55,19 +61,44 @@ func init() {
},
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_USERS.Id: {
PERMISSION_READ_OTHER_USERS_TEAMS,
PERMISSION_GET_ANALYTICS,
},
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS.Id: {
PERMISSION_LIST_PRIVATE_TEAMS,
PERMISSION_LIST_PUBLIC_TEAMS,
PERMISSION_VIEW_TEAM,
},
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT.Id: {
PERMISSION_READ_JOBS,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_ELASTICSEARCH.Id: {
PERMISSION_READ_ELASTICSEARCH_POST_INDEXING_JOB,
PERMISSION_READ_ELASTICSEARCH_POST_AGGREGATION_JOB,
},
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION.Id: {
PERMISSION_READ_JOBS,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_WEB_SERVER.Id: {
PERMISSION_TEST_SITE_URL,
PERMISSION_RELOAD_CONFIG,
PERMISSION_INVALIDATE_CACHES,
},
PERMISSION_SYSCONSOLE_READ_REPORTING.Id: {
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_DATABASE.Id: {
PERMISSION_RECYCLE_DATABASE_CONNECTIONS,
},
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_ELASTICSEARCH.Id: {
PERMISSION_TEST_ELASTICSEARCH,
PERMISSION_CREATE_ELASTICSEARCH_POST_INDEXING_JOB,
PERMISSION_CREATE_ELASTICSEARCH_POST_AGGREGATION_JOB,
PERMISSION_PURGE_ELASTICSEARCH_INDEXES,
},
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_FILE_STORAGE.Id: {
PERMISSION_TEST_S3,
},
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_SMTP.Id: {
PERMISSION_TEST_EMAIL,
},
PERMISSION_SYSCONSOLE_READ_REPORTING_SERVER_LOGS.Id: {
PERMISSION_GET_LOGS,
},
PERMISSION_SYSCONSOLE_READ_REPORTING_SITE_STATISTICS.Id: {
PERMISSION_GET_ANALYTICS,
},
PERMISSION_SYSCONSOLE_READ_REPORTING_TEAM_STATISTICS.Id: {
PERMISSION_VIEW_TEAM,
},
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_USERS.Id: {
@ -102,12 +133,54 @@ func init() {
PERMISSION_CONVERT_PUBLIC_CHANNEL_TO_PRIVATE,
PERMISSION_CONVERT_PRIVATE_CHANNEL_TO_PUBLIC,
},
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT.Id: {
PERMISSION_MANAGE_JOBS,
},
PERMISSION_SYSCONSOLE_WRITE_SITE.Id: {
PERMISSION_SYSCONSOLE_WRITE_SITE_CUSTOMIZATION.Id: {
PERMISSION_EDIT_BRAND,
},
PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_DATA_RETENTION_POLICY.Id: {
PERMISSION_CREATE_DATA_RETENTION_JOB,
},
PERMISSION_SYSCONSOLE_READ_COMPLIANCE_DATA_RETENTION_POLICY.Id: {
PERMISSION_READ_DATA_RETENTION_JOB,
},
PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_COMPLIANCE_EXPORT.Id: {
PERMISSION_CREATE_COMPLIANCE_EXPORT_JOB,
PERMISSION_DOWNLOAD_COMPLIANCE_EXPORT_RESULT,
},
PERMISSION_SYSCONSOLE_READ_COMPLIANCE_COMPLIANCE_EXPORT.Id: {
PERMISSION_READ_COMPLIANCE_EXPORT_JOB,
PERMISSION_DOWNLOAD_COMPLIANCE_EXPORT_RESULT,
},
PERMISSION_SYSCONSOLE_READ_COMPLIANCE_CUSTOM_TERMS_OF_SERVICE.Id: {
PERMISSION_READ_AUDITS,
},
PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL_BLEVE.Id: {
PERMISSION_CREATE_POST_BLEVE_INDEXES_JOB,
PERMISSION_PURGE_BLEVE_INDEXES,
},
PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_LDAP.Id: {
PERMISSION_CREATE_LDAP_SYNC_JOB,
PERMISSION_ADD_LDAP_PUBLIC_CERT,
PERMISSION_REMOVE_LDAP_PUBLIC_CERT,
PERMISSION_ADD_LDAP_PRIVATE_CERT,
PERMISSION_REMOVE_LDAP_PRIVATE_CERT,
},
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_LDAP.Id: {
PERMISSION_TEST_LDAP,
PERMISSION_READ_LDAP_SYNC_JOB,
},
PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_EMAIL.Id: {
PERMISSION_INVALIDATE_EMAIL_INVITE,
},
PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_SAML.Id: {
PERMISSION_GET_SAML_METADATA_FROM_IDP,
PERMISSION_ADD_SAML_PUBLIC_CERT,
PERMISSION_ADD_SAML_PRIVATE_CERT,
PERMISSION_ADD_SAML_IDP_CERT,
PERMISSION_REMOVE_SAML_PUBLIC_CERT,
PERMISSION_REMOVE_SAML_PRIVATE_CERT,
PERMISSION_REMOVE_SAML_IDP_CERT,
PERMISSION_GET_SAML_CERT_STATUS,
},
}
SystemUserManagerDefaultPermissions = []string{
@ -118,29 +191,76 @@ func init() {
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS.Id,
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_TEAMS.Id,
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_CHANNELS.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SIGNUP.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_EMAIL.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_PASSWORD.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_MFA.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_LDAP.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SAML.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_OPENID.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_GUEST_ACCESS.Id,
}
SystemReadOnlyAdminDefaultPermissions = []string{
PERMISSION_SYSCONSOLE_READ_ABOUT.Id,
PERMISSION_SYSCONSOLE_READ_REPORTING.Id,
PERMISSION_SYSCONSOLE_READ_ABOUT_EDITION_AND_LICENSE.Id,
PERMISSION_SYSCONSOLE_READ_REPORTING_SITE_STATISTICS.Id,
PERMISSION_SYSCONSOLE_READ_REPORTING_TEAM_STATISTICS.Id,
PERMISSION_SYSCONSOLE_READ_REPORTING_SERVER_LOGS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_USERS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_GROUPS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_PERMISSIONS.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT.Id,
PERMISSION_SYSCONSOLE_READ_SITE.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_WEB_SERVER.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DATABASE.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_ELASTICSEARCH.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_FILE_STORAGE.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_IMAGE_PROXY.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SMTP.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PUSH_NOTIFICATION_SERVER.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_HIGH_AVAILABILITY.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_RATE_LIMITING.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_LOGGING.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SESSION_LENGTHS.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PERFORMANCE_MONITORING.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DEVELOPER.Id,
PERMISSION_SYSCONSOLE_READ_SITE_CUSTOMIZATION.Id,
PERMISSION_SYSCONSOLE_READ_SITE_LOCALIZATION.Id,
PERMISSION_SYSCONSOLE_READ_SITE_USERS_AND_TEAMS.Id,
PERMISSION_SYSCONSOLE_READ_SITE_NOTIFICATIONS.Id,
PERMISSION_SYSCONSOLE_READ_SITE_ANNOUNCEMENT_BANNER.Id,
PERMISSION_SYSCONSOLE_READ_SITE_EMOJI.Id,
PERMISSION_SYSCONSOLE_READ_SITE_POSTS.Id,
PERMISSION_SYSCONSOLE_READ_SITE_FILE_SHARING_AND_DOWNLOADS.Id,
PERMISSION_SYSCONSOLE_READ_SITE_PUBLIC_LINKS.Id,
PERMISSION_SYSCONSOLE_READ_SITE_NOTICES.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SIGNUP.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_EMAIL.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_PASSWORD.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_MFA.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_LDAP.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SAML.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_OPENID.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_GUEST_ACCESS.Id,
PERMISSION_SYSCONSOLE_READ_PLUGINS.Id,
PERMISSION_SYSCONSOLE_READ_COMPLIANCE.Id,
PERMISSION_SYSCONSOLE_READ_INTEGRATIONS.Id,
PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL.Id,
PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_INTEGRATION_MANAGEMENT.Id,
PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_BOT_ACCOUNTS.Id,
PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_GIF.Id,
PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_CORS.Id,
PERMISSION_SYSCONSOLE_READ_COMPLIANCE_DATA_RETENTION_POLICY.Id,
PERMISSION_SYSCONSOLE_READ_COMPLIANCE_COMPLIANCE_EXPORT.Id,
PERMISSION_SYSCONSOLE_READ_COMPLIANCE_COMPLIANCE_MONITORING.Id,
PERMISSION_SYSCONSOLE_READ_COMPLIANCE_CUSTOM_TERMS_OF_SERVICE.Id,
PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_FEATURES.Id,
PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_FEATURE_FLAGS.Id,
PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_BLEVE.Id,
}
SystemManagerDefaultPermissions = []string{
PERMISSION_SYSCONSOLE_READ_ABOUT.Id,
PERMISSION_SYSCONSOLE_READ_REPORTING.Id,
PERMISSION_SYSCONSOLE_READ_ABOUT_EDITION_AND_LICENSE.Id,
PERMISSION_SYSCONSOLE_READ_REPORTING_SITE_STATISTICS.Id,
PERMISSION_SYSCONSOLE_READ_REPORTING_TEAM_STATISTICS.Id,
PERMISSION_SYSCONSOLE_READ_REPORTING_SERVER_LOGS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_GROUPS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS.Id,
@ -149,20 +269,75 @@ func init() {
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_TEAMS.Id,
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_CHANNELS.Id,
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_PERMISSIONS.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT.Id,
PERMISSION_SYSCONSOLE_READ_SITE.Id,
PERMISSION_SYSCONSOLE_WRITE_SITE.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_WEB_SERVER.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DATABASE.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_ELASTICSEARCH.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_FILE_STORAGE.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_IMAGE_PROXY.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SMTP.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PUSH_NOTIFICATION_SERVER.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_HIGH_AVAILABILITY.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_RATE_LIMITING.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_LOGGING.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SESSION_LENGTHS.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PERFORMANCE_MONITORING.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DEVELOPER.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_WEB_SERVER.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_DATABASE.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_ELASTICSEARCH.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_FILE_STORAGE.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_IMAGE_PROXY.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_SMTP.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_PUSH_NOTIFICATION_SERVER.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_HIGH_AVAILABILITY.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_RATE_LIMITING.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_LOGGING.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_SESSION_LENGTHS.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_PERFORMANCE_MONITORING.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_DEVELOPER.Id,
PERMISSION_SYSCONSOLE_READ_SITE_CUSTOMIZATION.Id,
PERMISSION_SYSCONSOLE_WRITE_SITE_CUSTOMIZATION.Id,
PERMISSION_SYSCONSOLE_READ_SITE_LOCALIZATION.Id,
PERMISSION_SYSCONSOLE_WRITE_SITE_LOCALIZATION.Id,
PERMISSION_SYSCONSOLE_READ_SITE_USERS_AND_TEAMS.Id,
PERMISSION_SYSCONSOLE_WRITE_SITE_USERS_AND_TEAMS.Id,
PERMISSION_SYSCONSOLE_READ_SITE_NOTIFICATIONS.Id,
PERMISSION_SYSCONSOLE_WRITE_SITE_NOTIFICATIONS.Id,
PERMISSION_SYSCONSOLE_READ_SITE_ANNOUNCEMENT_BANNER.Id,
PERMISSION_SYSCONSOLE_WRITE_SITE_ANNOUNCEMENT_BANNER.Id,
PERMISSION_SYSCONSOLE_READ_SITE_EMOJI.Id,
PERMISSION_SYSCONSOLE_WRITE_SITE_EMOJI.Id,
PERMISSION_SYSCONSOLE_READ_SITE_POSTS.Id,
PERMISSION_SYSCONSOLE_WRITE_SITE_POSTS.Id,
PERMISSION_SYSCONSOLE_READ_SITE_FILE_SHARING_AND_DOWNLOADS.Id,
PERMISSION_SYSCONSOLE_WRITE_SITE_FILE_SHARING_AND_DOWNLOADS.Id,
PERMISSION_SYSCONSOLE_READ_SITE_PUBLIC_LINKS.Id,
PERMISSION_SYSCONSOLE_WRITE_SITE_PUBLIC_LINKS.Id,
PERMISSION_SYSCONSOLE_READ_SITE_NOTICES.Id,
PERMISSION_SYSCONSOLE_WRITE_SITE_NOTICES.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SIGNUP.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_EMAIL.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_PASSWORD.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_MFA.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_LDAP.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SAML.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_OPENID.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_GUEST_ACCESS.Id,
PERMISSION_SYSCONSOLE_READ_PLUGINS.Id,
PERMISSION_SYSCONSOLE_READ_INTEGRATIONS.Id,
PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS.Id,
PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_INTEGRATION_MANAGEMENT.Id,
PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_BOT_ACCOUNTS.Id,
PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_GIF.Id,
PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_CORS.Id,
PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_INTEGRATION_MANAGEMENT.Id,
PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_BOT_ACCOUNTS.Id,
PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_GIF.Id,
PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_CORS.Id,
}
// Add the ancillary permissions to each system role
SystemUserManagerDefaultPermissions = addAncillaryPermissions(SystemUserManagerDefaultPermissions)
SystemReadOnlyAdminDefaultPermissions = addAncillaryPermissions(SystemReadOnlyAdminDefaultPermissions)
SystemManagerDefaultPermissions = addAncillaryPermissions(SystemManagerDefaultPermissions)
SystemUserManagerDefaultPermissions = AddAncillaryPermissions(SystemUserManagerDefaultPermissions)
SystemReadOnlyAdminDefaultPermissions = AddAncillaryPermissions(SystemReadOnlyAdminDefaultPermissions)
SystemManagerDefaultPermissions = AddAncillaryPermissions(SystemManagerDefaultPermissions)
}
type RoleType string
@ -278,7 +453,7 @@ func (r *Role) MergeChannelHigherScopedPermissions(higherScopedPermissions *Role
_, presentOnHigherScope := higherScopedPermissionsMap[cp.Id]
// For the channel admin role always look to the higher scope to determine if the role has ther permission.
// For the channel admin role always look to the higher scope to determine if the role has their permission.
// The channel admin is a special case because they're not part of the UI to be "channel moderated", only
// channel members and channel guests are.
if higherScopedPermissions.RoleID == CHANNEL_ADMIN_ROLE_ID && presentOnHigherScope {
@ -475,7 +650,7 @@ func (r *Role) IsValidWithoutId() bool {
return false
}
if len(r.DisplayName) == 0 || len(r.DisplayName) > ROLE_DISPLAY_NAME_MAX_LENGTH {
if r.DisplayName == "" || len(r.DisplayName) > ROLE_DISPLAY_NAME_MAX_LENGTH {
return false
}
@ -519,7 +694,7 @@ func CleanRoleNames(roleNames []string) ([]string, bool) {
}
func IsValidRoleName(roleName string) bool {
if len(roleName) <= 0 || len(roleName) > ROLE_NAME_MAX_LENGTH {
if roleName == "" || len(roleName) > ROLE_NAME_MAX_LENGTH {
return false
}
@ -765,7 +940,7 @@ func MakeDefaultRoles() map[string]*Role {
return roles
}
func addAncillaryPermissions(permissions []string) []string {
func AddAncillaryPermissions(permissions []string) []string {
for _, permission := range permissions {
if ancillaryPermissions, ok := SysconsoleAncillaryPermissions[permission]; ok {
for _, ancillaryPermission := range ancillaryPermissions {

View File

@ -11,42 +11,65 @@ import (
type TaskFunc func()
type ScheduledTask struct {
Name string `json:"name"`
Interval time.Duration `json:"interval"`
Recurring bool `json:"recurring"`
function func()
cancel chan struct{}
cancelled chan struct{}
Name string `json:"name"`
Interval time.Duration `json:"interval"`
Recurring bool `json:"recurring"`
function func()
cancel chan struct{}
cancelled chan struct{}
fromNextIntervalTime bool
}
func CreateTask(name string, function TaskFunc, timeToExecution time.Duration) *ScheduledTask {
return createTask(name, function, timeToExecution, false)
return createTask(name, function, timeToExecution, false, false)
}
func CreateRecurringTask(name string, function TaskFunc, interval time.Duration) *ScheduledTask {
return createTask(name, function, interval, true)
return createTask(name, function, interval, true, false)
}
func createTask(name string, function TaskFunc, interval time.Duration, recurring bool) *ScheduledTask {
func CreateRecurringTaskFromNextIntervalTime(name string, function TaskFunc, interval time.Duration) *ScheduledTask {
return createTask(name, function, interval, true, true)
}
func createTask(name string, function TaskFunc, interval time.Duration, recurring bool, fromNextIntervalTime bool) *ScheduledTask {
task := &ScheduledTask{
Name: name,
Interval: interval,
Recurring: recurring,
function: function,
cancel: make(chan struct{}),
cancelled: make(chan struct{}),
Name: name,
Interval: interval,
Recurring: recurring,
function: function,
cancel: make(chan struct{}),
cancelled: make(chan struct{}),
fromNextIntervalTime: fromNextIntervalTime,
}
go func() {
defer close(task.cancelled)
ticker := time.NewTicker(interval)
var firstTick <-chan time.Time
var ticker *time.Ticker
if task.fromNextIntervalTime {
currTime := time.Now()
first := currTime.Truncate(interval)
if first.Before(currTime) {
first = first.Add(interval)
}
firstTick = time.After(time.Until(first))
ticker = &time.Ticker{C: nil}
} else {
firstTick = nil
ticker = time.NewTicker(interval)
}
defer func() {
ticker.Stop()
}()
for {
select {
case <-firstTick:
ticker = time.NewTicker(interval)
function()
case <-ticker.C:
function()
case <-task.cancel:

View File

@ -101,9 +101,8 @@ func SchemesFromJson(data io.Reader) []*Scheme {
var schemes []*Scheme
if err := json.NewDecoder(data).Decode(&schemes); err == nil {
return schemes
} else {
return nil
}
return nil
}
func (scheme *Scheme) IsValid() bool {
@ -115,7 +114,7 @@ func (scheme *Scheme) IsValid() bool {
}
func (scheme *Scheme) IsValidForCreate() bool {
if len(scheme.DisplayName) == 0 || len(scheme.DisplayName) > SCHEME_DISPLAY_NAME_MAX_LENGTH {
if scheme.DisplayName == "" || len(scheme.DisplayName) > SCHEME_DISPLAY_NAME_MAX_LENGTH {
return false
}
@ -160,15 +159,15 @@ func (scheme *Scheme) IsValidForCreate() bool {
}
if scheme.Scope == SCHEME_SCOPE_CHANNEL {
if len(scheme.DefaultTeamAdminRole) != 0 {
if scheme.DefaultTeamAdminRole != "" {
return false
}
if len(scheme.DefaultTeamUserRole) != 0 {
if scheme.DefaultTeamUserRole != "" {
return false
}
if len(scheme.DefaultTeamGuestRole) != 0 {
if scheme.DefaultTeamGuestRole != "" {
return false
}
}

View File

@ -25,6 +25,8 @@ type SearchParams struct {
ExcludedAfterDate string
BeforeDate string
ExcludedBeforeDate string
Extensions []string
ExcludedExtensions []string
OnDate string
ExcludedDate string
OrTerms bool
@ -106,7 +108,7 @@ func (p *SearchParams) GetExcludedDateMillis() (int64, int64) {
return GetStartOfDayMillis(date, p.TimeZoneOffset), GetEndOfDayMillis(date, p.TimeZoneOffset)
}
var searchFlags = [...]string{"from", "channel", "in", "before", "after", "on"}
var searchFlags = [...]string{"from", "channel", "in", "before", "after", "on", "ext"}
type flag struct {
name string
@ -214,7 +216,7 @@ func parseSearchFlags(input []string) ([]searchWord, []flag) {
// and remove extra pound #s
word = hashtagStart.ReplaceAllString(word, "#")
if len(word) != 0 {
if word != "" {
words = append(words, searchWord{
word,
exclude,
@ -265,6 +267,8 @@ func ParseSearchParams(text string, timeZoneOffset int) []*SearchParams {
excludedBeforeDate := ""
onDate := ""
excludedDate := ""
excludedExtensions := []string{}
extensions := []string{}
for _, flag := range flags {
if flag.name == "in" || flag.name == "channel" {
@ -297,12 +301,18 @@ func ParseSearchParams(text string, timeZoneOffset int) []*SearchParams {
} else {
onDate = flag.value
}
} else if flag.name == "ext" {
if flag.exclude {
excludedExtensions = append(excludedExtensions, flag.value)
} else {
extensions = append(extensions, flag.value)
}
}
}
paramsList := []*SearchParams{}
if len(plainTerms) > 0 || len(excludedPlainTerms) > 0 {
if plainTerms != "" || excludedPlainTerms != "" {
paramsList = append(paramsList, &SearchParams{
Terms: plainTerms,
ExcludedTerms: excludedPlainTerms,
@ -315,13 +325,15 @@ func ParseSearchParams(text string, timeZoneOffset int) []*SearchParams {
ExcludedAfterDate: excludedAfterDate,
BeforeDate: beforeDate,
ExcludedBeforeDate: excludedBeforeDate,
Extensions: extensions,
ExcludedExtensions: excludedExtensions,
OnDate: onDate,
ExcludedDate: excludedDate,
TimeZoneOffset: timeZoneOffset,
})
}
if len(hashtagTerms) > 0 || len(excludedHashtagTerms) > 0 {
if hashtagTerms != "" || excludedHashtagTerms != "" {
paramsList = append(paramsList, &SearchParams{
Terms: hashtagTerms,
ExcludedTerms: excludedHashtagTerms,
@ -334,6 +346,8 @@ func ParseSearchParams(text string, timeZoneOffset int) []*SearchParams {
ExcludedAfterDate: excludedAfterDate,
BeforeDate: beforeDate,
ExcludedBeforeDate: excludedBeforeDate,
Extensions: extensions,
ExcludedExtensions: excludedExtensions,
OnDate: onDate,
ExcludedDate: excludedDate,
TimeZoneOffset: timeZoneOffset,
@ -341,13 +355,14 @@ func ParseSearchParams(text string, timeZoneOffset int) []*SearchParams {
}
// special case for when no terms are specified but we still have a filter
if len(plainTerms) == 0 && len(hashtagTerms) == 0 &&
len(excludedPlainTerms) == 0 && len(excludedHashtagTerms) == 0 &&
if plainTerms == "" && hashtagTerms == "" &&
excludedPlainTerms == "" && excludedHashtagTerms == "" &&
(len(inChannels) != 0 || len(fromUsers) != 0 ||
len(excludedChannels) != 0 || len(excludedUsers) != 0 ||
len(afterDate) != 0 || len(excludedAfterDate) != 0 ||
len(beforeDate) != 0 || len(excludedBeforeDate) != 0 ||
len(onDate) != 0 || len(excludedDate) != 0) {
len(extensions) != 0 || len(excludedExtensions) != 0 ||
afterDate != "" || excludedAfterDate != "" ||
beforeDate != "" || excludedBeforeDate != "" ||
onDate != "" || excludedDate != "") {
paramsList = append(paramsList, &SearchParams{
Terms: "",
ExcludedTerms: "",
@ -360,6 +375,8 @@ func ParseSearchParams(text string, timeZoneOffset int) []*SearchParams {
ExcludedAfterDate: excludedAfterDate,
BeforeDate: beforeDate,
ExcludedBeforeDate: excludedBeforeDate,
Extensions: extensions,
ExcludedExtensions: excludedExtensions,
OnDate: onDate,
ExcludedDate: excludedDate,
TimeZoneOffset: timeZoneOffset,

View File

@ -15,8 +15,8 @@ type SecurityBulletin struct {
type SecurityBulletins []SecurityBulletin
func (me *SecurityBulletin) ToJson() string {
b, _ := json.Marshal(me)
func (sb *SecurityBulletin) ToJson() string {
b, _ := json.Marshal(sb)
return string(b)
}
@ -26,12 +26,12 @@ func SecurityBulletinFromJson(data io.Reader) *SecurityBulletin {
return o
}
func (me SecurityBulletins) ToJson() string {
if b, err := json.Marshal(me); err != nil {
func (sb SecurityBulletins) ToJson() string {
b, err := json.Marshal(sb)
if err != nil {
return "[]"
} else {
return string(b)
}
return string(b)
}
func SecurityBulletinsFromJson(data io.Reader) SecurityBulletins {

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@ import (
"strconv"
"strings"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/shared/mlog"
)
const (
@ -25,11 +25,16 @@ const (
SESSION_PROP_IS_BOT = "is_bot"
SESSION_PROP_IS_BOT_VALUE = "true"
SESSION_TYPE_USER_ACCESS_TOKEN = "UserAccessToken"
SESSION_TYPE_CLOUD_KEY = "CloudKey"
SESSION_TYPE_REMOTECLUSTER_TOKEN = "RemoteClusterToken"
SESSION_PROP_IS_GUEST = "is_guest"
SESSION_ACTIVITY_TIMEOUT = 1000 * 60 * 5 // 5 minutes
SESSION_USER_ACCESS_TOKEN_EXPIRY = 100 * 365 // 100 years
)
//msgp StringMap
type StringMap map[string]string
//msgp:tuple Session
// Session contains the user session details.
@ -53,20 +58,20 @@ type Session struct {
// Returns true if the session is unrestricted, which should grant it
// with all permissions. This is used for local mode sessions
func (me *Session) IsUnrestricted() bool {
return me.Local
func (s *Session) IsUnrestricted() bool {
return s.Local
}
func (me *Session) DeepCopy() *Session {
copySession := *me
func (s *Session) DeepCopy() *Session {
copySession := *s
if me.Props != nil {
copySession.Props = CopyStringMap(me.Props)
if s.Props != nil {
copySession.Props = CopyStringMap(s.Props)
}
if me.TeamMembers != nil {
copySession.TeamMembers = make([]*TeamMember, len(me.TeamMembers))
for index, tm := range me.TeamMembers {
if s.TeamMembers != nil {
copySession.TeamMembers = make([]*TeamMember, len(s.TeamMembers))
for index, tm := range s.TeamMembers {
copySession.TeamMembers[index] = new(TeamMember)
*copySession.TeamMembers[index] = *tm
}
@ -75,45 +80,45 @@ func (me *Session) DeepCopy() *Session {
return &copySession
}
func (me *Session) ToJson() string {
b, _ := json.Marshal(me)
func (s *Session) ToJson() string {
b, _ := json.Marshal(s)
return string(b)
}
func SessionFromJson(data io.Reader) *Session {
var me *Session
json.NewDecoder(data).Decode(&me)
return me
var s *Session
json.NewDecoder(data).Decode(&s)
return s
}
func (me *Session) PreSave() {
if me.Id == "" {
me.Id = NewId()
func (s *Session) PreSave() {
if s.Id == "" {
s.Id = NewId()
}
if me.Token == "" {
me.Token = NewId()
if s.Token == "" {
s.Token = NewId()
}
me.CreateAt = GetMillis()
me.LastActivityAt = me.CreateAt
s.CreateAt = GetMillis()
s.LastActivityAt = s.CreateAt
if me.Props == nil {
me.Props = make(map[string]string)
if s.Props == nil {
s.Props = make(map[string]string)
}
}
func (me *Session) Sanitize() {
me.Token = ""
func (s *Session) Sanitize() {
s.Token = ""
}
func (me *Session) IsExpired() bool {
func (s *Session) IsExpired() bool {
if me.ExpiresAt <= 0 {
if s.ExpiresAt <= 0 {
return false
}
if GetMillis() > me.ExpiresAt {
if GetMillis() > s.ExpiresAt {
return true
}
@ -123,25 +128,25 @@ func (me *Session) IsExpired() bool {
// Deprecated: SetExpireInDays is deprecated and should not be used.
// Use (*App).SetSessionExpireInDays instead which handles the
// cases where the new ExpiresAt is not relative to CreateAt.
func (me *Session) SetExpireInDays(days int) {
if me.CreateAt == 0 {
me.ExpiresAt = GetMillis() + (1000 * 60 * 60 * 24 * int64(days))
func (s *Session) SetExpireInDays(days int) {
if s.CreateAt == 0 {
s.ExpiresAt = GetMillis() + (1000 * 60 * 60 * 24 * int64(days))
} else {
me.ExpiresAt = me.CreateAt + (1000 * 60 * 60 * 24 * int64(days))
s.ExpiresAt = s.CreateAt + (1000 * 60 * 60 * 24 * int64(days))
}
}
func (me *Session) AddProp(key string, value string) {
func (s *Session) AddProp(key string, value string) {
if me.Props == nil {
me.Props = make(map[string]string)
if s.Props == nil {
s.Props = make(map[string]string)
}
me.Props[key] = value
s.Props[key] = value
}
func (me *Session) GetTeamByTeamId(teamId string) *TeamMember {
for _, team := range me.TeamMembers {
func (s *Session) GetTeamByTeamId(teamId string) *TeamMember {
for _, team := range s.TeamMembers {
if team.TeamId == teamId {
return team
}
@ -150,77 +155,77 @@ func (me *Session) GetTeamByTeamId(teamId string) *TeamMember {
return nil
}
func (me *Session) IsMobileApp() bool {
return len(me.DeviceId) > 0 || me.IsMobile()
func (s *Session) IsMobileApp() bool {
return s.DeviceId != "" || s.IsMobile()
}
func (me *Session) IsMobile() bool {
val, ok := me.Props[USER_AUTH_SERVICE_IS_MOBILE]
func (s *Session) IsMobile() bool {
val, ok := s.Props[USER_AUTH_SERVICE_IS_MOBILE]
if !ok {
return false
}
isMobile, err := strconv.ParseBool(val)
if err != nil {
mlog.Error("Error parsing boolean property from Session", mlog.Err(err))
mlog.Debug("Error parsing boolean property from Session", mlog.Err(err))
return false
}
return isMobile
}
func (me *Session) IsSaml() bool {
val, ok := me.Props[USER_AUTH_SERVICE_IS_SAML]
func (s *Session) IsSaml() bool {
val, ok := s.Props[USER_AUTH_SERVICE_IS_SAML]
if !ok {
return false
}
isSaml, err := strconv.ParseBool(val)
if err != nil {
mlog.Error("Error parsing boolean property from Session", mlog.Err(err))
mlog.Debug("Error parsing boolean property from Session", mlog.Err(err))
return false
}
return isSaml
}
func (me *Session) IsOAuthUser() bool {
val, ok := me.Props[USER_AUTH_SERVICE_IS_OAUTH]
func (s *Session) IsOAuthUser() bool {
val, ok := s.Props[USER_AUTH_SERVICE_IS_OAUTH]
if !ok {
return false
}
isOAuthUser, err := strconv.ParseBool(val)
if err != nil {
mlog.Error("Error parsing boolean property from Session", mlog.Err(err))
mlog.Debug("Error parsing boolean property from Session", mlog.Err(err))
return false
}
return isOAuthUser
}
func (me *Session) IsSSOLogin() bool {
return me.IsOAuthUser() || me.IsSaml()
func (s *Session) IsSSOLogin() bool {
return s.IsOAuthUser() || s.IsSaml()
}
func (me *Session) GetUserRoles() []string {
return strings.Fields(me.Roles)
func (s *Session) GetUserRoles() []string {
return strings.Fields(s.Roles)
}
func (me *Session) GenerateCSRF() string {
func (s *Session) GenerateCSRF() string {
token := NewId()
me.AddProp("csrf", token)
s.AddProp("csrf", token)
return token
}
func (me *Session) GetCSRF() string {
if me.Props == nil {
func (s *Session) GetCSRF() string {
if s.Props == nil {
return ""
}
return me.Props["csrf"]
return s.Props["csrf"]
}
func SessionsToJson(o []*Session) string {
if b, err := json.Marshal(o); err != nil {
b, err := json.Marshal(o)
if err != nil {
return "[]"
} else {
return string(b)
}
return string(b)
}
func SessionsFromJson(data io.Reader) []*Session {

View File

@ -0,0 +1,540 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
import (
"github.com/tinylib/msgp/msgp"
)
// DecodeMsg implements msgp.Decodable
func (z *Session) DecodeMsg(dc *msgp.Reader) (err error) {
var zb0001 uint32
zb0001, err = dc.ReadArrayHeader()
if err != nil {
err = msgp.WrapError(err)
return
}
if zb0001 != 13 {
err = msgp.ArrayError{Wanted: 13, Got: zb0001}
return
}
z.Id, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Id")
return
}
z.Token, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Token")
return
}
z.CreateAt, err = dc.ReadInt64()
if err != nil {
err = msgp.WrapError(err, "CreateAt")
return
}
z.ExpiresAt, err = dc.ReadInt64()
if err != nil {
err = msgp.WrapError(err, "ExpiresAt")
return
}
z.LastActivityAt, err = dc.ReadInt64()
if err != nil {
err = msgp.WrapError(err, "LastActivityAt")
return
}
z.UserId, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "UserId")
return
}
z.DeviceId, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "DeviceId")
return
}
z.Roles, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Roles")
return
}
z.IsOAuth, err = dc.ReadBool()
if err != nil {
err = msgp.WrapError(err, "IsOAuth")
return
}
z.ExpiredNotify, err = dc.ReadBool()
if err != nil {
err = msgp.WrapError(err, "ExpiredNotify")
return
}
var zb0002 uint32
zb0002, err = dc.ReadMapHeader()
if err != nil {
err = msgp.WrapError(err, "Props")
return
}
if z.Props == nil {
z.Props = make(StringMap, zb0002)
} else if len(z.Props) > 0 {
for key := range z.Props {
delete(z.Props, key)
}
}
for zb0002 > 0 {
zb0002--
var za0001 string
var za0002 string
za0001, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Props")
return
}
za0002, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Props", za0001)
return
}
z.Props[za0001] = za0002
}
var zb0003 uint32
zb0003, err = dc.ReadArrayHeader()
if err != nil {
err = msgp.WrapError(err, "TeamMembers")
return
}
if cap(z.TeamMembers) >= int(zb0003) {
z.TeamMembers = (z.TeamMembers)[:zb0003]
} else {
z.TeamMembers = make([]*TeamMember, zb0003)
}
for za0003 := range z.TeamMembers {
if dc.IsNil() {
err = dc.ReadNil()
if err != nil {
err = msgp.WrapError(err, "TeamMembers", za0003)
return
}
z.TeamMembers[za0003] = nil
} else {
if z.TeamMembers[za0003] == nil {
z.TeamMembers[za0003] = new(TeamMember)
}
err = z.TeamMembers[za0003].DecodeMsg(dc)
if err != nil {
err = msgp.WrapError(err, "TeamMembers", za0003)
return
}
}
}
z.Local, err = dc.ReadBool()
if err != nil {
err = msgp.WrapError(err, "Local")
return
}
return
}
// EncodeMsg implements msgp.Encodable
func (z *Session) EncodeMsg(en *msgp.Writer) (err error) {
// array header, size 13
err = en.Append(0x9d)
if err != nil {
return
}
err = en.WriteString(z.Id)
if err != nil {
err = msgp.WrapError(err, "Id")
return
}
err = en.WriteString(z.Token)
if err != nil {
err = msgp.WrapError(err, "Token")
return
}
err = en.WriteInt64(z.CreateAt)
if err != nil {
err = msgp.WrapError(err, "CreateAt")
return
}
err = en.WriteInt64(z.ExpiresAt)
if err != nil {
err = msgp.WrapError(err, "ExpiresAt")
return
}
err = en.WriteInt64(z.LastActivityAt)
if err != nil {
err = msgp.WrapError(err, "LastActivityAt")
return
}
err = en.WriteString(z.UserId)
if err != nil {
err = msgp.WrapError(err, "UserId")
return
}
err = en.WriteString(z.DeviceId)
if err != nil {
err = msgp.WrapError(err, "DeviceId")
return
}
err = en.WriteString(z.Roles)
if err != nil {
err = msgp.WrapError(err, "Roles")
return
}
err = en.WriteBool(z.IsOAuth)
if err != nil {
err = msgp.WrapError(err, "IsOAuth")
return
}
err = en.WriteBool(z.ExpiredNotify)
if err != nil {
err = msgp.WrapError(err, "ExpiredNotify")
return
}
err = en.WriteMapHeader(uint32(len(z.Props)))
if err != nil {
err = msgp.WrapError(err, "Props")
return
}
for za0001, za0002 := range z.Props {
err = en.WriteString(za0001)
if err != nil {
err = msgp.WrapError(err, "Props")
return
}
err = en.WriteString(za0002)
if err != nil {
err = msgp.WrapError(err, "Props", za0001)
return
}
}
err = en.WriteArrayHeader(uint32(len(z.TeamMembers)))
if err != nil {
err = msgp.WrapError(err, "TeamMembers")
return
}
for za0003 := range z.TeamMembers {
if z.TeamMembers[za0003] == nil {
err = en.WriteNil()
if err != nil {
return
}
} else {
err = z.TeamMembers[za0003].EncodeMsg(en)
if err != nil {
err = msgp.WrapError(err, "TeamMembers", za0003)
return
}
}
}
err = en.WriteBool(z.Local)
if err != nil {
err = msgp.WrapError(err, "Local")
return
}
return
}
// MarshalMsg implements msgp.Marshaler
func (z *Session) MarshalMsg(b []byte) (o []byte, err error) {
o = msgp.Require(b, z.Msgsize())
// array header, size 13
o = append(o, 0x9d)
o = msgp.AppendString(o, z.Id)
o = msgp.AppendString(o, z.Token)
o = msgp.AppendInt64(o, z.CreateAt)
o = msgp.AppendInt64(o, z.ExpiresAt)
o = msgp.AppendInt64(o, z.LastActivityAt)
o = msgp.AppendString(o, z.UserId)
o = msgp.AppendString(o, z.DeviceId)
o = msgp.AppendString(o, z.Roles)
o = msgp.AppendBool(o, z.IsOAuth)
o = msgp.AppendBool(o, z.ExpiredNotify)
o = msgp.AppendMapHeader(o, uint32(len(z.Props)))
for za0001, za0002 := range z.Props {
o = msgp.AppendString(o, za0001)
o = msgp.AppendString(o, za0002)
}
o = msgp.AppendArrayHeader(o, uint32(len(z.TeamMembers)))
for za0003 := range z.TeamMembers {
if z.TeamMembers[za0003] == nil {
o = msgp.AppendNil(o)
} else {
o, err = z.TeamMembers[za0003].MarshalMsg(o)
if err != nil {
err = msgp.WrapError(err, "TeamMembers", za0003)
return
}
}
}
o = msgp.AppendBool(o, z.Local)
return
}
// UnmarshalMsg implements msgp.Unmarshaler
func (z *Session) UnmarshalMsg(bts []byte) (o []byte, err error) {
var zb0001 uint32
zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts)
if err != nil {
err = msgp.WrapError(err)
return
}
if zb0001 != 13 {
err = msgp.ArrayError{Wanted: 13, Got: zb0001}
return
}
z.Id, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Id")
return
}
z.Token, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Token")
return
}
z.CreateAt, bts, err = msgp.ReadInt64Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "CreateAt")
return
}
z.ExpiresAt, bts, err = msgp.ReadInt64Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "ExpiresAt")
return
}
z.LastActivityAt, bts, err = msgp.ReadInt64Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "LastActivityAt")
return
}
z.UserId, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "UserId")
return
}
z.DeviceId, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "DeviceId")
return
}
z.Roles, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Roles")
return
}
z.IsOAuth, bts, err = msgp.ReadBoolBytes(bts)
if err != nil {
err = msgp.WrapError(err, "IsOAuth")
return
}
z.ExpiredNotify, bts, err = msgp.ReadBoolBytes(bts)
if err != nil {
err = msgp.WrapError(err, "ExpiredNotify")
return
}
var zb0002 uint32
zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Props")
return
}
if z.Props == nil {
z.Props = make(StringMap, zb0002)
} else if len(z.Props) > 0 {
for key := range z.Props {
delete(z.Props, key)
}
}
for zb0002 > 0 {
var za0001 string
var za0002 string
zb0002--
za0001, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Props")
return
}
za0002, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Props", za0001)
return
}
z.Props[za0001] = za0002
}
var zb0003 uint32
zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts)
if err != nil {
err = msgp.WrapError(err, "TeamMembers")
return
}
if cap(z.TeamMembers) >= int(zb0003) {
z.TeamMembers = (z.TeamMembers)[:zb0003]
} else {
z.TeamMembers = make([]*TeamMember, zb0003)
}
for za0003 := range z.TeamMembers {
if msgp.IsNil(bts) {
bts, err = msgp.ReadNilBytes(bts)
if err != nil {
return
}
z.TeamMembers[za0003] = nil
} else {
if z.TeamMembers[za0003] == nil {
z.TeamMembers[za0003] = new(TeamMember)
}
bts, err = z.TeamMembers[za0003].UnmarshalMsg(bts)
if err != nil {
err = msgp.WrapError(err, "TeamMembers", za0003)
return
}
}
}
z.Local, bts, err = msgp.ReadBoolBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Local")
return
}
o = bts
return
}
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
func (z *Session) Msgsize() (s int) {
s = 1 + msgp.StringPrefixSize + len(z.Id) + msgp.StringPrefixSize + len(z.Token) + msgp.Int64Size + msgp.Int64Size + msgp.Int64Size + msgp.StringPrefixSize + len(z.UserId) + msgp.StringPrefixSize + len(z.DeviceId) + msgp.StringPrefixSize + len(z.Roles) + msgp.BoolSize + msgp.BoolSize + msgp.MapHeaderSize
if z.Props != nil {
for za0001, za0002 := range z.Props {
_ = za0002
s += msgp.StringPrefixSize + len(za0001) + msgp.StringPrefixSize + len(za0002)
}
}
s += msgp.ArrayHeaderSize
for za0003 := range z.TeamMembers {
if z.TeamMembers[za0003] == nil {
s += msgp.NilSize
} else {
s += z.TeamMembers[za0003].Msgsize()
}
}
s += msgp.BoolSize
return
}
// DecodeMsg implements msgp.Decodable
func (z *StringMap) DecodeMsg(dc *msgp.Reader) (err error) {
var zb0003 uint32
zb0003, err = dc.ReadMapHeader()
if err != nil {
err = msgp.WrapError(err)
return
}
if (*z) == nil {
(*z) = make(StringMap, zb0003)
} else if len((*z)) > 0 {
for key := range *z {
delete((*z), key)
}
}
for zb0003 > 0 {
zb0003--
var zb0001 string
var zb0002 string
zb0001, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err)
return
}
zb0002, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, zb0001)
return
}
(*z)[zb0001] = zb0002
}
return
}
// EncodeMsg implements msgp.Encodable
func (z StringMap) EncodeMsg(en *msgp.Writer) (err error) {
err = en.WriteMapHeader(uint32(len(z)))
if err != nil {
err = msgp.WrapError(err)
return
}
for zb0004, zb0005 := range z {
err = en.WriteString(zb0004)
if err != nil {
err = msgp.WrapError(err)
return
}
err = en.WriteString(zb0005)
if err != nil {
err = msgp.WrapError(err, zb0004)
return
}
}
return
}
// MarshalMsg implements msgp.Marshaler
func (z StringMap) MarshalMsg(b []byte) (o []byte, err error) {
o = msgp.Require(b, z.Msgsize())
o = msgp.AppendMapHeader(o, uint32(len(z)))
for zb0004, zb0005 := range z {
o = msgp.AppendString(o, zb0004)
o = msgp.AppendString(o, zb0005)
}
return
}
// UnmarshalMsg implements msgp.Unmarshaler
func (z *StringMap) UnmarshalMsg(bts []byte) (o []byte, err error) {
var zb0003 uint32
zb0003, bts, err = msgp.ReadMapHeaderBytes(bts)
if err != nil {
err = msgp.WrapError(err)
return
}
if (*z) == nil {
(*z) = make(StringMap, zb0003)
} else if len((*z)) > 0 {
for key := range *z {
delete((*z), key)
}
}
for zb0003 > 0 {
var zb0001 string
var zb0002 string
zb0003--
zb0001, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err)
return
}
zb0002, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, zb0001)
return
}
(*z)[zb0001] = zb0002
}
o = bts
return
}
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
func (z StringMap) Msgsize() (s int) {
s = msgp.MapHeaderSize
if z != nil {
for zb0004, zb0005 := range z {
_ = zb0005
s += msgp.StringPrefixSize + len(zb0004) + msgp.StringPrefixSize + len(zb0005)
}
}
return
}

View File

@ -0,0 +1,273 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"encoding/json"
"io"
"net/http"
"unicode/utf8"
)
// SharedChannel represents a channel that can be synchronized with a remote cluster.
// If "home" is true, then the shared channel is homed locally and "SharedChannelRemote"
// table contains the remote clusters that have been invited.
// If "home" is false, then the shared channel is homed remotely, and "RemoteId"
// field points to the remote cluster connection in "RemoteClusters" table.
type SharedChannel struct {
ChannelId string `json:"id"`
TeamId string `json:"team_id"`
Home bool `json:"home"`
ReadOnly bool `json:"readonly"`
ShareName string `json:"name"`
ShareDisplayName string `json:"display_name"`
SharePurpose string `json:"purpose"`
ShareHeader string `json:"header"`
CreatorId string `json:"creator_id"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
RemoteId string `json:"remote_id,omitempty"` // if not "home"
Type string `db:"-"`
}
func (sc *SharedChannel) ToJson() string {
b, _ := json.Marshal(sc)
return string(b)
}
func SharedChannelFromJson(data io.Reader) (*SharedChannel, error) {
var sc *SharedChannel
err := json.NewDecoder(data).Decode(&sc)
return sc, err
}
func (sc *SharedChannel) IsValid() *AppError {
if !IsValidId(sc.ChannelId) {
return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.id.app_error", nil, "ChannelId="+sc.ChannelId, http.StatusBadRequest)
}
if sc.Type != CHANNEL_DIRECT && !IsValidId(sc.TeamId) {
return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.id.app_error", nil, "TeamId="+sc.TeamId, http.StatusBadRequest)
}
if sc.CreateAt == 0 {
return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.create_at.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest)
}
if sc.UpdateAt == 0 {
return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.update_at.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest)
}
if utf8.RuneCountInString(sc.ShareDisplayName) > CHANNEL_DISPLAY_NAME_MAX_RUNES {
return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest)
}
if !IsValidChannelIdentifier(sc.ShareName) {
return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.2_or_more.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest)
}
if utf8.RuneCountInString(sc.ShareHeader) > CHANNEL_HEADER_MAX_RUNES {
return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.header.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest)
}
if utf8.RuneCountInString(sc.SharePurpose) > CHANNEL_PURPOSE_MAX_RUNES {
return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.purpose.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest)
}
if !IsValidId(sc.CreatorId) {
return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.creator_id.app_error", nil, "CreatorId="+sc.CreatorId, http.StatusBadRequest)
}
if !sc.Home {
if !IsValidId(sc.RemoteId) {
return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.id.app_error", nil, "RemoteId="+sc.RemoteId, http.StatusBadRequest)
}
}
return nil
}
func (sc *SharedChannel) PreSave() {
sc.ShareName = SanitizeUnicode(sc.ShareName)
sc.ShareDisplayName = SanitizeUnicode(sc.ShareDisplayName)
sc.CreateAt = GetMillis()
sc.UpdateAt = sc.CreateAt
}
func (sc *SharedChannel) PreUpdate() {
sc.UpdateAt = GetMillis()
sc.ShareName = SanitizeUnicode(sc.ShareName)
sc.ShareDisplayName = SanitizeUnicode(sc.ShareDisplayName)
}
// SharedChannelRemote represents a remote cluster that has been invited
// to a shared channel.
type SharedChannelRemote struct {
Id string `json:"id"`
ChannelId string `json:"channel_id"`
CreatorId string `json:"creator_id"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
IsInviteAccepted bool `json:"is_invite_accepted"`
IsInviteConfirmed bool `json:"is_invite_confirmed"`
RemoteId string `json:"remote_id"`
LastPostUpdateAt int64 `json:"last_post_update_at"`
LastPostId string `json:"last_post_id"`
}
func (sc *SharedChannelRemote) ToJson() string {
b, _ := json.Marshal(sc)
return string(b)
}
func SharedChannelRemoteFromJson(data io.Reader) (*SharedChannelRemote, error) {
var sc *SharedChannelRemote
err := json.NewDecoder(data).Decode(&sc)
return sc, err
}
func (sc *SharedChannelRemote) IsValid() *AppError {
if !IsValidId(sc.Id) {
return NewAppError("SharedChannelRemote.IsValid", "model.channel.is_valid.id.app_error", nil, "Id="+sc.Id, http.StatusBadRequest)
}
if !IsValidId(sc.ChannelId) {
return NewAppError("SharedChannelRemote.IsValid", "model.channel.is_valid.id.app_error", nil, "ChannelId="+sc.ChannelId, http.StatusBadRequest)
}
if sc.CreateAt == 0 {
return NewAppError("SharedChannelRemote.IsValid", "model.channel.is_valid.create_at.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest)
}
if sc.UpdateAt == 0 {
return NewAppError("SharedChannelRemote.IsValid", "model.channel.is_valid.update_at.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest)
}
if !IsValidId(sc.CreatorId) {
return NewAppError("SharedChannelRemote.IsValid", "model.channel.is_valid.creator_id.app_error", nil, "id="+sc.CreatorId, http.StatusBadRequest)
}
return nil
}
func (sc *SharedChannelRemote) PreSave() {
if sc.Id == "" {
sc.Id = NewId()
}
sc.CreateAt = GetMillis()
sc.UpdateAt = sc.CreateAt
}
func (sc *SharedChannelRemote) PreUpdate() {
sc.UpdateAt = GetMillis()
}
type SharedChannelRemoteStatus struct {
ChannelId string `json:"channel_id"`
DisplayName string `json:"display_name"`
SiteURL string `json:"site_url"`
LastPingAt int64 `json:"last_ping_at"`
NextSyncAt int64 `json:"next_sync_at"`
ReadOnly bool `json:"readonly"`
IsInviteAccepted bool `json:"is_invite_accepted"`
Token string `json:"token"`
}
// SharedChannelUser stores a lastSyncAt timestamp on behalf of a remote cluster for
// each user that has been synchronized.
type SharedChannelUser struct {
Id string `json:"id"`
UserId string `json:"user_id"`
ChannelId string `json:"channel_id"`
RemoteId string `json:"remote_id"`
CreateAt int64 `json:"create_at"`
LastSyncAt int64 `json:"last_sync_at"`
}
func (scu *SharedChannelUser) PreSave() {
scu.Id = NewId()
scu.CreateAt = GetMillis()
}
func (scu *SharedChannelUser) IsValid() *AppError {
if !IsValidId(scu.Id) {
return NewAppError("SharedChannelUser.IsValid", "model.channel.is_valid.id.app_error", nil, "Id="+scu.Id, http.StatusBadRequest)
}
if !IsValidId(scu.UserId) {
return NewAppError("SharedChannelUser.IsValid", "model.channel.is_valid.id.app_error", nil, "UserId="+scu.UserId, http.StatusBadRequest)
}
if !IsValidId(scu.ChannelId) {
return NewAppError("SharedChannelUser.IsValid", "model.channel.is_valid.id.app_error", nil, "ChannelId="+scu.ChannelId, http.StatusBadRequest)
}
if !IsValidId(scu.RemoteId) {
return NewAppError("SharedChannelUser.IsValid", "model.channel.is_valid.id.app_error", nil, "RemoteId="+scu.RemoteId, http.StatusBadRequest)
}
if scu.CreateAt == 0 {
return NewAppError("SharedChannelUser.IsValid", "model.channel.is_valid.create_at.app_error", nil, "", http.StatusBadRequest)
}
return nil
}
type GetUsersForSyncFilter struct {
CheckProfileImage bool
ChannelID string
Limit uint64
}
// SharedChannelAttachment stores a lastSyncAt timestamp on behalf of a remote cluster for
// each file attachment that has been synchronized.
type SharedChannelAttachment struct {
Id string `json:"id"`
FileId string `json:"file_id"`
RemoteId string `json:"remote_id"`
CreateAt int64 `json:"create_at"`
LastSyncAt int64 `json:"last_sync_at"`
}
func (scf *SharedChannelAttachment) PreSave() {
if scf.Id == "" {
scf.Id = NewId()
}
if scf.CreateAt == 0 {
scf.CreateAt = GetMillis()
scf.LastSyncAt = scf.CreateAt
} else {
scf.LastSyncAt = GetMillis()
}
}
func (scf *SharedChannelAttachment) IsValid() *AppError {
if !IsValidId(scf.Id) {
return NewAppError("SharedChannelAttachment.IsValid", "model.channel.is_valid.id.app_error", nil, "Id="+scf.Id, http.StatusBadRequest)
}
if !IsValidId(scf.FileId) {
return NewAppError("SharedChannelAttachment.IsValid", "model.channel.is_valid.id.app_error", nil, "FileId="+scf.FileId, http.StatusBadRequest)
}
if !IsValidId(scf.RemoteId) {
return NewAppError("SharedChannelAttachment.IsValid", "model.channel.is_valid.id.app_error", nil, "RemoteId="+scf.RemoteId, http.StatusBadRequest)
}
if scf.CreateAt == 0 {
return NewAppError("SharedChannelAttachment.IsValid", "model.channel.is_valid.create_at.app_error", nil, "", http.StatusBadRequest)
}
return nil
}
type SharedChannelFilterOpts struct {
TeamId string
CreatorId string
ExcludeHome bool
ExcludeRemote bool
}
type SharedChannelRemoteFilterOpts struct {
ChannelId string
RemoteId string
InclUnconfirmed bool
}

View File

@ -179,6 +179,9 @@ func ParseSlackAttachment(post *Post, attachments []*SlackAttachment) {
attachment.Pretext = ParseSlackLinksToMarkdown(attachment.Pretext)
for _, field := range attachment.Fields {
if field == nil {
continue
}
if value, ok := field.Value.(string); ok {
field.Value = ParseSlackLinksToMarkdown(value)
}

View File

@ -25,6 +25,8 @@ type Status struct {
Manual bool `json:"manual"`
LastActivityAt int64 `json:"last_activity_at"`
ActiveChannel string `json:"active_channel,omitempty" db:"-"`
DNDEndTime int64 `json:"dnd_end_time"`
PrevStatus string `json:"-"`
}
func (o *Status) ToJson() string {

View File

@ -34,14 +34,16 @@ func (o *SwitchRequest) EmailToOAuth() bool {
(o.NewService == USER_AUTH_SERVICE_SAML ||
o.NewService == USER_AUTH_SERVICE_GITLAB ||
o.NewService == SERVICE_GOOGLE ||
o.NewService == SERVICE_OFFICE365)
o.NewService == SERVICE_OFFICE365 ||
o.NewService == SERVICE_OPENID)
}
func (o *SwitchRequest) OAuthToEmail() bool {
return (o.CurrentService == USER_AUTH_SERVICE_SAML ||
o.CurrentService == USER_AUTH_SERVICE_GITLAB ||
o.CurrentService == SERVICE_GOOGLE ||
o.CurrentService == SERVICE_OFFICE365) && o.NewService == USER_AUTH_SERVICE_EMAIL
o.CurrentService == SERVICE_OFFICE365 ||
o.CurrentService == SERVICE_OPENID) && o.NewService == USER_AUTH_SERVICE_EMAIL
}
func (o *SwitchRequest) EmailToLdap() bool {

View File

@ -14,6 +14,7 @@ const (
SYSTEM_RAN_UNIT_TESTS = "RanUnitTests"
SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime"
SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId"
SYSTEM_LICENSE_RENEWAL_TOKEN = "LicenseRenewalToken"
SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime"
SYSTEM_ASYMMETRIC_SIGNING_KEY = "AsymmetricSigningKey"
SYSTEM_POST_ACTION_COOKIE_SECRET = "PostActionCookieSecret"
@ -31,10 +32,13 @@ const (
SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_500 = "warn_metric_number_of_active_users_500"
SYSTEM_WARN_METRIC_NUMBER_OF_POSTS_2M = "warn_metric_number_of_posts_2M"
SYSTEM_WARN_METRIC_LAST_RUN_TIMESTAMP_KEY = "LastWarnMetricRunTimestamp"
SYSTEM_METRIC_SUPPORT_EMAIL_NOT_CONFIGURED = "warn_metric_support_email_not_configured"
SYSTEM_FIRST_ADMIN_VISIT_MARKETPLACE = "FirstAdminVisitMarketplace"
AWS_METERING_REPORT_INTERVAL = 1
AWS_METERING_DIMENSION_USAGE_HRS = "UsageHrs"
USER_LIMIT_OVERAGE_CYCLE_END_DATE = "UserLimitOverageCycleEndDate"
OVER_USER_LIMIT_FORGIVEN_COUNT = "OverUserLimitForgivenCount"
OVER_USER_LIMIT_LAST_EMAIL_SENT = "OverUserLimitLastEmailSent"
)
const (
@ -85,6 +89,22 @@ type ServerBusyState struct {
Expires_ts string `json:"expires_ts,omitempty"`
}
type SupportPacket struct {
ServerOS string `yaml:"server_os"`
ServerArchitecture string `yaml:"server_architecture"`
DatabaseType string `yaml:"database_type"`
DatabaseVersion string `yaml:"database_version"`
LdapVendorName string `yaml:"ldap_vendor_name,omitempty"`
LdapVendorVersion string `yaml:"ldap_vendor_version,omitempty"`
ElasticServerVersion string `yaml:"elastic_server_version,omitempty"`
ElasticServerPlugins []string `yaml:"elastic_server_plugins,omitempty"`
}
type FileData struct {
Filename string
Body []byte
}
func (sbs *ServerBusyState) ToJson() string {
b, _ := json.Marshal(sbs)
return string(b)
@ -151,13 +171,21 @@ var WarnMetricsTable = map[string]WarnMetric{
IsBotOnly: false,
IsRunOnce: true,
},
SYSTEM_METRIC_SUPPORT_EMAIL_NOT_CONFIGURED: {
Id: SYSTEM_METRIC_SUPPORT_EMAIL_NOT_CONFIGURED,
Limit: -1,
IsBotOnly: true,
IsRunOnce: false,
SkipAction: true,
},
}
type WarnMetric struct {
Id string
Limit int64
IsBotOnly bool
IsRunOnce bool
Id string
Limit int64
IsBotOnly bool
IsRunOnce bool
SkipAction bool
}
type WarnMetricDisplayTexts struct {
@ -182,9 +210,8 @@ func WarnMetricStatusFromJson(data io.Reader) *WarnMetricStatus {
var o WarnMetricStatus
if err := json.NewDecoder(data).Decode(&o); err != nil {
return nil
} else {
return &o
}
return &o
}
func MapWarnMetricStatusToJson(o map[string]*WarnMetricStatus) string {

View File

@ -42,6 +42,7 @@ type Team struct {
LastTeamIconUpdate int64 `json:"last_team_icon_update,omitempty"`
SchemeId *string `json:"scheme_id"`
GroupConstrained *bool `json:"group_constrained"`
PolicyID *string `json:"policy_id" db:"-"`
}
type TeamPatch struct {
@ -152,7 +153,7 @@ func (o *Team) IsValid() *AppError {
return NewAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
if len(o.Email) > 0 && !IsValidEmail(o.Email) {
if o.Email != "" && !IsValidEmail(o.Email) {
return NewAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
@ -168,7 +169,7 @@ func (o *Team) IsValid() *AppError {
return NewAppError("Team.IsValid", "model.team.is_valid.description.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
if len(o.InviteId) == 0 {
if o.InviteId == "" {
return NewAppError("Team.IsValid", "model.team.is_valid.invite_id.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
@ -208,7 +209,7 @@ func (o *Team) PreSave() {
o.Description = SanitizeUnicode(o.Description)
o.CompanyName = SanitizeUnicode(o.CompanyName)
if len(o.InviteId) == 0 {
if o.InviteId == "" {
o.InviteId = NewId()
}
}

View File

@ -15,6 +15,9 @@ const (
USERNAME = "Username"
)
//msgp:tuple TeamMember
// This struct's serializer methods are auto-generated. If a new field is added/removed,
// please run make gen-serialized.
type TeamMember struct {
TeamId string `json:"team_id"`
UserId string `json:"user_id"`
@ -26,28 +29,37 @@ type TeamMember struct {
ExplicitRoles string `json:"explicit_roles"`
}
//msgp:ignore TeamUnread
type TeamUnread struct {
TeamId string `json:"team_id"`
MsgCount int64 `json:"msg_count"`
MentionCount int64 `json:"mention_count"`
TeamId string `json:"team_id"`
MsgCount int64 `json:"msg_count"`
MentionCount int64 `json:"mention_count"`
MentionCountRoot int64 `json:"mention_count_root"`
MsgCountRoot int64 `json:"msg_count_root"`
ThreadCount int64 `json:"thread_count"`
ThreadMentionCount int64 `json:"thread_mention_count"`
}
//msgp:ignore TeamMemberForExport
type TeamMemberForExport struct {
TeamMember
TeamName string
}
//msgp:ignore TeamMemberWithError
type TeamMemberWithError struct {
UserId string `json:"user_id"`
Member *TeamMember `json:"member"`
Error *AppError `json:"error"`
}
//msgp:ignore EmailInviteWithError
type EmailInviteWithError struct {
Email string `json:"email"`
Error *AppError `json:"error"`
}
//msgp:ignore TeamMembersGetOptions
type TeamMembersGetOptions struct {
// Sort the team members. Accepts "Username", but defaults to "Id".
Sort string
@ -98,11 +110,11 @@ func EmailInviteWithErrorToEmails(o []*EmailInviteWithError) []string {
}
func EmailInviteWithErrorToJson(o []*EmailInviteWithError) string {
if b, err := json.Marshal(o); err != nil {
b, err := json.Marshal(o)
if err != nil {
return "[]"
} else {
return string(b)
}
return string(b)
}
func EmailInviteWithErrorToString(o *EmailInviteWithError) string {
@ -120,11 +132,11 @@ func TeamMembersWithErrorToTeamMembers(o []*TeamMemberWithError) []*TeamMember {
}
func TeamMembersWithErrorToJson(o []*TeamMemberWithError) string {
if b, err := json.Marshal(o); err != nil {
b, err := json.Marshal(o)
if err != nil {
return "[]"
} else {
return string(b)
}
return string(b)
}
func TeamMemberWithErrorToString(o *TeamMemberWithError) string {
@ -138,11 +150,11 @@ func TeamMembersWithErrorFromJson(data io.Reader) []*TeamMemberWithError {
}
func TeamMembersToJson(o []*TeamMember) string {
if b, err := json.Marshal(o); err != nil {
b, err := json.Marshal(o)
if err != nil {
return "[]"
} else {
return string(b)
}
return string(b)
}
func TeamMembersFromJson(data io.Reader) []*TeamMember {
@ -152,11 +164,11 @@ func TeamMembersFromJson(data io.Reader) []*TeamMember {
}
func TeamsUnreadToJson(o []*TeamUnread) string {
if b, err := json.Marshal(o); err != nil {
b, err := json.Marshal(o)
if err != nil {
return "[]"
} else {
return string(b)
}
return string(b)
}
func TeamsUnreadFromJson(data io.Reader) []*TeamUnread {

View File

@ -0,0 +1,193 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
import (
"github.com/tinylib/msgp/msgp"
)
// DecodeMsg implements msgp.Decodable
func (z *TeamMember) DecodeMsg(dc *msgp.Reader) (err error) {
var zb0001 uint32
zb0001, err = dc.ReadArrayHeader()
if err != nil {
err = msgp.WrapError(err)
return
}
if zb0001 != 8 {
err = msgp.ArrayError{Wanted: 8, Got: zb0001}
return
}
z.TeamId, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "TeamId")
return
}
z.UserId, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "UserId")
return
}
z.Roles, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Roles")
return
}
z.DeleteAt, err = dc.ReadInt64()
if err != nil {
err = msgp.WrapError(err, "DeleteAt")
return
}
z.SchemeGuest, err = dc.ReadBool()
if err != nil {
err = msgp.WrapError(err, "SchemeGuest")
return
}
z.SchemeUser, err = dc.ReadBool()
if err != nil {
err = msgp.WrapError(err, "SchemeUser")
return
}
z.SchemeAdmin, err = dc.ReadBool()
if err != nil {
err = msgp.WrapError(err, "SchemeAdmin")
return
}
z.ExplicitRoles, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "ExplicitRoles")
return
}
return
}
// EncodeMsg implements msgp.Encodable
func (z *TeamMember) EncodeMsg(en *msgp.Writer) (err error) {
// array header, size 8
err = en.Append(0x98)
if err != nil {
return
}
err = en.WriteString(z.TeamId)
if err != nil {
err = msgp.WrapError(err, "TeamId")
return
}
err = en.WriteString(z.UserId)
if err != nil {
err = msgp.WrapError(err, "UserId")
return
}
err = en.WriteString(z.Roles)
if err != nil {
err = msgp.WrapError(err, "Roles")
return
}
err = en.WriteInt64(z.DeleteAt)
if err != nil {
err = msgp.WrapError(err, "DeleteAt")
return
}
err = en.WriteBool(z.SchemeGuest)
if err != nil {
err = msgp.WrapError(err, "SchemeGuest")
return
}
err = en.WriteBool(z.SchemeUser)
if err != nil {
err = msgp.WrapError(err, "SchemeUser")
return
}
err = en.WriteBool(z.SchemeAdmin)
if err != nil {
err = msgp.WrapError(err, "SchemeAdmin")
return
}
err = en.WriteString(z.ExplicitRoles)
if err != nil {
err = msgp.WrapError(err, "ExplicitRoles")
return
}
return
}
// MarshalMsg implements msgp.Marshaler
func (z *TeamMember) MarshalMsg(b []byte) (o []byte, err error) {
o = msgp.Require(b, z.Msgsize())
// array header, size 8
o = append(o, 0x98)
o = msgp.AppendString(o, z.TeamId)
o = msgp.AppendString(o, z.UserId)
o = msgp.AppendString(o, z.Roles)
o = msgp.AppendInt64(o, z.DeleteAt)
o = msgp.AppendBool(o, z.SchemeGuest)
o = msgp.AppendBool(o, z.SchemeUser)
o = msgp.AppendBool(o, z.SchemeAdmin)
o = msgp.AppendString(o, z.ExplicitRoles)
return
}
// UnmarshalMsg implements msgp.Unmarshaler
func (z *TeamMember) UnmarshalMsg(bts []byte) (o []byte, err error) {
var zb0001 uint32
zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts)
if err != nil {
err = msgp.WrapError(err)
return
}
if zb0001 != 8 {
err = msgp.ArrayError{Wanted: 8, Got: zb0001}
return
}
z.TeamId, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "TeamId")
return
}
z.UserId, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "UserId")
return
}
z.Roles, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Roles")
return
}
z.DeleteAt, bts, err = msgp.ReadInt64Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "DeleteAt")
return
}
z.SchemeGuest, bts, err = msgp.ReadBoolBytes(bts)
if err != nil {
err = msgp.WrapError(err, "SchemeGuest")
return
}
z.SchemeUser, bts, err = msgp.ReadBoolBytes(bts)
if err != nil {
err = msgp.WrapError(err, "SchemeUser")
return
}
z.SchemeAdmin, bts, err = msgp.ReadBoolBytes(bts)
if err != nil {
err = msgp.WrapError(err, "SchemeAdmin")
return
}
z.ExplicitRoles, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "ExplicitRoles")
return
}
o = bts
return
}
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
func (z *TeamMember) Msgsize() (s int) {
s = 1 + msgp.StringPrefixSize + len(z.TeamId) + msgp.StringPrefixSize + len(z.UserId) + msgp.StringPrefixSize + len(z.Roles) + msgp.Int64Size + msgp.BoolSize + msgp.BoolSize + msgp.BoolSize + msgp.StringPrefixSize + len(z.ExplicitRoles)
return
}

View File

@ -9,12 +9,17 @@ import (
)
type TeamSearch struct {
Term string `json:"term"`
Page *int `json:"page,omitempty"`
PerPage *int `json:"per_page,omitempty"`
AllowOpenInvite *bool `json:"allow_open_invite,omitempty"`
GroupConstrained *bool `json:"group_constrained,omitempty"`
IncludeGroupConstrained *bool `json:"include_group_constrained,omitempty"`
Term string `json:"term"`
Page *int `json:"page,omitempty"`
PerPage *int `json:"per_page,omitempty"`
AllowOpenInvite *bool `json:"allow_open_invite,omitempty"`
GroupConstrained *bool `json:"group_constrained,omitempty"`
IncludeGroupConstrained *bool `json:"include_group_constrained,omitempty"`
PolicyID *string `json:"policy_id,omitempty"`
ExcludePolicyConstrained *bool `json:"exclude_policy_constrained,omitempty"`
IncludePolicyID *bool `json:"-"`
IncludeDeleted *bool `json:"-"`
TeamType *string `json:"-"`
}
func (t *TeamSearch) IsPaginated() bool {

View File

@ -16,23 +16,24 @@ type Thread struct {
}
type ThreadResponse struct {
PostId string `json:"id"`
ReplyCount int64 `json:"reply_count"`
LastReplyAt int64 `json:"last_reply_at"`
LastViewedAt int64 `json:"last_viewed_at"`
Participants []*User `json:"participants"`
Post *Post `json:"post"`
PostId string `json:"id"`
ReplyCount int64 `json:"reply_count"`
LastReplyAt int64 `json:"last_reply_at"`
LastViewedAt int64 `json:"last_viewed_at"`
Participants []*User `json:"participants"`
Post *Post `json:"post"`
UnreadReplies int64 `json:"unread_replies"`
UnreadMentions int64 `json:"unread_mentions"`
}
type Threads struct {
Total int64 `json:"total"`
Threads []*ThreadResponse `json:"threads"`
Total int64 `json:"total"`
TotalUnreadThreads int64 `json:"total_unread_threads"`
TotalUnreadMentions int64 `json:"total_unread_mentions"`
Threads []*ThreadResponse `json:"threads"`
}
type GetUserThreadsOpts struct {
// Page specifies which part of the results to return, by PageSize. Default = 0
Page uint64
// PageSize specifies the size of the returned chunk of results. Default = 30
PageSize uint64
@ -44,6 +45,32 @@ type GetUserThreadsOpts struct {
// Since filters the threads based on their LastUpdateAt timestamp.
Since uint64
// Before specifies thread id as a cursor for pagination and will return `PageSize` threads before the cursor
Before string
// After specifies thread id as a cursor for pagination and will return `PageSize` threads after the cursor
After string
// Unread will make sure that only threads with unread replies are returned
Unread bool
// TotalsOnly will not fetch any threads and just fetch the total counts
TotalsOnly bool
// TeamOnly will only fetch threads and unreads for the specified team and excludes DMs/GMs
TeamOnly bool
}
func (o *ThreadResponse) ToJson() string {
b, _ := json.Marshal(o)
return string(b)
}
func ThreadResponseFromJson(s string) (*ThreadResponse, error) {
var t ThreadResponse
err := json.Unmarshal([]byte(s), &t)
return &t, err
}
func (o *Threads) ToJson() string {
@ -56,6 +83,12 @@ func (o *Thread) ToJson() string {
return string(b)
}
func ThreadFromJson(s string) (*Thread, error) {
var t Thread
err := json.Unmarshal([]byte(s), &t)
return &t, err
}
func (o *Thread) Etag() string {
return Etag(o.PostId, o.LastReplyAt)
}

View File

@ -3,7 +3,9 @@
package model
import "net/http"
import (
"net/http"
)
const (
TOKEN_SIZE = 64

View File

@ -18,6 +18,9 @@ const (
UploadTypeImport UploadType = "import"
)
// UploadNoUserID is a "fake" user id used by the API layer when in local mode.
const UploadNoUserID = "nouser"
// UploadSession contains information used to keep track of a file upload.
type UploadSession struct {
// The unique identifier for the session.
@ -29,7 +32,7 @@ type UploadSession struct {
// The id of the user performing the upload.
UserId string `json:"user_id"`
// The id of the channel to upload to.
ChannelId string `json:"channel_id"`
ChannelId string `json:"channel_id,omitempty"`
// The name of the file to upload.
Filename string `json:"filename"`
// The path where the file is stored.
@ -39,6 +42,10 @@ type UploadSession struct {
// The amount of received data in bytes. If equal to FileSize it means the
// upload has finished.
FileOffset int64 `json:"file_offset"`
// Id of remote cluster if uploading for shared channel
RemoteId string `json:"remote_id"`
// Requested file id if uploading for shared channel
ReqFileId string `json:"req_file_id"`
}
// ToJson serializes the UploadSession into JSON and returns it as string.
@ -109,7 +116,7 @@ func (us *UploadSession) IsValid() *AppError {
return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.type.app_error", nil, err.Error(), http.StatusBadRequest)
}
if !IsValidId(us.UserId) {
if !IsValidId(us.UserId) && us.UserId != UploadNoUserID {
return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.user_id.app_error", nil, "id="+us.Id, http.StatusBadRequest)
}

View File

@ -9,17 +9,17 @@ import (
"fmt"
"io"
"io/ioutil"
"math/rand"
"net/http"
"regexp"
"sort"
"strings"
"time"
"unicode/utf8"
"github.com/mattermost/mattermost-server/v5/services/timezones"
"golang.org/x/crypto/bcrypt"
"golang.org/x/text/language"
"github.com/mattermost/mattermost-server/v5/services/timezones"
"github.com/mattermost/mattermost-server/v5/shared/mlog"
)
const (
@ -57,6 +57,7 @@ const (
USER_NAME_MIN_LENGTH = 1
USER_PASSWORD_MAX_LENGTH = 72
USER_LOCALE_MAX_LENGTH = 5
USER_TIMEZONE_MAX_RUNES = 256
)
//msgp:tuple User
@ -90,12 +91,14 @@ type User struct {
Timezone StringMap `json:"timezone"`
MfaActive bool `json:"mfa_active,omitempty"`
MfaSecret string `json:"mfa_secret,omitempty"`
RemoteId *string `json:"remote_id,omitempty"`
LastActivityAt int64 `db:"-" json:"last_activity_at,omitempty"`
IsBot bool `db:"-" json:"is_bot,omitempty"`
BotDescription string `db:"-" json:"bot_description,omitempty"`
BotLastIconUpdate int64 `db:"-" json:"bot_last_icon_update,omitempty"`
TermsOfServiceId string `db:"-" json:"terms_of_service_id,omitempty"`
TermsOfServiceCreateAt int64 `db:"-" json:"terms_of_service_create_at,omitempty"`
DisableWelcomeEmail bool `db:"-" json:"disable_welcome_email"`
}
//msgp UserMap
@ -104,11 +107,13 @@ type User struct {
// It is used to generate methods which can be used for fast serialization/de-serialization.
type UserMap map[string]*User
//msgp:ignore UserUpdate
type UserUpdate struct {
Old *User
New *User
}
//msgp:ignore UserPatch
type UserPatch struct {
Username *string `json:"username"`
Password *string `json:"password,omitempty"`
@ -121,14 +126,17 @@ type UserPatch struct {
NotifyProps StringMap `json:"notify_props,omitempty"`
Locale *string `json:"locale"`
Timezone StringMap `json:"timezone"`
RemoteId *string `json:"remote_id"`
}
//msgp:ignore UserAuth
type UserAuth struct {
Password string `json:"password,omitempty"`
Password string `json:"password,omitempty"` // DEPRECATED: It is not used.
AuthData *string `json:"auth_data,omitempty"`
AuthService string `json:"auth_service,omitempty"`
}
//msgp:ignore UserForIndexing
type UserForIndexing struct {
Id string `json:"id"`
Username string `json:"username"`
@ -142,6 +150,7 @@ type UserForIndexing struct {
ChannelsIds []string `json:"channel_id"`
}
//msgp:ignore ViewUsersRestrictions
type ViewUsersRestrictions struct {
Teams []string
Channels []string
@ -158,6 +167,7 @@ func (r *ViewUsersRestrictions) Hash() string {
return fmt.Sprintf("%x", hash.Sum(nil))
}
//msgp:ignore UserSlice
type UserSlice []*User
func (u UserSlice) Usernames() []string {
@ -262,11 +272,17 @@ func (u *User) IsValid() *AppError {
return InvalidUserError("update_at", u.Id)
}
if !IsValidUsername(u.Username) {
return InvalidUserError("username", u.Id)
if u.IsRemote() {
if !IsValidUsernameAllowRemote(u.Username) {
return InvalidUserError("username", u.Id)
}
} else {
if !IsValidUsername(u.Username) {
return InvalidUserError("username", u.Id)
}
}
if len(u.Email) > USER_EMAIL_MAX_LENGTH || len(u.Email) == 0 || !IsValidEmail(u.Email) {
if len(u.Email) > USER_EMAIL_MAX_LENGTH || u.Email == "" || !IsValidEmail(u.Email) {
return InvalidUserError("email", u.Id)
}
@ -290,11 +306,11 @@ func (u *User) IsValid() *AppError {
return InvalidUserError("auth_data", u.Id)
}
if u.AuthData != nil && len(*u.AuthData) > 0 && len(u.AuthService) == 0 {
if u.AuthData != nil && *u.AuthData != "" && u.AuthService == "" {
return InvalidUserError("auth_data_type", u.Id)
}
if len(u.Password) > 0 && u.AuthData != nil && len(*u.AuthData) > 0 {
if u.Password != "" && u.AuthData != nil && *u.AuthData != "" {
return InvalidUserError("auth_data_pwd", u.Id)
}
@ -306,6 +322,14 @@ func (u *User) IsValid() *AppError {
return InvalidUserError("locale", u.Id)
}
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, err.Error(), http.StatusInternalServerError)
} else if utf8.RuneCount(tzJSON) > USER_TIMEZONE_MAX_RUNES {
return InvalidUserError("timezone_limit", u.Id)
}
}
return nil
}
@ -373,7 +397,7 @@ func (u *User) PreSave() {
u.Timezone = timezones.DefaultUserTimezone()
}
if len(u.Password) > 0 {
if u.Password != "" {
u.Password = HashPassword(u.Password)
}
}
@ -406,7 +430,7 @@ func (u *User) PreUpdate() {
splitKeys := strings.Split(u.NotifyProps[MENTION_KEYS_NOTIFY_PROP], ",")
goodKeys := []string{}
for _, key := range splitKeys {
if len(key) > 0 {
if key != "" {
goodKeys = append(goodKeys, strings.ToLower(key))
}
}
@ -497,6 +521,10 @@ func (u *User) Patch(patch *UserPatch) {
if patch.Timezone != nil {
u.Timezone = patch.Timezone
}
if patch.RemoteId != nil {
u.RemoteId = patch.RemoteId
}
}
// ToJson convert a User to a json string
@ -553,6 +581,7 @@ func (u *User) SanitizeInput(isAdmin bool) {
u.FailedAttempts = 0
u.MfaActive = false
u.MfaSecret = ""
u.Email = strings.TrimSpace(u.Email)
}
func (u *User) ClearNonProfileFields() {
@ -588,12 +617,22 @@ func (u *User) AddNotifyProp(key string, value string) {
u.NotifyProps[key] = value
}
func (u *User) SetCustomStatus(cs *CustomStatus) {
u.MakeNonNil()
u.Props[UserPropsKeyCustomStatus] = cs.ToJson()
}
func (u *User) ClearCustomStatus() {
u.MakeNonNil()
u.Props[UserPropsKeyCustomStatus] = ""
}
func (u *User) GetFullName() string {
if len(u.FirstName) > 0 && len(u.LastName) > 0 {
if u.FirstName != "" && u.LastName != "" {
return u.FirstName + " " + u.LastName
} else if len(u.FirstName) > 0 {
} else if u.FirstName != "" {
return u.FirstName
} else if len(u.LastName) > 0 {
} else if u.LastName != "" {
return u.LastName
} else {
return ""
@ -604,13 +643,13 @@ func (u *User) getDisplayName(baseName, nameFormat string) string {
displayName := baseName
if nameFormat == SHOW_NICKNAME_FULLNAME {
if len(u.Nickname) > 0 {
if u.Nickname != "" {
displayName = u.Nickname
} else if fullName := u.GetFullName(); len(fullName) > 0 {
} else if fullName := u.GetFullName(); fullName != "" {
displayName = fullName
}
} else if nameFormat == SHOW_FULLNAME {
if fullName := u.GetFullName(); len(fullName) > 0 {
if fullName := u.GetFullName(); fullName != "" {
displayName = fullName
}
}
@ -691,7 +730,10 @@ func (u *User) IsSSOUser() bool {
}
func (u *User) IsOAuthUser() bool {
return u.AuthService == USER_AUTH_SERVICE_GITLAB
return u.AuthService == SERVICE_GITLAB ||
u.AuthService == SERVICE_GOOGLE ||
u.AuthService == SERVICE_OFFICE365 ||
u.AuthService == SERVICE_OPENID
}
func (u *User) IsLDAPUser() bool {
@ -706,6 +748,61 @@ func (u *User) GetPreferredTimezone() string {
return GetPreferredTimezone(u.Timezone)
}
// IsRemote returns true if the user belongs to a remote cluster (has RemoteId).
func (u *User) IsRemote() bool {
return u.RemoteId != nil && *u.RemoteId != ""
}
// GetRemoteID returns the remote id for this user or "" if not a remote user.
func (u *User) GetRemoteID() string {
if u.RemoteId != nil {
return *u.RemoteId
}
return ""
}
// GetProp fetches a prop value by name.
func (u *User) GetProp(name string) (string, bool) {
val, ok := u.Props[name]
return val, ok
}
// SetProp sets a prop value by name, creating the map if nil.
// Not thread safe.
func (u *User) SetProp(name string, value string) {
if u.Props == nil {
u.Props = make(map[string]string)
}
u.Props[name] = value
}
func (u *User) ToPatch() *UserPatch {
return &UserPatch{
Username: &u.Username, Password: &u.Password,
Nickname: &u.Nickname, FirstName: &u.FirstName, LastName: &u.LastName,
Position: &u.Position, Email: &u.Email,
Props: u.Props, NotifyProps: u.NotifyProps,
Locale: &u.Locale, Timezone: u.Timezone,
}
}
func (u *UserPatch) SetField(fieldName string, fieldValue string) {
switch fieldName {
case "FirstName":
u.FirstName = &fieldValue
case "LastName":
u.LastName = &fieldValue
case "Nickname":
u.Nickname = &fieldValue
case "Email":
u.Email = &fieldValue
case "Position":
u.Position = &fieldValue
case "Username":
u.Username = &fieldValue
}
}
// UserFromJson will decode the input and return a User
func UserFromJson(data io.Reader) *User {
var user *User
@ -758,9 +855,10 @@ func HashPassword(password string) string {
}
// ComparePassword compares the hash
// This function is deprecated and will be removed in a future release.
func ComparePassword(hash string, password string) bool {
if len(password) == 0 || len(hash) == 0 {
if password == "" || hash == "" {
return false
}
@ -769,12 +867,13 @@ func ComparePassword(hash string, password string) bool {
}
var validUsernameChars = regexp.MustCompile(`^[a-z0-9\.\-_]+$`)
var validUsernameCharsForRemote = regexp.MustCompile(`^[a-z0-9\.\-_:]+$`)
var restrictedUsernames = []string{
"all",
"channel",
"matterbot",
"system",
var restrictedUsernames = map[string]struct{}{
"all": {},
"channel": {},
"matterbot": {},
"system": {},
}
func IsValidUsername(s string) bool {
@ -786,17 +885,25 @@ func IsValidUsername(s string) bool {
return false
}
for _, restrictedUsername := range restrictedUsernames {
if s == restrictedUsername {
return false
}
}
return true
_, found := restrictedUsernames[s]
return !found
}
func CleanUsername(s string) string {
s = NormalizeUsername(strings.Replace(s, " ", "-", -1))
func IsValidUsernameAllowRemote(s string) bool {
if len(s) < USER_NAME_MIN_LENGTH || len(s) > USER_NAME_MAX_LENGTH {
return false
}
if !validUsernameCharsForRemote.MatchString(s) {
return false
}
_, found := restrictedUsernames[s]
return !found
}
func CleanUsername(username string) string {
s := NormalizeUsername(strings.Replace(username, " ", "-", -1))
for _, value := range reservedName {
if s == value {
@ -817,6 +924,8 @@ func CleanUsername(s string) string {
if !IsValidUsername(s) {
s = "a" + NewId()
mlog.Warn("Generating new username since provided username was invalid",
mlog.String("provided_username", username), mlog.String("new_username", s))
}
return s
@ -858,6 +967,7 @@ func IsValidLocale(locale string) bool {
return true
}
//msgp:ignore UserWithGroups
type UserWithGroups struct {
User
GroupIDs *string `json:"-"`
@ -872,12 +982,13 @@ func (u *UserWithGroups) GetGroupIDs() []string {
return nil
}
trimmed := strings.TrimSpace(*u.GroupIDs)
if len(trimmed) == 0 {
if trimmed == "" {
return nil
}
return strings.Split(trimmed, ",")
}
//msgp:ignore UsersWithGroupsAndCount
type UsersWithGroupsAndCount struct {
Users []*UserWithGroups `json:"users"`
Count int64 `json:"total_count"`
@ -889,27 +1000,3 @@ func UsersWithGroupsAndCountFromJson(data io.Reader) *UsersWithGroupsAndCount {
json.Unmarshal(bodyBytes, uwg)
return uwg
}
var passwordRandomSource = rand.NewSource(time.Now().Unix())
var passwordSpecialChars = "!$%^&*(),."
var passwordNumbers = "0123456789"
var passwordUpperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
var passwordLowerCaseLetters = "abcdefghijklmnopqrstuvwxyz"
var passwordAllChars = passwordSpecialChars + passwordNumbers + passwordUpperCaseLetters + passwordLowerCaseLetters
func GeneratePassword(minimumLength int) string {
r := rand.New(passwordRandomSource)
// Make sure we are guaranteed at least one of each type to meet any possible password complexity requirements.
password := string([]rune(passwordUpperCaseLetters)[r.Intn(len(passwordUpperCaseLetters))]) +
string([]rune(passwordNumbers)[r.Intn(len(passwordNumbers))]) +
string([]rune(passwordLowerCaseLetters)[r.Intn(len(passwordLowerCaseLetters))]) +
string([]rune(passwordSpecialChars)[r.Intn(len(passwordSpecialChars))])
for len(password) < minimumLength {
i := r.Intn(len(passwordAllChars))
password = password + string([]rune(passwordAllChars)[i])
}
return password
}

View File

@ -31,11 +31,10 @@ func UserAutocompleteFromJson(data io.Reader) *UserAutocomplete {
decoder := json.NewDecoder(data)
autocomplete := new(UserAutocomplete)
err := decoder.Decode(&autocomplete)
if err == nil {
return autocomplete
} else {
if err != nil {
return nil
}
return autocomplete
}
func (o *UserAutocompleteInChannel) ToJson() string {

View File

@ -0,0 +1,826 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
import (
"github.com/tinylib/msgp/msgp"
)
// DecodeMsg implements msgp.Decodable
func (z *User) DecodeMsg(dc *msgp.Reader) (err error) {
var zb0001 uint32
zb0001, err = dc.ReadArrayHeader()
if err != nil {
err = msgp.WrapError(err)
return
}
if zb0001 != 32 {
err = msgp.ArrayError{Wanted: 32, Got: zb0001}
return
}
z.Id, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Id")
return
}
z.CreateAt, err = dc.ReadInt64()
if err != nil {
err = msgp.WrapError(err, "CreateAt")
return
}
z.UpdateAt, err = dc.ReadInt64()
if err != nil {
err = msgp.WrapError(err, "UpdateAt")
return
}
z.DeleteAt, err = dc.ReadInt64()
if err != nil {
err = msgp.WrapError(err, "DeleteAt")
return
}
z.Username, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Username")
return
}
z.Password, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Password")
return
}
if dc.IsNil() {
err = dc.ReadNil()
if err != nil {
err = msgp.WrapError(err, "AuthData")
return
}
z.AuthData = nil
} else {
if z.AuthData == nil {
z.AuthData = new(string)
}
*z.AuthData, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "AuthData")
return
}
}
z.AuthService, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "AuthService")
return
}
z.Email, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Email")
return
}
z.EmailVerified, err = dc.ReadBool()
if err != nil {
err = msgp.WrapError(err, "EmailVerified")
return
}
z.Nickname, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Nickname")
return
}
z.FirstName, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "FirstName")
return
}
z.LastName, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "LastName")
return
}
z.Position, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Position")
return
}
z.Roles, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Roles")
return
}
z.AllowMarketing, err = dc.ReadBool()
if err != nil {
err = msgp.WrapError(err, "AllowMarketing")
return
}
err = z.Props.DecodeMsg(dc)
if err != nil {
err = msgp.WrapError(err, "Props")
return
}
err = z.NotifyProps.DecodeMsg(dc)
if err != nil {
err = msgp.WrapError(err, "NotifyProps")
return
}
z.LastPasswordUpdate, err = dc.ReadInt64()
if err != nil {
err = msgp.WrapError(err, "LastPasswordUpdate")
return
}
z.LastPictureUpdate, err = dc.ReadInt64()
if err != nil {
err = msgp.WrapError(err, "LastPictureUpdate")
return
}
z.FailedAttempts, err = dc.ReadInt()
if err != nil {
err = msgp.WrapError(err, "FailedAttempts")
return
}
z.Locale, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "Locale")
return
}
err = z.Timezone.DecodeMsg(dc)
if err != nil {
err = msgp.WrapError(err, "Timezone")
return
}
z.MfaActive, err = dc.ReadBool()
if err != nil {
err = msgp.WrapError(err, "MfaActive")
return
}
z.MfaSecret, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "MfaSecret")
return
}
if dc.IsNil() {
err = dc.ReadNil()
if err != nil {
err = msgp.WrapError(err, "RemoteId")
return
}
z.RemoteId = nil
} else {
if z.RemoteId == nil {
z.RemoteId = new(string)
}
*z.RemoteId, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "RemoteId")
return
}
}
z.LastActivityAt, err = dc.ReadInt64()
if err != nil {
err = msgp.WrapError(err, "LastActivityAt")
return
}
z.IsBot, err = dc.ReadBool()
if err != nil {
err = msgp.WrapError(err, "IsBot")
return
}
z.BotDescription, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "BotDescription")
return
}
z.BotLastIconUpdate, err = dc.ReadInt64()
if err != nil {
err = msgp.WrapError(err, "BotLastIconUpdate")
return
}
z.TermsOfServiceId, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err, "TermsOfServiceId")
return
}
z.TermsOfServiceCreateAt, err = dc.ReadInt64()
if err != nil {
err = msgp.WrapError(err, "TermsOfServiceCreateAt")
return
}
return
}
// EncodeMsg implements msgp.Encodable
func (z *User) EncodeMsg(en *msgp.Writer) (err error) {
// array header, size 32
err = en.Append(0xdc, 0x0, 0x20)
if err != nil {
return
}
err = en.WriteString(z.Id)
if err != nil {
err = msgp.WrapError(err, "Id")
return
}
err = en.WriteInt64(z.CreateAt)
if err != nil {
err = msgp.WrapError(err, "CreateAt")
return
}
err = en.WriteInt64(z.UpdateAt)
if err != nil {
err = msgp.WrapError(err, "UpdateAt")
return
}
err = en.WriteInt64(z.DeleteAt)
if err != nil {
err = msgp.WrapError(err, "DeleteAt")
return
}
err = en.WriteString(z.Username)
if err != nil {
err = msgp.WrapError(err, "Username")
return
}
err = en.WriteString(z.Password)
if err != nil {
err = msgp.WrapError(err, "Password")
return
}
if z.AuthData == nil {
err = en.WriteNil()
if err != nil {
return
}
} else {
err = en.WriteString(*z.AuthData)
if err != nil {
err = msgp.WrapError(err, "AuthData")
return
}
}
err = en.WriteString(z.AuthService)
if err != nil {
err = msgp.WrapError(err, "AuthService")
return
}
err = en.WriteString(z.Email)
if err != nil {
err = msgp.WrapError(err, "Email")
return
}
err = en.WriteBool(z.EmailVerified)
if err != nil {
err = msgp.WrapError(err, "EmailVerified")
return
}
err = en.WriteString(z.Nickname)
if err != nil {
err = msgp.WrapError(err, "Nickname")
return
}
err = en.WriteString(z.FirstName)
if err != nil {
err = msgp.WrapError(err, "FirstName")
return
}
err = en.WriteString(z.LastName)
if err != nil {
err = msgp.WrapError(err, "LastName")
return
}
err = en.WriteString(z.Position)
if err != nil {
err = msgp.WrapError(err, "Position")
return
}
err = en.WriteString(z.Roles)
if err != nil {
err = msgp.WrapError(err, "Roles")
return
}
err = en.WriteBool(z.AllowMarketing)
if err != nil {
err = msgp.WrapError(err, "AllowMarketing")
return
}
err = z.Props.EncodeMsg(en)
if err != nil {
err = msgp.WrapError(err, "Props")
return
}
err = z.NotifyProps.EncodeMsg(en)
if err != nil {
err = msgp.WrapError(err, "NotifyProps")
return
}
err = en.WriteInt64(z.LastPasswordUpdate)
if err != nil {
err = msgp.WrapError(err, "LastPasswordUpdate")
return
}
err = en.WriteInt64(z.LastPictureUpdate)
if err != nil {
err = msgp.WrapError(err, "LastPictureUpdate")
return
}
err = en.WriteInt(z.FailedAttempts)
if err != nil {
err = msgp.WrapError(err, "FailedAttempts")
return
}
err = en.WriteString(z.Locale)
if err != nil {
err = msgp.WrapError(err, "Locale")
return
}
err = z.Timezone.EncodeMsg(en)
if err != nil {
err = msgp.WrapError(err, "Timezone")
return
}
err = en.WriteBool(z.MfaActive)
if err != nil {
err = msgp.WrapError(err, "MfaActive")
return
}
err = en.WriteString(z.MfaSecret)
if err != nil {
err = msgp.WrapError(err, "MfaSecret")
return
}
if z.RemoteId == nil {
err = en.WriteNil()
if err != nil {
return
}
} else {
err = en.WriteString(*z.RemoteId)
if err != nil {
err = msgp.WrapError(err, "RemoteId")
return
}
}
err = en.WriteInt64(z.LastActivityAt)
if err != nil {
err = msgp.WrapError(err, "LastActivityAt")
return
}
err = en.WriteBool(z.IsBot)
if err != nil {
err = msgp.WrapError(err, "IsBot")
return
}
err = en.WriteString(z.BotDescription)
if err != nil {
err = msgp.WrapError(err, "BotDescription")
return
}
err = en.WriteInt64(z.BotLastIconUpdate)
if err != nil {
err = msgp.WrapError(err, "BotLastIconUpdate")
return
}
err = en.WriteString(z.TermsOfServiceId)
if err != nil {
err = msgp.WrapError(err, "TermsOfServiceId")
return
}
err = en.WriteInt64(z.TermsOfServiceCreateAt)
if err != nil {
err = msgp.WrapError(err, "TermsOfServiceCreateAt")
return
}
return
}
// MarshalMsg implements msgp.Marshaler
func (z *User) MarshalMsg(b []byte) (o []byte, err error) {
o = msgp.Require(b, z.Msgsize())
// array header, size 32
o = append(o, 0xdc, 0x0, 0x20)
o = msgp.AppendString(o, z.Id)
o = msgp.AppendInt64(o, z.CreateAt)
o = msgp.AppendInt64(o, z.UpdateAt)
o = msgp.AppendInt64(o, z.DeleteAt)
o = msgp.AppendString(o, z.Username)
o = msgp.AppendString(o, z.Password)
if z.AuthData == nil {
o = msgp.AppendNil(o)
} else {
o = msgp.AppendString(o, *z.AuthData)
}
o = msgp.AppendString(o, z.AuthService)
o = msgp.AppendString(o, z.Email)
o = msgp.AppendBool(o, z.EmailVerified)
o = msgp.AppendString(o, z.Nickname)
o = msgp.AppendString(o, z.FirstName)
o = msgp.AppendString(o, z.LastName)
o = msgp.AppendString(o, z.Position)
o = msgp.AppendString(o, z.Roles)
o = msgp.AppendBool(o, z.AllowMarketing)
o, err = z.Props.MarshalMsg(o)
if err != nil {
err = msgp.WrapError(err, "Props")
return
}
o, err = z.NotifyProps.MarshalMsg(o)
if err != nil {
err = msgp.WrapError(err, "NotifyProps")
return
}
o = msgp.AppendInt64(o, z.LastPasswordUpdate)
o = msgp.AppendInt64(o, z.LastPictureUpdate)
o = msgp.AppendInt(o, z.FailedAttempts)
o = msgp.AppendString(o, z.Locale)
o, err = z.Timezone.MarshalMsg(o)
if err != nil {
err = msgp.WrapError(err, "Timezone")
return
}
o = msgp.AppendBool(o, z.MfaActive)
o = msgp.AppendString(o, z.MfaSecret)
if z.RemoteId == nil {
o = msgp.AppendNil(o)
} else {
o = msgp.AppendString(o, *z.RemoteId)
}
o = msgp.AppendInt64(o, z.LastActivityAt)
o = msgp.AppendBool(o, z.IsBot)
o = msgp.AppendString(o, z.BotDescription)
o = msgp.AppendInt64(o, z.BotLastIconUpdate)
o = msgp.AppendString(o, z.TermsOfServiceId)
o = msgp.AppendInt64(o, z.TermsOfServiceCreateAt)
return
}
// UnmarshalMsg implements msgp.Unmarshaler
func (z *User) UnmarshalMsg(bts []byte) (o []byte, err error) {
var zb0001 uint32
zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts)
if err != nil {
err = msgp.WrapError(err)
return
}
if zb0001 != 32 {
err = msgp.ArrayError{Wanted: 32, Got: zb0001}
return
}
z.Id, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Id")
return
}
z.CreateAt, bts, err = msgp.ReadInt64Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "CreateAt")
return
}
z.UpdateAt, bts, err = msgp.ReadInt64Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "UpdateAt")
return
}
z.DeleteAt, bts, err = msgp.ReadInt64Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "DeleteAt")
return
}
z.Username, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Username")
return
}
z.Password, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Password")
return
}
if msgp.IsNil(bts) {
bts, err = msgp.ReadNilBytes(bts)
if err != nil {
return
}
z.AuthData = nil
} else {
if z.AuthData == nil {
z.AuthData = new(string)
}
*z.AuthData, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "AuthData")
return
}
}
z.AuthService, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "AuthService")
return
}
z.Email, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Email")
return
}
z.EmailVerified, bts, err = msgp.ReadBoolBytes(bts)
if err != nil {
err = msgp.WrapError(err, "EmailVerified")
return
}
z.Nickname, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Nickname")
return
}
z.FirstName, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "FirstName")
return
}
z.LastName, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "LastName")
return
}
z.Position, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Position")
return
}
z.Roles, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Roles")
return
}
z.AllowMarketing, bts, err = msgp.ReadBoolBytes(bts)
if err != nil {
err = msgp.WrapError(err, "AllowMarketing")
return
}
bts, err = z.Props.UnmarshalMsg(bts)
if err != nil {
err = msgp.WrapError(err, "Props")
return
}
bts, err = z.NotifyProps.UnmarshalMsg(bts)
if err != nil {
err = msgp.WrapError(err, "NotifyProps")
return
}
z.LastPasswordUpdate, bts, err = msgp.ReadInt64Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "LastPasswordUpdate")
return
}
z.LastPictureUpdate, bts, err = msgp.ReadInt64Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "LastPictureUpdate")
return
}
z.FailedAttempts, bts, err = msgp.ReadIntBytes(bts)
if err != nil {
err = msgp.WrapError(err, "FailedAttempts")
return
}
z.Locale, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Locale")
return
}
bts, err = z.Timezone.UnmarshalMsg(bts)
if err != nil {
err = msgp.WrapError(err, "Timezone")
return
}
z.MfaActive, bts, err = msgp.ReadBoolBytes(bts)
if err != nil {
err = msgp.WrapError(err, "MfaActive")
return
}
z.MfaSecret, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "MfaSecret")
return
}
if msgp.IsNil(bts) {
bts, err = msgp.ReadNilBytes(bts)
if err != nil {
return
}
z.RemoteId = nil
} else {
if z.RemoteId == nil {
z.RemoteId = new(string)
}
*z.RemoteId, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "RemoteId")
return
}
}
z.LastActivityAt, bts, err = msgp.ReadInt64Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "LastActivityAt")
return
}
z.IsBot, bts, err = msgp.ReadBoolBytes(bts)
if err != nil {
err = msgp.WrapError(err, "IsBot")
return
}
z.BotDescription, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "BotDescription")
return
}
z.BotLastIconUpdate, bts, err = msgp.ReadInt64Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "BotLastIconUpdate")
return
}
z.TermsOfServiceId, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err, "TermsOfServiceId")
return
}
z.TermsOfServiceCreateAt, bts, err = msgp.ReadInt64Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "TermsOfServiceCreateAt")
return
}
o = bts
return
}
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
func (z *User) Msgsize() (s int) {
s = 3 + msgp.StringPrefixSize + len(z.Id) + msgp.Int64Size + msgp.Int64Size + msgp.Int64Size + msgp.StringPrefixSize + len(z.Username) + msgp.StringPrefixSize + len(z.Password)
if z.AuthData == nil {
s += msgp.NilSize
} else {
s += msgp.StringPrefixSize + len(*z.AuthData)
}
s += msgp.StringPrefixSize + len(z.AuthService) + msgp.StringPrefixSize + len(z.Email) + msgp.BoolSize + msgp.StringPrefixSize + len(z.Nickname) + msgp.StringPrefixSize + len(z.FirstName) + msgp.StringPrefixSize + len(z.LastName) + msgp.StringPrefixSize + len(z.Position) + msgp.StringPrefixSize + len(z.Roles) + msgp.BoolSize + z.Props.Msgsize() + z.NotifyProps.Msgsize() + msgp.Int64Size + msgp.Int64Size + msgp.IntSize + msgp.StringPrefixSize + len(z.Locale) + z.Timezone.Msgsize() + msgp.BoolSize + msgp.StringPrefixSize + len(z.MfaSecret)
if z.RemoteId == nil {
s += msgp.NilSize
} else {
s += msgp.StringPrefixSize + len(*z.RemoteId)
}
s += msgp.Int64Size + msgp.BoolSize + msgp.StringPrefixSize + len(z.BotDescription) + msgp.Int64Size + msgp.StringPrefixSize + len(z.TermsOfServiceId) + msgp.Int64Size
return
}
// DecodeMsg implements msgp.Decodable
func (z *UserMap) DecodeMsg(dc *msgp.Reader) (err error) {
var zb0003 uint32
zb0003, err = dc.ReadMapHeader()
if err != nil {
err = msgp.WrapError(err)
return
}
if (*z) == nil {
(*z) = make(UserMap, zb0003)
} else if len((*z)) > 0 {
for key := range *z {
delete((*z), key)
}
}
for zb0003 > 0 {
zb0003--
var zb0001 string
var zb0002 *User
zb0001, err = dc.ReadString()
if err != nil {
err = msgp.WrapError(err)
return
}
if dc.IsNil() {
err = dc.ReadNil()
if err != nil {
err = msgp.WrapError(err, zb0001)
return
}
zb0002 = nil
} else {
if zb0002 == nil {
zb0002 = new(User)
}
err = zb0002.DecodeMsg(dc)
if err != nil {
err = msgp.WrapError(err, zb0001)
return
}
}
(*z)[zb0001] = zb0002
}
return
}
// EncodeMsg implements msgp.Encodable
func (z UserMap) EncodeMsg(en *msgp.Writer) (err error) {
err = en.WriteMapHeader(uint32(len(z)))
if err != nil {
err = msgp.WrapError(err)
return
}
for zb0004, zb0005 := range z {
err = en.WriteString(zb0004)
if err != nil {
err = msgp.WrapError(err)
return
}
if zb0005 == nil {
err = en.WriteNil()
if err != nil {
return
}
} else {
err = zb0005.EncodeMsg(en)
if err != nil {
err = msgp.WrapError(err, zb0004)
return
}
}
}
return
}
// MarshalMsg implements msgp.Marshaler
func (z UserMap) MarshalMsg(b []byte) (o []byte, err error) {
o = msgp.Require(b, z.Msgsize())
o = msgp.AppendMapHeader(o, uint32(len(z)))
for zb0004, zb0005 := range z {
o = msgp.AppendString(o, zb0004)
if zb0005 == nil {
o = msgp.AppendNil(o)
} else {
o, err = zb0005.MarshalMsg(o)
if err != nil {
err = msgp.WrapError(err, zb0004)
return
}
}
}
return
}
// UnmarshalMsg implements msgp.Unmarshaler
func (z *UserMap) UnmarshalMsg(bts []byte) (o []byte, err error) {
var zb0003 uint32
zb0003, bts, err = msgp.ReadMapHeaderBytes(bts)
if err != nil {
err = msgp.WrapError(err)
return
}
if (*z) == nil {
(*z) = make(UserMap, zb0003)
} else if len((*z)) > 0 {
for key := range *z {
delete((*z), key)
}
}
for zb0003 > 0 {
var zb0001 string
var zb0002 *User
zb0003--
zb0001, bts, err = msgp.ReadStringBytes(bts)
if err != nil {
err = msgp.WrapError(err)
return
}
if msgp.IsNil(bts) {
bts, err = msgp.ReadNilBytes(bts)
if err != nil {
return
}
zb0002 = nil
} else {
if zb0002 == nil {
zb0002 = new(User)
}
bts, err = zb0002.UnmarshalMsg(bts)
if err != nil {
err = msgp.WrapError(err, zb0001)
return
}
}
(*z)[zb0001] = zb0002
}
o = bts
return
}
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
func (z UserMap) Msgsize() (s int) {
s = msgp.MapHeaderSize
if z != nil {
for zb0004, zb0005 := range z {
_ = zb0005
s += msgp.StringPrefixSize + len(zb0004)
if zb0005 == nil {
s += msgp.NilSize
} else {
s += zb0005.Msgsize()
}
}
}
return
}

View File

@ -18,10 +18,11 @@ import (
"regexp"
"strconv"
"strings"
"sync"
"time"
"unicode"
goi18n "github.com/mattermost/go-i18n/i18n"
"github.com/mattermost/mattermost-server/v5/shared/i18n"
"github.com/pborman/uuid"
)
@ -30,10 +31,10 @@ const (
UPPERCASE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
NUMBERS = "0123456789"
SYMBOLS = " !\"\\#$%&'()*+,-./:;<=>?@[]^_`|~"
MB = 1 << 20
)
type StringInterface map[string]interface{}
type StringMap map[string]string
type StringArray []string
func (sa StringArray) Remove(input string) StringArray {
@ -72,10 +73,13 @@ func (sa StringArray) Equals(input StringArray) bool {
return true
}
var translateFunc goi18n.TranslateFunc = nil
var translateFunc i18n.TranslateFunc
var translateFuncOnce sync.Once
func AppErrorInit(t goi18n.TranslateFunc) {
translateFunc = t
func AppErrorInit(t i18n.TranslateFunc) {
translateFuncOnce.Do(func() {
translateFunc = t
})
}
type AppError struct {
@ -93,7 +97,7 @@ func (er *AppError) Error() string {
return er.Where + ": " + er.Message + ", " + er.DetailedError
}
func (er *AppError) Translate(T goi18n.TranslateFunc) {
func (er *AppError) Translate(T i18n.TranslateFunc) {
if T == nil {
er.Message = er.Id
return
@ -106,12 +110,11 @@ func (er *AppError) Translate(T goi18n.TranslateFunc) {
}
}
func (er *AppError) SystemMessage(T goi18n.TranslateFunc) string {
func (er *AppError) SystemMessage(T i18n.TranslateFunc) string {
if er.params == nil {
return T(er.Id)
} else {
return T(er.Id, er.params)
}
return T(er.Id, er.params)
}
func (er *AppError) ToJson() string {
@ -132,11 +135,10 @@ func AppErrorFromJson(data io.Reader) *AppError {
decoder := json.NewDecoder(strings.NewReader(str))
var er AppError
err := decoder.Decode(&er)
if err == nil {
return &er
} else {
if err != nil {
return NewAppError("AppErrorFromJson", "model.utils.decode_json.app_error", nil, "body: "+str, http.StatusInternalServerError)
}
return &er
}
func NewAppError(where string, id string, params map[string]interface{}, details string, status int) *AppError {
@ -183,14 +185,6 @@ func NewRandomString(length int) string {
return encoding.EncodeToString(data)[:length]
}
// NewRandomBase32String returns a base32 encoded string of a random slice
// of bytes of the given size. The resulting entropy will be (8 * size) bits.
func NewRandomBase32String(size int) string {
data := make([]byte, size)
rand.Read(data)
return base32.StdEncoding.EncodeToString(data)
}
// GetMillis is a convenience method to get milliseconds since epoch.
func GetMillis() int64 {
return time.Now().UnixNano() / int64(time.Millisecond)
@ -201,6 +195,11 @@ func GetMillisForTime(thisTime time.Time) int64 {
return thisTime.UnixNano() / int64(time.Millisecond)
}
// GetTimeForMillis is a convenience method to get time.Time for milliseconds since epoch.
func GetTimeForMillis(millis int64) time.Time {
return time.Unix(0, millis*int64(time.Millisecond))
}
// PadDateStringZeros is a convenience method to pad 2 digit date parts with zeros to meet ISO 8601 format
func PadDateStringZeros(dateString string) string {
parts := strings.Split(dateString, "-")
@ -254,9 +253,8 @@ func MapFromJson(data io.Reader) map[string]string {
var objmap map[string]string
if err := decoder.Decode(&objmap); err != nil {
return make(map[string]string)
} else {
return objmap
}
return objmap
}
// MapFromJson will decode the key/value pair map
@ -266,9 +264,8 @@ func MapBoolFromJson(data io.Reader) map[string]bool {
var objmap map[string]bool
if err := decoder.Decode(&objmap); err != nil {
return make(map[string]bool)
} else {
return objmap
}
return objmap
}
func ArrayToJson(objmap []string) string {
@ -282,9 +279,8 @@ func ArrayFromJson(data io.Reader) []string {
var objmap []string
if err := decoder.Decode(&objmap); err != nil {
return make([]string, 0)
} else {
return objmap
}
return objmap
}
func ArrayFromInterface(data interface{}) []string {
@ -315,9 +311,8 @@ func StringInterfaceFromJson(data io.Reader) map[string]interface{} {
var objmap map[string]interface{}
if err := decoder.Decode(&objmap); err != nil {
return make(map[string]interface{})
} else {
return objmap
}
return objmap
}
func StringToJson(s string) string {
@ -331,14 +326,19 @@ func StringFromJson(data io.Reader) string {
var s string
if err := decoder.Decode(&s); err != nil {
return ""
} else {
return s
}
return s
}
// ToJson serializes an arbitrary data type to JSON, discarding the error.
func ToJson(v interface{}) []byte {
b, _ := json.Marshal(v)
return b
}
func GetServerIpAddress(iface string) string {
var addrs []net.Addr
if len(iface) == 0 {
if iface == "" {
var err error
addrs, err = net.InterfaceAddrs()
if err != nil {
@ -397,6 +397,7 @@ var reservedName = []string{
"channel",
"claim",
"error",
"files",
"help",
"landing",
"login",
@ -437,6 +438,12 @@ func IsValidAlphaNumHyphenUnderscore(s string, withFormat bool) bool {
return validSimpleAlphaNumHyphenUnderscore.MatchString(s)
}
func IsValidAlphaNumHyphenUnderscorePlus(s string) bool {
validSimpleAlphaNumHyphenUnderscorePlus := regexp.MustCompile(`^[a-zA-Z0-9+_-]+$`)
return validSimpleAlphaNumHyphenUnderscorePlus.MatchString(s)
}
func Etag(parts ...interface{}) string {
etag := CurrentVersion
@ -486,25 +493,6 @@ func ParseHashtags(text string) (string, string) {
return strings.TrimSpace(hashtagString), strings.TrimSpace(plainString)
}
func IsFileExtImage(ext string) bool {
ext = strings.ToLower(ext)
for _, imgExt := range IMAGE_EXTENSIONS {
if ext == imgExt {
return true
}
}
return false
}
func GetImageMimeType(ext string) string {
ext = strings.ToLower(ext)
if len(IMAGE_MIME_TYPES[ext]) == 0 {
return "image"
} else {
return IMAGE_MIME_TYPES[ext]
}
}
func ClearMentionTags(post string) string {
post = strings.Replace(post, "<mention>", "", -1)
post = strings.Replace(post, "</mention>", "", -1)
@ -721,3 +709,18 @@ func filterBlocklist(r rune) rune {
return r
}
// UniqueStrings returns a unique subset of the string slice provided.
func UniqueStrings(input []string) []string {
u := make([]string, 0, len(input))
m := make(map[string]bool)
for _, val := range input {
if _, ok := m[val]; !ok {
m[val] = true
u = append(u, val)
}
}
return u
}

View File

@ -13,6 +13,17 @@ import (
// It should be maintained in chronological order with most current
// release at the front of the list.
var versions = []string{
"5.39.0",
"5.38.2",
"5.38.1",
"5.38.0",
"5.37.0",
"5.36.0",
"5.35.0",
"5.34.0",
"5.33.0",
"5.32.0",
"5.31.0",
"5.30.0",
"5.29.0",
"5.28.0",
@ -148,9 +159,8 @@ func IsCurrentVersion(versionToCheck string) bool {
if toCheckMajor == currentMajor && toCheckMinor == currentMinor {
return true
} else {
return false
}
return false
}
func IsPreviousVersionsSupported(versionToCheck string) bool {

View File

@ -70,6 +70,10 @@ const (
WEBSOCKET_WARN_METRIC_STATUS_RECEIVED = "warn_metric_status_received"
WEBSOCKET_WARN_METRIC_STATUS_REMOVED = "warn_metric_status_removed"
WEBSOCKET_EVENT_CLOUD_PAYMENT_STATUS_UPDATED = "cloud_payment_status_updated"
WEBSOCKET_EVENT_THREAD_UPDATED = "thread_updated"
WEBSOCKET_EVENT_THREAD_FOLLOW_CHANGED = "thread_follow_changed"
WEBSOCKET_EVENT_THREAD_READ_CHANGED = "thread_read_changed"
WEBSOCKET_FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED = "first_admin_visit_marketplace_status_received"
)
type WebSocketMessage interface {

View File

@ -7,7 +7,7 @@ import (
"encoding/json"
"io"
goi18n "github.com/mattermost/go-i18n/i18n"
"github.com/mattermost/mattermost-server/v5/shared/i18n"
)
// WebSocketRequest represents a request made to the server through a websocket.
@ -18,9 +18,9 @@ type WebSocketRequest struct {
Data map[string]interface{} `json:"data"` // The metadata for an action.
// Server-provided fields
Session Session `json:"-"`
T goi18n.TranslateFunc `json:"-"`
Locale string `json:"-"`
Session Session `json:"-"`
T i18n.TranslateFunc `json:"-"`
Locale string `json:"-"`
}
func (o *WebSocketRequest) ToJson() string {