diff options
Diffstat (limited to 'vendor/github.com/mattermost/platform/model')
25 files changed, 1760 insertions, 204 deletions
diff --git a/vendor/github.com/mattermost/platform/model/authorization.go b/vendor/github.com/mattermost/platform/model/authorization.go index 75aebf55..a7a6f374 100644 --- a/vendor/github.com/mattermost/platform/model/authorization.go +++ b/vendor/github.com/mattermost/platform/model/authorization.go @@ -27,7 +27,10 @@ var PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS *Permission var PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS *Permission var PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE *Permission var PERMISSION_MANAGE_ROLES *Permission +var PERMISSION_MANAGE_TEAM_ROLES *Permission +var PERMISSION_MANAGE_CHANNEL_ROLES *Permission var PERMISSION_CREATE_DIRECT_CHANNEL *Permission +var PERMISSION_CREATE_GROUP_CHANNEL *Permission var PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES *Permission var PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES *Permission var PERMISSION_LIST_TEAM_CHANNELS *Permission @@ -46,9 +49,13 @@ var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission var PERMISSION_CREATE_POST *Permission var PERMISSION_EDIT_POST *Permission var PERMISSION_EDIT_OTHERS_POSTS *Permission +var PERMISSION_DELETE_POST *Permission +var PERMISSION_DELETE_OTHERS_POSTS *Permission var PERMISSION_REMOVE_USER_FROM_TEAM *Permission +var PERMISSION_CREATE_TEAM *Permission var PERMISSION_MANAGE_TEAM *Permission var PERMISSION_IMPORT_TEAM *Permission +var PERMISSION_VIEW_TEAM *Permission // General permission that encompases all system admin functions // in the future this could be broken up to allow access to some @@ -123,6 +130,16 @@ func InitalizePermissions() { "authentication.permissions.manage_roles.name", "authentication.permissions.manage_roles.description", } + PERMISSION_MANAGE_TEAM_ROLES = &Permission{ + "manage_team_roles", + "authentication.permissions.manage_team_roles.name", + "authentication.permissions.manage_team_roles.description", + } + PERMISSION_MANAGE_CHANNEL_ROLES = &Permission{ + "manage_channel_roles", + "authentication.permissions.manage_channel_roles.name", + "authentication.permissions.manage_channel_roles.description", + } PERMISSION_MANAGE_SYSTEM = &Permission{ "manage_system", "authentication.permissions.manage_system.name", @@ -133,6 +150,11 @@ func InitalizePermissions() { "authentication.permissions.create_direct_channel.name", "authentication.permissions.create_direct_channel.description", } + PERMISSION_CREATE_GROUP_CHANNEL = &Permission{ + "create_group_channel", + "authentication.permissions.create_group_channel.name", + "authentication.permissions.create_group_channel.description", + } PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES = &Permission{ "manage__publicchannel_properties", "authentication.permissions.manage_public_channel_properties.name", @@ -223,11 +245,26 @@ func InitalizePermissions() { "authentication.permissions.edit_others_posts.name", "authentication.permissions.edit_others_posts.description", } + PERMISSION_DELETE_POST = &Permission{ + "delete_post", + "authentication.permissions.delete_post.name", + "authentication.permissions.delete_post.description", + } + PERMISSION_DELETE_OTHERS_POSTS = &Permission{ + "delete_others_posts", + "authentication.permissions.delete_others_posts.name", + "authentication.permissions.delete_others_posts.description", + } PERMISSION_REMOVE_USER_FROM_TEAM = &Permission{ "remove_user_from_team", "authentication.permissions.remove_user_from_team.name", "authentication.permissions.remove_user_from_team.description", } + PERMISSION_CREATE_TEAM = &Permission{ + "create_team", + "authentication.permissions.create_team.name", + "authentication.permissions.create_team.description", + } PERMISSION_MANAGE_TEAM = &Permission{ "manage_team", "authentication.permissions.manage_team.name", @@ -238,6 +275,11 @@ func InitalizePermissions() { "authentication.permissions.import_team.name", "authentication.permissions.import_team.description", } + PERMISSION_VIEW_TEAM = &Permission{ + "view_team", + "authentication.permissions.view_team.name", + "authentication.permissions.view_team.description", + } } func InitalizeRoles() { @@ -264,7 +306,9 @@ func InitalizeRoles() { "channel_admin", "authentication.roles.channel_admin.name", "authentication.roles.channel_admin.description", - []string{}, + []string{ + PERMISSION_MANAGE_CHANNEL_ROLES.Id, + }, } BuiltInRoles[ROLE_CHANNEL_ADMIN.Id] = ROLE_CHANNEL_ADMIN ROLE_CHANNEL_GUEST = &Role{ @@ -282,6 +326,7 @@ func InitalizeRoles() { []string{ PERMISSION_LIST_TEAM_CHANNELS.Id, PERMISSION_JOIN_PUBLIC_CHANNELS.Id, + PERMISSION_VIEW_TEAM.Id, }, } BuiltInRoles[ROLE_TEAM_USER.Id] = ROLE_TEAM_USER @@ -295,7 +340,8 @@ func InitalizeRoles() { PERMISSION_REMOVE_USER_FROM_TEAM.Id, PERMISSION_MANAGE_TEAM.Id, PERMISSION_IMPORT_TEAM.Id, - PERMISSION_MANAGE_ROLES.Id, + PERMISSION_MANAGE_TEAM_ROLES.Id, + PERMISSION_MANAGE_CHANNEL_ROLES.Id, PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id, PERMISSION_MANAGE_SLASH_COMMANDS.Id, PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id, @@ -310,6 +356,7 @@ func InitalizeRoles() { "authentication.roles.global_user.description", []string{ PERMISSION_CREATE_DIRECT_CHANNEL.Id, + PERMISSION_CREATE_GROUP_CHANNEL.Id, PERMISSION_PERMANENT_DELETE_USER.Id, PERMISSION_MANAGE_OAUTH.Id, }, @@ -329,6 +376,7 @@ func InitalizeRoles() { []string{ PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id, PERMISSION_MANAGE_SYSTEM.Id, + PERMISSION_MANAGE_ROLES.Id, PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, PERMISSION_DELETE_PUBLIC_CHANNEL.Id, PERMISSION_CREATE_PUBLIC_CHANNEL.Id, @@ -340,6 +388,9 @@ func InitalizeRoles() { PERMISSION_EDIT_OTHER_USERS.Id, PERMISSION_MANAGE_OAUTH.Id, PERMISSION_INVITE_USER.Id, + PERMISSION_DELETE_POST.Id, + PERMISSION_DELETE_OTHERS_POSTS.Id, + PERMISSION_CREATE_TEAM.Id, }, ROLE_TEAM_USER.Permissions..., ), diff --git a/vendor/github.com/mattermost/platform/model/channel.go b/vendor/github.com/mattermost/platform/model/channel.go index 2ad257cc..d24fdb2b 100644 --- a/vendor/github.com/mattermost/platform/model/channel.go +++ b/vendor/github.com/mattermost/platform/model/channel.go @@ -4,8 +4,12 @@ package model import ( + "crypto/sha1" + "encoding/hex" "encoding/json" "io" + "sort" + "strings" "unicode/utf8" ) @@ -13,11 +17,16 @@ const ( CHANNEL_OPEN = "O" CHANNEL_PRIVATE = "P" CHANNEL_DIRECT = "D" + CHANNEL_GROUP = "G" + CHANNEL_GROUP_MAX_USERS = 8 + CHANNEL_GROUP_MIN_USERS = 3 DEFAULT_CHANNEL = "town-square" CHANNEL_DISPLAY_NAME_MAX_RUNES = 64 + CHANNEL_NAME_MIN_LENGTH = 2 CHANNEL_NAME_MAX_LENGTH = 64 CHANNEL_HEADER_MAX_RUNES = 1024 CHANNEL_PURPOSE_MAX_RUNES = 250 + CHANNEL_CACHE_SIZE = 25000 ) type Channel struct { @@ -83,15 +92,11 @@ func (o *Channel) IsValid() *AppError { return NewLocAppError("Channel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+o.Id) } - if len(o.Name) > CHANNEL_NAME_MAX_LENGTH { - return NewLocAppError("Channel.IsValid", "model.channel.is_valid.name.app_error", nil, "id="+o.Id) - } - if !IsValidChannelIdentifier(o.Name) { return NewLocAppError("Channel.IsValid", "model.channel.is_valid.2_or_more.app_error", nil, "id="+o.Id) } - if !(o.Type == CHANNEL_OPEN || o.Type == CHANNEL_PRIVATE || o.Type == CHANNEL_DIRECT) { + if !(o.Type == CHANNEL_OPEN || o.Type == CHANNEL_PRIVATE || o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP) { return NewLocAppError("Channel.IsValid", "model.channel.is_valid.type.app_error", nil, "id="+o.Id) } @@ -128,6 +133,10 @@ func (o *Channel) ExtraUpdated() { o.ExtraUpdateAt = GetMillis() } +func (o *Channel) IsGroupOrDirect() bool { + return o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP +} + func GetDMNameFromIds(userId1, userId2 string) string { if userId1 > userId2 { return userId2 + "__" + userId1 @@ -135,3 +144,31 @@ func GetDMNameFromIds(userId1, userId2 string) string { return userId1 + "__" + userId2 } } + +func GetGroupDisplayNameFromUsers(users []*User, truncate bool) string { + usernames := make([]string, len(users)) + for index, user := range users { + usernames[index] = user.Username + } + + sort.Strings(usernames) + + name := strings.Join(usernames, ", ") + + if truncate && len(name) > CHANNEL_NAME_MAX_LENGTH { + name = name[:CHANNEL_NAME_MAX_LENGTH] + } + + return name +} + +func GetGroupNameFromUserIds(userIds []string) string { + sort.Strings(userIds) + + h := sha1.New() + for _, id := range userIds { + io.WriteString(h, id) + } + + return hex.EncodeToString(h.Sum(nil)) +} diff --git a/vendor/github.com/mattermost/platform/model/channel_member.go b/vendor/github.com/mattermost/platform/model/channel_member.go index a607a505..5de58bc4 100644 --- a/vendor/github.com/mattermost/platform/model/channel_member.go +++ b/vendor/github.com/mattermost/platform/model/channel_member.go @@ -88,18 +88,32 @@ func (o *ChannelMember) IsValid() *AppError { return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.user_id.app_error", nil, "") } - notifyLevel := o.NotifyProps["desktop"] + notifyLevel := o.NotifyProps[DESKTOP_NOTIFY_PROP] if len(notifyLevel) > 20 || !IsChannelNotifyLevelValid(notifyLevel) { return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.notify_level.app_error", nil, "notify_level="+notifyLevel) } - markUnreadLevel := o.NotifyProps["mark_unread"] + markUnreadLevel := o.NotifyProps[MARK_UNREAD_NOTIFY_PROP] if len(markUnreadLevel) > 20 || !IsChannelMarkUnreadLevelValid(markUnreadLevel) { return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.unread_level.app_error", nil, "mark_unread_level="+markUnreadLevel) } + if pushLevel, ok := o.NotifyProps[PUSH_NOTIFY_PROP]; ok { + if len(pushLevel) > 20 || !IsChannelNotifyLevelValid(pushLevel) { + return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.push_level.app_error", + nil, "push_notification_level="+pushLevel) + } + } + + if sendEmail, ok := o.NotifyProps[EMAIL_NOTIFY_PROP]; ok { + if len(sendEmail) > 20 || !IsSendEmailValid(sendEmail) { + return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.email_value.app_error", + nil, "push_notification_level="+sendEmail) + } + } + return nil } @@ -126,9 +140,15 @@ func IsChannelMarkUnreadLevelValid(markUnreadLevel string) bool { return markUnreadLevel == CHANNEL_MARK_UNREAD_ALL || markUnreadLevel == CHANNEL_MARK_UNREAD_MENTION } +func IsSendEmailValid(sendEmail string) bool { + return sendEmail == CHANNEL_NOTIFY_DEFAULT || sendEmail == "true" || sendEmail == "false" +} + func GetDefaultChannelNotifyProps() StringMap { return StringMap{ - "desktop": CHANNEL_NOTIFY_DEFAULT, - "mark_unread": CHANNEL_MARK_UNREAD_ALL, + DESKTOP_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT, + MARK_UNREAD_NOTIFY_PROP: CHANNEL_MARK_UNREAD_ALL, + PUSH_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT, + EMAIL_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT, } } diff --git a/vendor/github.com/mattermost/platform/model/client.go b/vendor/github.com/mattermost/platform/model/client.go index 540bc747..24ee2c2b 100644 --- a/vendor/github.com/mattermost/platform/model/client.go +++ b/vendor/github.com/mattermost/platform/model/client.go @@ -35,12 +35,14 @@ const ( STATUS = "status" STATUS_OK = "OK" STATUS_FAIL = "FAIL" + STATUS_REMOVE = "REMOVE" CLIENT_DIR = "webapp/dist" API_URL_SUFFIX_V1 = "/api/v1" API_URL_SUFFIX_V3 = "/api/v3" - API_URL_SUFFIX = API_URL_SUFFIX_V3 + API_URL_SUFFIX_V4 = "/api/v4" + API_URL_SUFFIX = API_URL_SUFFIX_V4 ) type Result struct { @@ -71,7 +73,7 @@ type Client struct { // NewClient constructs a new client with convienence methods for talking to // the server. func NewClient(url string) *Client { - return &Client{url, url + API_URL_SUFFIX, &http.Client{}, "", "", "", "", "", ""} + return &Client{url, url + API_URL_SUFFIX_V3, &http.Client{}, "", "", "", "", "", ""} } func closeBody(r *http.Response) { @@ -782,7 +784,7 @@ func (c *Client) GetSessions(id string) (*Result, *AppError) { } func (c *Client) EmailToOAuth(m map[string]string) (*Result, *AppError) { - if r, err := c.DoApiPost("/users/claim/email_to_sso", MapToJson(m)); err != nil { + if r, err := c.DoApiPost("/users/claim/email_to_oauth", MapToJson(m)); err != nil { return nil, err } else { defer closeBody(r) @@ -1119,6 +1121,16 @@ func (c *Client) CreateDirectChannel(userId string) (*Result, *AppError) { } } +func (c *Client) CreateGroupChannel(userIds []string) (*Result, *AppError) { + if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/create_group", ArrayToJson(userIds)); err != nil { + return nil, err + } else { + defer closeBody(r) + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil + } +} + func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) { if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update", channel.ToJson()); err != nil { return nil, err @@ -1471,6 +1483,21 @@ func (c *Client) GetPostById(postId string, etag string) (*PostList, *ResponseMe } } +// GetPermalink returns a post list, based on the provided channel and post ID. +func (c *Client) GetPermalink(channelId string, postId string, etag string) (*PostList, *ResponseMetadata) { + if r, err := c.DoApiGet(c.GetTeamRoute()+fmt.Sprintf("/pltmp/%v", postId), "", etag); err != nil { + return nil, &ResponseMetadata{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return PostListFromJson(r.Body), + &ResponseMetadata{ + StatusCode: r.StatusCode, + RequestId: r.Header.Get(HEADER_REQUEST_ID), + Etag: r.Header.Get(HEADER_ETAG_SERVER), + } + } +} + func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError) { if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/delete", postId), ""); err != nil { return nil, err @@ -1991,6 +2018,16 @@ func (c *Client) CreateIncomingWebhook(hook *IncomingWebhook) (*Result, *AppErro } } +func (c *Client) UpdateIncomingWebhook(hook *IncomingWebhook) (*Result, *AppError) { + if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/incoming/update", hook.ToJson()); err != nil { + return nil, err + } else { + defer closeBody(r) + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), IncomingWebhookFromJson(r.Body)}, nil + } +} + func (c *Client) PostToWebhook(id, payload string) (*Result, *AppError) { if r, err := c.DoPost("/hooks/"+id, payload, "application/x-www-form-urlencoded"); err != nil { return nil, err @@ -2082,6 +2119,16 @@ func (c *Client) CreateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppErro } } +func (c *Client) UpdateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppError) { + if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/update", hook.ToJson()); err != nil { + return nil, err + } else { + defer closeBody(r) + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookFromJson(r.Body)}, nil + } +} + func (c *Client) DeleteOutgoingWebhook(id string) (*Result, *AppError) { data := make(map[string]string) data["id"] = id @@ -2319,3 +2366,26 @@ func (c *Client) ListReactions(channelId string, postId string) ([]*Reaction, *A return ReactionsFromJson(r.Body), nil } } + +// Updates the user's roles in the channel by replacing them with the roles provided. +func (c *Client) UpdateChannelRoles(channelId string, userId string, roles string) (map[string]string, *ResponseMetadata) { + data := make(map[string]string) + data["new_roles"] = roles + data["user_id"] = userId + + if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/update_member_roles", MapToJson(data)); err != nil { + metadata := ResponseMetadata{Error: err} + if r != nil { + metadata.StatusCode = r.StatusCode + } + return nil, &metadata + } else { + defer closeBody(r) + return MapFromJson(r.Body), + &ResponseMetadata{ + StatusCode: r.StatusCode, + RequestId: r.Header.Get(HEADER_REQUEST_ID), + Etag: r.Header.Get(HEADER_ETAG_SERVER), + } + } +} diff --git a/vendor/github.com/mattermost/platform/model/client4.go b/vendor/github.com/mattermost/platform/model/client4.go new file mode 100644 index 00000000..d3bb6534 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/client4.go @@ -0,0 +1,1006 @@ +// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "net/url" + "strconv" + "strings" +) + +type Response struct { + StatusCode int + Error *AppError + RequestId string + Etag string + ServerVersion string +} + +type Client4 struct { + Url string // The location of the server, for example "http://localhost:8065" + ApiUrl string // The api location of the server, for example "http://localhost:8065/api/v4" + HttpClient *http.Client // The http client + AuthToken string + AuthType string +} + +func NewAPIv4Client(url string) *Client4 { + return &Client4{url, url + API_URL_SUFFIX, &http.Client{}, "", ""} +} + +func BuildResponse(r *http.Response) *Response { + return &Response{ + StatusCode: r.StatusCode, + RequestId: r.Header.Get(HEADER_REQUEST_ID), + Etag: r.Header.Get(HEADER_ETAG_SERVER), + ServerVersion: r.Header.Get(HEADER_VERSION_ID), + } +} + +func (c *Client4) SetOAuthToken(token string) { + c.AuthToken = token + c.AuthType = HEADER_TOKEN +} + +func (c *Client4) ClearOAuthToken() { + c.AuthToken = "" + c.AuthType = HEADER_BEARER +} + +func (c *Client4) GetUsersRoute() string { + return fmt.Sprintf("/users") +} + +func (c *Client4) GetUserRoute(userId string) string { + return fmt.Sprintf(c.GetUsersRoute()+"/%v", userId) +} + +func (c *Client4) GetUserByUsernameRoute(userName string) string { + return fmt.Sprintf(c.GetUsersRoute()+"/username/%v", userName) +} + +func (c *Client4) GetUserByEmailRoute(email string) string { + return fmt.Sprintf(c.GetUsersRoute()+"/email/%v", email) +} + +func (c *Client4) GetTeamsRoute() string { + return fmt.Sprintf("/teams") +} + +func (c *Client4) GetTeamRoute(teamId string) string { + return fmt.Sprintf(c.GetTeamsRoute()+"/%v", teamId) +} + +func (c *Client4) GetTeamByNameRoute(teamName string) string { + return fmt.Sprintf(c.GetTeamsRoute()+"/name/%v", teamName) +} + +func (c *Client4) GetTeamMemberRoute(teamId, userId string) string { + return fmt.Sprintf(c.GetTeamRoute(teamId)+"/members/%v", userId) +} + +func (c *Client4) GetTeamMembersRoute(teamId string) string { + return fmt.Sprintf(c.GetTeamRoute(teamId) + "/members") +} + +func (c *Client4) GetTeamStatsRoute(teamId string) string { + return fmt.Sprintf(c.GetTeamRoute(teamId) + "/stats") +} + +func (c *Client4) GetChannelsRoute() string { + return fmt.Sprintf("/channels") +} + +func (c *Client4) GetChannelRoute(channelId string) string { + return fmt.Sprintf(c.GetChannelsRoute()+"/%v", channelId) +} + +func (c *Client4) GetChannelByNameRoute(channelName, teamId string) string { + return fmt.Sprintf(c.GetTeamRoute(teamId)+"/channels/name/%v", channelName) +} + +func (c *Client4) GetChannelByNameForTeamNameRoute(channelName, teamName string) string { + return fmt.Sprintf(c.GetTeamByNameRoute(teamName)+"/channels/name/%v", channelName) +} + +func (c *Client4) GetChannelMembersRoute(channelId string) string { + return fmt.Sprintf(c.GetChannelRoute(channelId) + "/members") +} + +func (c *Client4) GetChannelMemberRoute(channelId, userId string) string { + return fmt.Sprintf(c.GetChannelMembersRoute(channelId)+"/%v", userId) +} + +func (c *Client4) GetPostsRoute() string { + return fmt.Sprintf("/posts") +} + +func (c *Client4) GetPostRoute(postId string) string { + return fmt.Sprintf(c.GetPostsRoute()+"/%v", postId) +} + +func (c *Client4) GetFilesRoute() string { + return fmt.Sprintf("/files") +} + +func (c *Client4) GetFileRoute(fileId string) string { + return fmt.Sprintf(c.GetFilesRoute()+"/%v", fileId) +} + +func (c *Client4) GetSystemRoute() string { + return fmt.Sprintf("/system") +} + +func (c *Client4) GetIncomingWebhooksRoute() string { + return fmt.Sprintf("/hooks/incoming") +} + +func (c *Client4) GetPreferencesRoute(userId string) string { + return fmt.Sprintf(c.GetUserRoute(userId) + "/preferences") +} + +func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) { + return c.DoApiRequest(http.MethodGet, url, "", etag) +} + +func (c *Client4) DoApiPost(url string, data string) (*http.Response, *AppError) { + return c.DoApiRequest(http.MethodPost, url, data, "") +} + +func (c *Client4) DoApiPut(url string, data string) (*http.Response, *AppError) { + return c.DoApiRequest(http.MethodPut, url, data, "") +} + +func (c *Client4) DoApiDelete(url string) (*http.Response, *AppError) { + return c.DoApiRequest(http.MethodDelete, url, "", "") +} + +func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response, *AppError) { + rq, _ := http.NewRequest(method, c.ApiUrl+url, strings.NewReader(data)) + rq.Close = true + + if len(etag) > 0 { + rq.Header.Set(HEADER_ETAG_CLIENT, etag) + } + + if len(c.AuthToken) > 0 { + rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) + } + + if rp, err := c.HttpClient.Do(rq); err != nil { + return nil, NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error()) + } else if rp.StatusCode == 304 { + return rp, nil + } else if rp.StatusCode >= 300 { + defer closeBody(rp) + return rp, AppErrorFromJson(rp.Body) + } else { + return rp, nil + } +} + +func (c *Client4) DoUploadFile(url string, data []byte, contentType string) (*FileUploadResponse, *Response) { + rq, _ := http.NewRequest("POST", c.ApiUrl+url, bytes.NewReader(data)) + rq.Header.Set("Content-Type", contentType) + rq.Close = true + + if len(c.AuthToken) > 0 { + rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) + } + + if rp, err := c.HttpClient.Do(rq); err != nil { + return nil, &Response{Error: NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)} + } else if rp.StatusCode >= 300 { + return nil, &Response{StatusCode: rp.StatusCode, Error: AppErrorFromJson(rp.Body)} + } else { + defer closeBody(rp) + return FileUploadResponseFromJson(rp.Body), BuildResponse(rp) + } +} + +// CheckStatusOK is a convenience function for checking the standard OK response +// from the web service. +func CheckStatusOK(r *http.Response) bool { + m := MapFromJson(r.Body) + defer closeBody(r) + + if m != nil && m[STATUS] == STATUS_OK { + return true + } + + return false +} + +// Authentication Section + +// LoginById authenticates a user by user id and password. +func (c *Client4) LoginById(id string, password string) (*User, *Response) { + m := make(map[string]string) + m["id"] = id + m["password"] = password + return c.login(m) +} + +// Login authenticates a user by login id, which can be username, email or some sort +// of SSO identifier based on server configuration, and a password. +func (c *Client4) Login(loginId string, password string) (*User, *Response) { + m := make(map[string]string) + m["login_id"] = loginId + m["password"] = password + return c.login(m) +} + +// LoginByLdap authenticates a user by LDAP id and password. +func (c *Client4) LoginByLdap(loginId string, password string) (*User, *Response) { + m := make(map[string]string) + m["login_id"] = loginId + m["password"] = password + m["ldap_only"] = "true" + return c.login(m) +} + +// LoginWithDevice authenticates a user by login id (username, email or some sort +// of SSO identifier based on configuration), password and attaches a device id to +// the session. +func (c *Client4) LoginWithDevice(loginId string, password string, deviceId string) (*User, *Response) { + m := make(map[string]string) + m["login_id"] = loginId + m["password"] = password + m["device_id"] = deviceId + return c.login(m) +} + +func (c *Client4) login(m map[string]string) (*User, *Response) { + if r, err := c.DoApiPost("/users/login", MapToJson(m)); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + c.AuthToken = r.Header.Get(HEADER_TOKEN) + c.AuthType = HEADER_BEARER + defer closeBody(r) + return UserFromJson(r.Body), BuildResponse(r) + } +} + +// Logout terminates the current user's session. +func (c *Client4) Logout() (bool, *Response) { + if r, err := c.DoApiPost("/users/logout", ""); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + c.AuthToken = "" + c.AuthType = HEADER_BEARER + + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// User Section + +// CreateUser creates a user in the system based on the provided user struct. +func (c *Client4) CreateUser(user *User) (*User, *Response) { + if r, err := c.DoApiPost(c.GetUsersRoute(), user.ToJson()); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return UserFromJson(r.Body), BuildResponse(r) + } +} + +// GetUser returns a user based on the provided user id string. +func (c *Client4) GetUser(userId, etag string) (*User, *Response) { + if r, err := c.DoApiGet(c.GetUserRoute(userId), etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return UserFromJson(r.Body), BuildResponse(r) + } +} + +// GetUserByUsername returns a user based on the provided user name string. +func (c *Client4) GetUserByUsername(userName, etag string) (*User, *Response) { + if r, err := c.DoApiGet(c.GetUserByUsernameRoute(userName), etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return UserFromJson(r.Body), BuildResponse(r) + } +} + +// GetUserByEmail returns a user based on the provided user email string. +func (c *Client4) GetUserByEmail(email, etag string) (*User, *Response) { + if r, err := c.DoApiGet(c.GetUserByEmailRoute(email), etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return UserFromJson(r.Body), BuildResponse(r) + } +} + +// GetProfileImage gets user's profile image. Must be logged in or be a system administrator. +func (c *Client4) GetProfileImage(userId, etag string) ([]byte, *Response) { + if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/image", etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else if data, err := ioutil.ReadAll(r.Body); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: NewAppError("GetProfileImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)} + } else { + return data, BuildResponse(r) + } +} + +// GetUsers returns a page of users on the system. Page counting starts at 0. +func (c *Client4) GetUsers(page int, perPage int, etag string) ([]*User, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return UserListFromJson(r.Body), BuildResponse(r) + } +} + +// GetUsersInTeam returns a page of users on a team. Page counting starts at 0. +func (c *Client4) GetUsersInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) { + query := fmt.Sprintf("?in_team=%v&page=%v&per_page=%v", teamId, page, perPage) + if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return UserListFromJson(r.Body), BuildResponse(r) + } +} + +// GetUsersInChannel returns a page of users on a team. Page counting starts at 0. +func (c *Client4) GetUsersInChannel(channelId string, page int, perPage int, etag string) ([]*User, *Response) { + query := fmt.Sprintf("?in_channel=%v&page=%v&per_page=%v", channelId, page, perPage) + if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return UserListFromJson(r.Body), BuildResponse(r) + } +} + +// GetUsersNotInChannel returns a page of users on a team. Page counting starts at 0. +func (c *Client4) GetUsersNotInChannel(teamId, channelId string, page int, perPage int, etag string) ([]*User, *Response) { + query := fmt.Sprintf("?in_team=%v¬_in_channel=%v&page=%v&per_page=%v", teamId, channelId, page, perPage) + if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return UserListFromJson(r.Body), BuildResponse(r) + } +} + +// GetUsersByIds returns a list of users based on the provided user ids. +func (c *Client4) GetUsersByIds(userIds []string) ([]*User, *Response) { + if r, err := c.DoApiPost(c.GetUsersRoute()+"/ids", ArrayToJson(userIds)); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return UserListFromJson(r.Body), BuildResponse(r) + } +} + +// UpdateUser updates a user in the system based on the provided user struct. +func (c *Client4) UpdateUser(user *User) (*User, *Response) { + if r, err := c.DoApiPut(c.GetUserRoute(user.Id), user.ToJson()); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return UserFromJson(r.Body), BuildResponse(r) + } +} + +// PatchUser partially updates a user in the system. Any missing fields are not updated. +func (c *Client4) PatchUser(userId string, patch *UserPatch) (*User, *Response) { + if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/patch", patch.ToJson()); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return UserFromJson(r.Body), BuildResponse(r) + } +} + +// UpdateUserPassword updates a user's password. Must be logged in as the user or be a system administrator. +func (c *Client4) UpdateUserPassword(userId, currentPassword, newPassword string) (bool, *Response) { + requestBody := map[string]string{"current_password": currentPassword, "new_password": newPassword} + if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/password", MapToJson(requestBody)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// UpdateUserRoles updates a user's roles in the system. A user can have "system_user" and "system_admin" roles. +func (c *Client4) UpdateUserRoles(userId, roles string) (bool, *Response) { + requestBody := map[string]string{"roles": roles} + if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/roles", MapToJson(requestBody)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// DeleteUser deactivates a user in the system based on the provided user id string. +func (c *Client4) DeleteUser(userId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetUserRoute(userId)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// SendPasswordResetEmail will send a link for password resetting to a user with the +// provided email. +func (c *Client4) SendPasswordResetEmail(email string) (bool, *Response) { + requestBody := map[string]string{"email": email} + if r, err := c.DoApiPost(c.GetUsersRoute()+"/password/reset/send", MapToJson(requestBody)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// ResetPassword uses a recovery code to update reset a user's password. +func (c *Client4) ResetPassword(code, newPassword string) (bool, *Response) { + requestBody := map[string]string{"code": code, "new_password": newPassword} + if r, err := c.DoApiPost(c.GetUsersRoute()+"/password/reset", MapToJson(requestBody)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// GetSessions returns a list of sessions based on the provided user id string. +func (c *Client4) GetSessions(userId, etag string) ([]*Session, *Response) { + if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/sessions", etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return SessionsFromJson(r.Body), BuildResponse(r) + } +} + +// RevokeSession revokes a user session based on the provided user id and session id strings. +func (c *Client4) RevokeSession(userId, sessionId string) (bool, *Response) { + requestBody := map[string]string{"session_id": sessionId} + if r, err := c.DoApiPost(c.GetUserRoute(userId)+"/sessions/revoke", MapToJson(requestBody)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// 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) { + optional := "" + if teamIdToExclude != "" { + optional += fmt.Sprintf("?exclude_team=%s", url.QueryEscape(teamIdToExclude)) + } + + if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams/unread"+optional, ""); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return TeamsUnreadFromJson(r.Body), BuildResponse(r) + } +} + +// GetAudits returns a list of audit based on the provided user id string. +func (c *Client4) GetAudits(userId string, page int, perPage int, etag string) (Audits, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/audits"+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return AuditsFromJson(r.Body), BuildResponse(r) + } +} + +// Verify user email user id and hash strings. +func (c *Client4) VerifyUserEmail(userId, hashId string) (bool, *Response) { + requestBody := map[string]string{"uid": userId, "hid": hashId} + if r, err := c.DoApiPost(c.GetUserRoute(userId)+"/email/verify", MapToJson(requestBody)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// SetProfileImage sets profile image of the user +func (c *Client4) SetProfileImage(userId string, data []byte) (bool, *Response) { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + if part, err := writer.CreateFormFile("image", "profile.png"); err != nil { + return false, &Response{Error: NewAppError("SetProfileImage", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} + } else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { + return false, &Response{Error: NewAppError("SetProfileImage", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + if err := writer.Close(); err != nil { + return false, &Response{Error: NewAppError("SetProfileImage", "model.client.set_profile_user.writer.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetUserRoute(userId)+"/image", bytes.NewReader(body.Bytes())) + rq.Header.Set("Content-Type", writer.FormDataContentType()) + rq.Close = true + + if len(c.AuthToken) > 0 { + rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) + } + + if rp, err := c.HttpClient.Do(rq); err != nil { + // set to http.StatusForbidden(403) + return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetUserRoute(userId)+"/image", "model.client.connecting.app_error", nil, err.Error(), 403)} + } else if rp.StatusCode >= 300 { + return false, &Response{StatusCode: rp.StatusCode, Error: AppErrorFromJson(rp.Body)} + } else { + defer closeBody(rp) + return CheckStatusOK(rp), BuildResponse(rp) + } +} + +// Team Section + +// CreateTeam creates a team in the system based on the provided team struct. +func (c *Client4) CreateTeam(team *Team) (*Team, *Response) { + if r, err := c.DoApiPost(c.GetTeamsRoute(), team.ToJson()); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return TeamFromJson(r.Body), BuildResponse(r) + } +} + +// GetTeam returns a team based on the provided team id string. +func (c *Client4) GetTeam(teamId, etag string) (*Team, *Response) { + if r, err := c.DoApiGet(c.GetTeamRoute(teamId), etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return TeamFromJson(r.Body), BuildResponse(r) + } +} + +// GetAllTeams returns all teams based on permissions. +func (c *Client4) GetAllTeams(etag string, page int, perPage int) ([]*Team, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetTeamsRoute()+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + 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) { + if r, err := c.DoApiGet(c.GetTeamByNameRoute(name), etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return TeamFromJson(r.Body), BuildResponse(r) + } +} + +// GetTeamsForUser returns a list of teams a user is on. Must be logged in as the user +// or be a system administrator. +func (c *Client4) GetTeamsForUser(userId, etag string) ([]*Team, *Response) { + if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams", etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return TeamListFromJson(r.Body), BuildResponse(r) + } +} + +// GetTeamMember returns a team member based on the provided team and user id strings. +func (c *Client4) GetTeamMember(teamId, userId, etag string) (*TeamMember, *Response) { + if r, err := c.DoApiGet(c.GetTeamMemberRoute(teamId, userId), etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return TeamMemberFromJson(r.Body), BuildResponse(r) + } +} + +// UpdateTeamMemberRoles will update the roles on a team for a user +func (c *Client4) UpdateTeamMemberRoles(teamId, userId, newRoles string) (bool, *Response) { + requestBody := map[string]string{"roles": newRoles} + if r, err := c.DoApiPut(c.GetTeamMemberRoute(teamId, userId)+"/roles", MapToJson(requestBody)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// GetTeamMembers returns team members based on the provided team id string. +func (c *Client4) GetTeamMembers(teamId string, page int, perPage int, etag string) ([]*TeamMember, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetTeamMembersRoute(teamId)+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return TeamMembersFromJson(r.Body), BuildResponse(r) + } +} + +// GetTeamStats returns a team stats based on the team id string. +// Must be authenticated. +func (c *Client4) GetTeamStats(teamId, etag string) (*TeamStats, *Response) { + if r, err := c.DoApiGet(c.GetTeamStatsRoute(teamId), etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return TeamStatsFromJson(r.Body), BuildResponse(r) + } +} + +// Channel Section + +// CreateChannel creates a channel based on the provided channel struct. +func (c *Client4) CreateChannel(channel *Channel) (*Channel, *Response) { + if r, err := c.DoApiPost(c.GetChannelsRoute(), channel.ToJson()); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return ChannelFromJson(r.Body), BuildResponse(r) + } +} + +// CreateDirectChannel creates a direct message channel based on the two user +// ids provided. +func (c *Client4) CreateDirectChannel(userId1, userId2 string) (*Channel, *Response) { + requestBody := []string{userId1, userId2} + if r, err := c.DoApiPost(c.GetChannelsRoute()+"/direct", ArrayToJson(requestBody)); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return ChannelFromJson(r.Body), BuildResponse(r) + } +} + +// GetChannel returns a channel based on the provided channel id string. +func (c *Client4) GetChannel(channelId, etag string) (*Channel, *Response) { + if r, err := c.DoApiGet(c.GetChannelRoute(channelId), etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return ChannelFromJson(r.Body), BuildResponse(r) + } +} + +// GetChannelByName returns a channel based on the provided channel name and team id strings. +func (c *Client4) GetChannelByName(channelName, teamId string, etag string) (*Channel, *Response) { + if r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId), etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return ChannelFromJson(r.Body), BuildResponse(r) + } +} + +// GetChannelByNameForTeamName returns a channel based on the provided channel name and team name strings. +func (c *Client4) GetChannelByNameForTeamName(channelName, teamName string, etag string) (*Channel, *Response) { + if r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName), etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return ChannelFromJson(r.Body), BuildResponse(r) + } +} + +// GetChannelMembers gets a page of channel members. +func (c *Client4) GetChannelMembers(channelId string, page, perPage int, etag string) (*ChannelMembers, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetChannelMembersRoute(channelId)+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return ChannelMembersFromJson(r.Body), BuildResponse(r) + } +} + +// GetChannelMember gets a channel member. +func (c *Client4) GetChannelMember(channelId, userId, etag string) (*ChannelMember, *Response) { + if r, err := c.DoApiGet(c.GetChannelMemberRoute(channelId, userId), etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return ChannelMemberFromJson(r.Body), BuildResponse(r) + } +} + +// GetChannelMembersForUser gets all the channel members for a user on a team. +func (c *Client4) GetChannelMembersForUser(userId, teamId, etag string) (*ChannelMembers, *Response) { + if r, err := c.DoApiGet(fmt.Sprintf(c.GetUserRoute(userId)+"/teams/%v/channels/members", teamId), etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return ChannelMembersFromJson(r.Body), BuildResponse(r) + } +} + +// ViewChannel performs a view action for a user. Synonymous with switching channels or marking channels as read by a user. +func (c *Client4) ViewChannel(userId string, view *ChannelView) (bool, *Response) { + url := fmt.Sprintf(c.GetChannelsRoute()+"/members/%v/view", userId) + if r, err := c.DoApiPost(url, view.ToJson()); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// UpdateChannelRoles will update the roles on a channel for a user. +func (c *Client4) UpdateChannelRoles(channelId, userId, roles string) (bool, *Response) { + requestBody := map[string]string{"roles": roles} + if r, err := c.DoApiPut(c.GetChannelMemberRoute(channelId, userId)+"/roles", MapToJson(requestBody)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// RemoveUserFromChannel will delete the channel member object for a user, effectively removing the user from a channel. +func (c *Client4) RemoveUserFromChannel(channelId, userId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetChannelMemberRoute(channelId, userId)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// Post Section + +// CreatePost creates a post based on the provided post struct. +func (c *Client4) CreatePost(post *Post) (*Post, *Response) { + if r, err := c.DoApiPost(c.GetPostsRoute(), post.ToJson()); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return PostFromJson(r.Body), BuildResponse(r) + } +} + +// UpdatePost updates a post based on the provided post struct. +func (c *Client4) UpdatePost(postId string, post *Post) (*Post, *Response) { + if r, err := c.DoApiPut(c.GetPostRoute(postId), post.ToJson()); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return PostFromJson(r.Body), BuildResponse(r) + } +} + +// GetPost gets a single post. +func (c *Client4) GetPost(postId string, etag string) (*Post, *Response) { + if r, err := c.DoApiGet(c.GetPostRoute(postId), etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return PostFromJson(r.Body), BuildResponse(r) + } +} + +// DeletePost deletes a post from the provided post id string. +func (c *Client4) DeletePost(postId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetPostRoute(postId)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// GetPostThread gets a post with all the other posts in the same thread. +func (c *Client4) GetPostThread(postId string, etag string) (*PostList, *Response) { + if r, err := c.DoApiGet(c.GetPostRoute(postId)+"/thread", etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return PostListFromJson(r.Body), BuildResponse(r) + } +} + +// 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) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return PostListFromJson(r.Body), BuildResponse(r) + } +} + +// SearchPosts returns any posts with matching terms string. +func (c *Client4) SearchPosts(teamId string, terms string, isOrSearch bool) (*PostList, *Response) { + requestBody := map[string]string{"terms": terms, "is_or_search": strconv.FormatBool(isOrSearch)} + if r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/posts/search", MapToJson(requestBody)); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return PostListFromJson(r.Body), BuildResponse(r) + } +} + +// File Section + +// UploadFile will upload a file to a channel, to be later attached to a post. +func (c *Client4) UploadFile(data []byte, channelId string, filename string) (*FileUploadResponse, *Response) { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + if part, err := writer.CreateFormFile("files", filename); err != nil { + return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), http.StatusBadRequest)} + } else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { + return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + if part, err := writer.CreateFormField("channel_id"); err != nil { + return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.channel_id.app_error", nil, err.Error(), http.StatusBadRequest)} + } else if _, err = io.Copy(part, strings.NewReader(channelId)); err != nil { + return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.channel_id.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + if err := writer.Close(); err != nil { + return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.writer.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + return c.DoUploadFile(c.GetFilesRoute(), body.Bytes(), writer.FormDataContentType()) +} + +// GetFile gets the bytes for a file by id. +func (c *Client4) GetFile(fileId string) ([]byte, *Response) { + if r, err := c.DoApiGet(c.GetFileRoute(fileId), ""); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else if data, err := ioutil.ReadAll(r.Body); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: NewAppError("GetFile", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)} + } else { + return data, BuildResponse(r) + } +} + +// GetFileThumbnail gets the bytes for a file by id. +func (c *Client4) GetFileThumbnail(fileId string) ([]byte, *Response) { + if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/thumbnail", ""); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else if data, err := ioutil.ReadAll(r.Body); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: NewAppError("GetFileThumbnail", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)} + } else { + return data, BuildResponse(r) + } +} + +// GetFileInfosForPost gets all the file info objects attached to a post. +func (c *Client4) GetFileInfosForPost(postId string, etag string) ([]*FileInfo, *Response) { + if r, err := c.DoApiGet(c.GetPostRoute(postId)+"/files/info", etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return FileInfosFromJson(r.Body), BuildResponse(r) + } +} + +// General Section + +// GetPing will ping the server and to see if it is up and running. +func (c *Client4) GetPing() (bool, *Response) { + if r, err := c.DoApiGet(c.GetSystemRoute()+"/ping", ""); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// Webhooks Section + +// CreateIncomingWebhook creates an incoming webhook for a channel. +func (c *Client4) CreateIncomingWebhook(hook *IncomingWebhook) (*IncomingWebhook, *Response) { + if r, err := c.DoApiPost(c.GetIncomingWebhooksRoute(), hook.ToJson()); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return IncomingWebhookFromJson(r.Body), BuildResponse(r) + } +} + +// GetIncomingWebhooks returns a page of incoming webhooks on the system. Page counting starts at 0. +func (c *Client4) GetIncomingWebhooks(page int, perPage int, etag string) ([]*IncomingWebhook, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetIncomingWebhooksRoute()+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return IncomingWebhookListFromJson(r.Body), BuildResponse(r) + } +} + +// GetIncomingWebhooksForTeam returns a page of incoming webhooks for a team. Page counting starts at 0. +func (c *Client4) GetIncomingWebhooksForTeam(teamId string, page int, perPage int, etag string) ([]*IncomingWebhook, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v&team_id=%v", page, perPage, teamId) + if r, err := c.DoApiGet(c.GetIncomingWebhooksRoute()+query, etag); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return IncomingWebhookListFromJson(r.Body), BuildResponse(r) + } +} + +// Preferences Section + +// GetPreferences returns the user's preferences +func (c *Client4) GetPreferences(userId string) (Preferences, *Response) { + if r, err := c.DoApiGet(c.GetPreferencesRoute(userId), ""); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + preferences, _ := PreferencesFromJson(r.Body) + defer closeBody(r) + return preferences, BuildResponse(r) + } +} + +// UpdatePreferences saves the user's preferences +func (c *Client4) UpdatePreferences(userId string, preferences *Preferences) (bool, *Response) { + if r, err := c.DoApiPut(c.GetPreferencesRoute(userId), preferences.ToJson()); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return true, BuildResponse(r) + } +} + +// DeletePreferences deletes the user's preferences +func (c *Client4) DeletePreferences(userId string, preferences *Preferences) (bool, *Response) { + if r, err := c.DoApiPost(c.GetPreferencesRoute(userId)+"/delete", preferences.ToJson()); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return true, BuildResponse(r) + } +} + +// GetPreferencesByCategory returns the user's preferences from the provided category string +func (c *Client4) GetPreferencesByCategory(userId string, category string) (Preferences, *Response) { + url := fmt.Sprintf(c.GetPreferencesRoute(userId)+"/%s", category) + if r, err := c.DoApiGet(url, ""); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + preferences, _ := PreferencesFromJson(r.Body) + defer closeBody(r) + return preferences, BuildResponse(r) + } +} + +// GetPreferenceByCategoryAndName returns the user's preferences from the provided category and preference name string +func (c *Client4) GetPreferenceByCategoryAndName(userId string, category string, preferenceName string) (*Preference, *Response) { + url := fmt.Sprintf(c.GetPreferencesRoute(userId)+"/%s/name/%v", category, preferenceName) + if r, err := c.DoApiGet(url, ""); err != nil { + return nil, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return PreferenceFromJson(r.Body), BuildResponse(r) + } +} diff --git a/vendor/github.com/mattermost/platform/model/command_response.go b/vendor/github.com/mattermost/platform/model/command_response.go index bbb70418..f6977235 100644 --- a/vendor/github.com/mattermost/platform/model/command_response.go +++ b/vendor/github.com/mattermost/platform/model/command_response.go @@ -5,6 +5,7 @@ package model import ( "encoding/json" + "fmt" "io" ) @@ -14,12 +15,12 @@ const ( ) type CommandResponse struct { - ResponseType string `json:"response_type"` - Text string `json:"text"` - Username string `json:"username"` - IconURL string `json:"icon_url"` - GotoLocation string `json:"goto_location"` - Attachments interface{} `json:"attachments"` + ResponseType string `json:"response_type"` + Text string `json:"text"` + Username string `json:"username"` + IconURL string `json:"icon_url"` + GotoLocation string `json:"goto_location"` + Attachments []*SlackAttachment `json:"attachments"` } func (o *CommandResponse) ToJson() string { @@ -34,10 +35,19 @@ func (o *CommandResponse) ToJson() string { func CommandResponseFromJson(data io.Reader) *CommandResponse { decoder := json.NewDecoder(data) var o CommandResponse - err := decoder.Decode(&o) - if err == nil { - return &o - } else { + + if err := decoder.Decode(&o); err != nil { return nil } + + // Ensure attachment fields are stored as strings + for _, attachment := range o.Attachments { + for _, field := range attachment.Fields { + if field.Value != nil { + field.Value = fmt.Sprintf("%v", field.Value) + } + } + } + + return &o } diff --git a/vendor/github.com/mattermost/platform/model/config.go b/vendor/github.com/mattermost/platform/model/config.go index 0134e1a3..86693b57 100644 --- a/vendor/github.com/mattermost/platform/model/config.go +++ b/vendor/github.com/mattermost/platform/model/config.go @@ -49,49 +49,109 @@ const ( RESTRICT_EMOJI_CREATION_ADMIN = "admin" RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN = "system_admin" + PERMISSIONS_DELETE_POST_ALL = "all" + PERMISSIONS_DELETE_POST_TEAM_ADMIN = "team_admin" + PERMISSIONS_DELETE_POST_SYSTEM_ADMIN = "system_admin" + + ALLOW_EDIT_POST_ALWAYS = "always" + ALLOW_EDIT_POST_NEVER = "never" + ALLOW_EDIT_POST_TIME_LIMIT = "time_limit" + EMAIL_BATCHING_BUFFER_SIZE = 256 EMAIL_BATCHING_INTERVAL = 30 SITENAME_MAX_LENGTH = 30 + + SERVICE_SETTINGS_DEFAULT_SITE_URL = "" + SERVICE_SETTINGS_DEFAULT_TLS_CERT_FILE = "" + SERVICE_SETTINGS_DEFAULT_TLS_KEY_FILE = "" + SERVICE_SETTINGS_DEFAULT_READ_TIMEOUT = 300 + SERVICE_SETTINGS_DEFAULT_WRITE_TIMEOUT = 300 + SERVICE_SETTINGS_DEFAULT_ALLOW_CORS_FROM = "" + + TEAM_SETTINGS_DEFAULT_CUSTOM_BRAND_TEXT = "" + TEAM_SETTINGS_DEFAULT_CUSTOM_DESCRIPTION_TEXT = "" + TEAM_SETTINGS_DEFAULT_USER_STATUS_AWAY_TIMEOUT = 300 + + EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION = "" + + SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK = "https://about.mattermost.com/default-terms/" + SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK = "https://about.mattermost.com/default-privacy-policy/" + SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK = "https://about.mattermost.com/default-about/" + SUPPORT_SETTINGS_DEFAULT_HELP_LINK = "https://about.mattermost.com/default-help/" + SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK = "https://about.mattermost.com/default-report-a-problem/" + SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL = "feedback@mattermost.com" + + LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = "" + LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = "" + LDAP_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE = "" + LDAP_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE = "" + LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE = "" + LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE = "" + LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE = "" + LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME = "" + + SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = "" + SAML_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = "" + SAML_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE = "" + SAML_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE = "" + SAML_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE = "" + SAML_SETTINGS_DEFAULT_LOCALE_ATTRIBUTE = "" + SAML_SETTINGS_DEFAULT_POSITION_ATTRIBUTE = "" + + NATIVEAPP_SETTINGS_DEFAULT_APP_DOWNLOAD_LINK = "https://about.mattermost.com/downloads/" + NATIVEAPP_SETTINGS_DEFAULT_ANDROID_APP_DOWNLOAD_LINK = "https://about.mattermost.com/mattermost-android-app/" + NATIVEAPP_SETTINGS_DEFAULT_IOS_APP_DOWNLOAD_LINK = "https://about.mattermost.com/mattermost-ios-app/" + + WEBRTC_SETTINGS_DEFAULT_STUN_URI = "" + WEBRTC_SETTINGS_DEFAULT_TURN_URI = "" + + ANALYTICS_SETTINGS_DEFAULT_MAX_USERS_FOR_STATISTICS = 2500 ) type ServiceSettings struct { - SiteURL *string - ListenAddress string - ConnectionSecurity *string - TLSCertFile *string - TLSKeyFile *string - UseLetsEncrypt *bool - LetsEncryptCertificateCacheFile *string - Forward80To443 *bool - ReadTimeout *int - WriteTimeout *int - MaximumLoginAttempts int - SegmentDeveloperKey string - GoogleDeveloperKey string - EnableOAuthServiceProvider bool - EnableIncomingWebhooks bool - EnableOutgoingWebhooks bool - EnableCommands *bool - EnableOnlyAdminIntegrations *bool - EnablePostUsernameOverride bool - EnablePostIconOverride bool - EnableTesting bool - EnableDeveloper *bool - EnableSecurityFixAlert *bool - EnableInsecureOutgoingConnections *bool - EnableMultifactorAuthentication *bool - EnforceMultifactorAuthentication *bool - AllowCorsFrom *string - SessionLengthWebInDays *int - SessionLengthMobileInDays *int - SessionLengthSSOInDays *int - SessionCacheInMinutes *int - WebsocketSecurePort *int - WebsocketPort *int - WebserverMode *string - EnableCustomEmoji *bool - RestrictCustomEmojiCreation *string + SiteURL *string + ListenAddress string + ConnectionSecurity *string + TLSCertFile *string + TLSKeyFile *string + UseLetsEncrypt *bool + LetsEncryptCertificateCacheFile *string + Forward80To443 *bool + ReadTimeout *int + WriteTimeout *int + MaximumLoginAttempts int + GoogleDeveloperKey string + EnableOAuthServiceProvider bool + EnableIncomingWebhooks bool + EnableOutgoingWebhooks bool + EnableCommands *bool + EnableOnlyAdminIntegrations *bool + EnablePostUsernameOverride bool + EnablePostIconOverride bool + EnableLinkPreviews *bool + EnableTesting bool + EnableDeveloper *bool + EnableSecurityFixAlert *bool + EnableInsecureOutgoingConnections *bool + EnableMultifactorAuthentication *bool + EnforceMultifactorAuthentication *bool + AllowCorsFrom *string + SessionLengthWebInDays *int + SessionLengthMobileInDays *int + SessionLengthSSOInDays *int + SessionCacheInMinutes *int + WebsocketSecurePort *int + WebsocketPort *int + WebserverMode *string + EnableCustomEmoji *bool + RestrictCustomEmojiCreation *string + RestrictPostDelete *string + AllowEditPost *string + PostEditTimeLimit *int + TimeBetweenUserTypingUpdatesMilliseconds *int64 + EnableUserTypingMessages *bool + ClusterLogTimeoutMilliseconds *int } type ClusterSettings struct { @@ -433,7 +493,12 @@ func (o *Config) SetDefaults() { if o.ServiceSettings.SiteURL == nil { o.ServiceSettings.SiteURL = new(string) - *o.ServiceSettings.SiteURL = "" + *o.ServiceSettings.SiteURL = SERVICE_SETTINGS_DEFAULT_SITE_URL + } + + if o.ServiceSettings.EnableLinkPreviews == nil { + o.ServiceSettings.EnableLinkPreviews = new(bool) + *o.ServiceSettings.EnableLinkPreviews = false } if o.ServiceSettings.EnableDeveloper == nil { @@ -493,12 +558,12 @@ func (o *Config) SetDefaults() { if o.TeamSettings.CustomBrandText == nil { o.TeamSettings.CustomBrandText = new(string) - *o.TeamSettings.CustomBrandText = "" + *o.TeamSettings.CustomBrandText = TEAM_SETTINGS_DEFAULT_CUSTOM_BRAND_TEXT } if o.TeamSettings.CustomDescriptionText == nil { o.TeamSettings.CustomDescriptionText = new(string) - *o.TeamSettings.CustomDescriptionText = "" + *o.TeamSettings.CustomDescriptionText = TEAM_SETTINGS_DEFAULT_CUSTOM_DESCRIPTION_TEXT } if o.TeamSettings.EnableOpenServer == nil { @@ -552,7 +617,7 @@ func (o *Config) SetDefaults() { if o.TeamSettings.UserStatusAwayTimeout == nil { o.TeamSettings.UserStatusAwayTimeout = new(int64) - *o.TeamSettings.UserStatusAwayTimeout = 300 + *o.TeamSettings.UserStatusAwayTimeout = TEAM_SETTINGS_DEFAULT_USER_STATUS_AWAY_TIMEOUT } if o.TeamSettings.MaxChannelsPerTeam == nil { @@ -597,7 +662,7 @@ func (o *Config) SetDefaults() { if o.EmailSettings.FeedbackOrganization == nil { o.EmailSettings.FeedbackOrganization = new(string) - *o.EmailSettings.FeedbackOrganization = "" + *o.EmailSettings.FeedbackOrganization = EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION } if o.EmailSettings.EnableEmailBatching == nil { @@ -621,7 +686,7 @@ func (o *Config) SetDefaults() { if o.SupportSettings.TermsOfServiceLink == nil { o.SupportSettings.TermsOfServiceLink = new(string) - *o.SupportSettings.TermsOfServiceLink = "https://about.mattermost.com/default-terms/" + *o.SupportSettings.TermsOfServiceLink = SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK } if !IsSafeLink(o.SupportSettings.PrivacyPolicyLink) { @@ -630,7 +695,7 @@ func (o *Config) SetDefaults() { if o.SupportSettings.PrivacyPolicyLink == nil { o.SupportSettings.PrivacyPolicyLink = new(string) - *o.SupportSettings.PrivacyPolicyLink = "" + *o.SupportSettings.PrivacyPolicyLink = SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK } if !IsSafeLink(o.SupportSettings.AboutLink) { @@ -639,7 +704,7 @@ func (o *Config) SetDefaults() { if o.SupportSettings.AboutLink == nil { o.SupportSettings.AboutLink = new(string) - *o.SupportSettings.AboutLink = "" + *o.SupportSettings.AboutLink = SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK } if !IsSafeLink(o.SupportSettings.HelpLink) { @@ -648,7 +713,7 @@ func (o *Config) SetDefaults() { if o.SupportSettings.HelpLink == nil { o.SupportSettings.HelpLink = new(string) - *o.SupportSettings.HelpLink = "" + *o.SupportSettings.HelpLink = SUPPORT_SETTINGS_DEFAULT_HELP_LINK } if !IsSafeLink(o.SupportSettings.ReportAProblemLink) { @@ -657,12 +722,12 @@ func (o *Config) SetDefaults() { if o.SupportSettings.ReportAProblemLink == nil { o.SupportSettings.ReportAProblemLink = new(string) - *o.SupportSettings.ReportAProblemLink = "" + *o.SupportSettings.ReportAProblemLink = SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK } if o.SupportSettings.SupportEmail == nil { o.SupportSettings.SupportEmail = new(string) - *o.SupportSettings.SupportEmail = "feedback@mattermost.com" + *o.SupportSettings.SupportEmail = SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL } if o.LdapSettings.Enable == nil { @@ -707,37 +772,37 @@ func (o *Config) SetDefaults() { if o.LdapSettings.FirstNameAttribute == nil { o.LdapSettings.FirstNameAttribute = new(string) - *o.LdapSettings.FirstNameAttribute = "" + *o.LdapSettings.FirstNameAttribute = LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE } if o.LdapSettings.LastNameAttribute == nil { o.LdapSettings.LastNameAttribute = new(string) - *o.LdapSettings.LastNameAttribute = "" + *o.LdapSettings.LastNameAttribute = LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE } if o.LdapSettings.EmailAttribute == nil { o.LdapSettings.EmailAttribute = new(string) - *o.LdapSettings.EmailAttribute = "" + *o.LdapSettings.EmailAttribute = LDAP_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE } if o.LdapSettings.UsernameAttribute == nil { o.LdapSettings.UsernameAttribute = new(string) - *o.LdapSettings.UsernameAttribute = "" + *o.LdapSettings.UsernameAttribute = LDAP_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE } if o.LdapSettings.NicknameAttribute == nil { o.LdapSettings.NicknameAttribute = new(string) - *o.LdapSettings.NicknameAttribute = "" + *o.LdapSettings.NicknameAttribute = LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE } if o.LdapSettings.IdAttribute == nil { o.LdapSettings.IdAttribute = new(string) - *o.LdapSettings.IdAttribute = "" + *o.LdapSettings.IdAttribute = LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE } if o.LdapSettings.PositionAttribute == nil { o.LdapSettings.PositionAttribute = new(string) - *o.LdapSettings.PositionAttribute = "" + *o.LdapSettings.PositionAttribute = LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE } if o.LdapSettings.SyncIntervalMinutes == nil { @@ -762,7 +827,7 @@ func (o *Config) SetDefaults() { if o.LdapSettings.LoginFieldName == nil { o.LdapSettings.LoginFieldName = new(string) - *o.LdapSettings.LoginFieldName = "" + *o.LdapSettings.LoginFieldName = LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME } if o.ServiceSettings.SessionLengthWebInDays == nil { @@ -807,7 +872,7 @@ func (o *Config) SetDefaults() { if o.ServiceSettings.AllowCorsFrom == nil { o.ServiceSettings.AllowCorsFrom = new(string) - *o.ServiceSettings.AllowCorsFrom = "" + *o.ServiceSettings.AllowCorsFrom = SERVICE_SETTINGS_DEFAULT_ALLOW_CORS_FROM } if o.ServiceSettings.WebserverMode == nil { @@ -827,6 +892,21 @@ func (o *Config) SetDefaults() { *o.ServiceSettings.RestrictCustomEmojiCreation = RESTRICT_EMOJI_CREATION_ALL } + if o.ServiceSettings.RestrictPostDelete == nil { + o.ServiceSettings.RestrictPostDelete = new(string) + *o.ServiceSettings.RestrictPostDelete = PERMISSIONS_DELETE_POST_ALL + } + + if o.ServiceSettings.AllowEditPost == nil { + o.ServiceSettings.AllowEditPost = new(string) + *o.ServiceSettings.AllowEditPost = ALLOW_EDIT_POST_ALWAYS + } + + if o.ServiceSettings.PostEditTimeLimit == nil { + o.ServiceSettings.PostEditTimeLimit = new(int) + *o.ServiceSettings.PostEditTimeLimit = 300 + } + if o.ClusterSettings.InterNodeListenAddress == nil { o.ClusterSettings.InterNodeListenAddress = new(string) *o.ClusterSettings.InterNodeListenAddress = ":8075" @@ -853,7 +933,7 @@ func (o *Config) SetDefaults() { if o.AnalyticsSettings.MaxUsersForStatistics == nil { o.AnalyticsSettings.MaxUsersForStatistics = new(int) - *o.AnalyticsSettings.MaxUsersForStatistics = 2500 + *o.AnalyticsSettings.MaxUsersForStatistics = ANALYTICS_SETTINGS_DEFAULT_MAX_USERS_FOR_STATISTICS } if o.ComplianceSettings.Enable == nil { @@ -943,52 +1023,52 @@ func (o *Config) SetDefaults() { if o.SamlSettings.FirstNameAttribute == nil { o.SamlSettings.FirstNameAttribute = new(string) - *o.SamlSettings.FirstNameAttribute = "" + *o.SamlSettings.FirstNameAttribute = SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE } if o.SamlSettings.LastNameAttribute == nil { o.SamlSettings.LastNameAttribute = new(string) - *o.SamlSettings.LastNameAttribute = "" + *o.SamlSettings.LastNameAttribute = SAML_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE } if o.SamlSettings.EmailAttribute == nil { o.SamlSettings.EmailAttribute = new(string) - *o.SamlSettings.EmailAttribute = "" + *o.SamlSettings.EmailAttribute = SAML_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE } if o.SamlSettings.UsernameAttribute == nil { o.SamlSettings.UsernameAttribute = new(string) - *o.SamlSettings.UsernameAttribute = "" + *o.SamlSettings.UsernameAttribute = SAML_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE } if o.SamlSettings.NicknameAttribute == nil { o.SamlSettings.NicknameAttribute = new(string) - *o.SamlSettings.NicknameAttribute = "" + *o.SamlSettings.NicknameAttribute = SAML_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE } if o.SamlSettings.PositionAttribute == nil { o.SamlSettings.PositionAttribute = new(string) - *o.SamlSettings.PositionAttribute = "" + *o.SamlSettings.PositionAttribute = SAML_SETTINGS_DEFAULT_POSITION_ATTRIBUTE } if o.SamlSettings.LocaleAttribute == nil { o.SamlSettings.LocaleAttribute = new(string) - *o.SamlSettings.LocaleAttribute = "" + *o.SamlSettings.LocaleAttribute = SAML_SETTINGS_DEFAULT_LOCALE_ATTRIBUTE } if o.NativeAppSettings.AppDownloadLink == nil { o.NativeAppSettings.AppDownloadLink = new(string) - *o.NativeAppSettings.AppDownloadLink = "https://about.mattermost.com/downloads/" + *o.NativeAppSettings.AppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_APP_DOWNLOAD_LINK } if o.NativeAppSettings.AndroidAppDownloadLink == nil { o.NativeAppSettings.AndroidAppDownloadLink = new(string) - *o.NativeAppSettings.AndroidAppDownloadLink = "https://about.mattermost.com/mattermost-android-app/" + *o.NativeAppSettings.AndroidAppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_ANDROID_APP_DOWNLOAD_LINK } if o.NativeAppSettings.IosAppDownloadLink == nil { o.NativeAppSettings.IosAppDownloadLink = new(string) - *o.NativeAppSettings.IosAppDownloadLink = "https://about.mattermost.com/mattermost-ios-app/" + *o.NativeAppSettings.IosAppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_IOS_APP_DOWNLOAD_LINK } if o.RateLimitSettings.Enable == nil { @@ -1008,12 +1088,12 @@ func (o *Config) SetDefaults() { if o.ServiceSettings.TLSKeyFile == nil { o.ServiceSettings.TLSKeyFile = new(string) - *o.ServiceSettings.TLSKeyFile = "" + *o.ServiceSettings.TLSKeyFile = SERVICE_SETTINGS_DEFAULT_TLS_KEY_FILE } if o.ServiceSettings.TLSCertFile == nil { o.ServiceSettings.TLSCertFile = new(string) - *o.ServiceSettings.TLSCertFile = "" + *o.ServiceSettings.TLSCertFile = SERVICE_SETTINGS_DEFAULT_TLS_CERT_FILE } if o.ServiceSettings.UseLetsEncrypt == nil { @@ -1028,12 +1108,12 @@ func (o *Config) SetDefaults() { if o.ServiceSettings.ReadTimeout == nil { o.ServiceSettings.ReadTimeout = new(int) - *o.ServiceSettings.ReadTimeout = 300 + *o.ServiceSettings.ReadTimeout = SERVICE_SETTINGS_DEFAULT_READ_TIMEOUT } if o.ServiceSettings.WriteTimeout == nil { o.ServiceSettings.WriteTimeout = new(int) - *o.ServiceSettings.WriteTimeout = 300 + *o.ServiceSettings.WriteTimeout = SERVICE_SETTINGS_DEFAULT_WRITE_TIMEOUT } if o.ServiceSettings.Forward80To443 == nil { @@ -1046,6 +1126,21 @@ func (o *Config) SetDefaults() { *o.MetricsSettings.BlockProfileRate = 0 } + if o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds == nil { + o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds = new(int64) + *o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds = 5000 + } + + if o.ServiceSettings.EnableUserTypingMessages == nil { + o.ServiceSettings.EnableUserTypingMessages = new(bool) + *o.ServiceSettings.EnableUserTypingMessages = true + } + + if o.ServiceSettings.ClusterLogTimeoutMilliseconds == nil { + o.ServiceSettings.ClusterLogTimeoutMilliseconds = new(int) + *o.ServiceSettings.ClusterLogTimeoutMilliseconds = 2000 + } + o.defaultWebrtcSettings() } @@ -1277,6 +1372,10 @@ func (o *Config) IsValid() *AppError { return NewLocAppError("Config.IsValid", "model.config.is_valid.write_timeout.app_error", nil, "") } + if *o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds < 1000 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.time_between_user_typing.app_error", nil, "") + } + return nil } @@ -1339,12 +1438,12 @@ func (o *Config) defaultWebrtcSettings() { if o.WebrtcSettings.StunURI == nil { o.WebrtcSettings.StunURI = new(string) - *o.WebrtcSettings.StunURI = "" + *o.WebrtcSettings.StunURI = WEBRTC_SETTINGS_DEFAULT_STUN_URI } if o.WebrtcSettings.TurnURI == nil { o.WebrtcSettings.TurnURI = new(string) - *o.WebrtcSettings.TurnURI = "" + *o.WebrtcSettings.TurnURI = WEBRTC_SETTINGS_DEFAULT_TURN_URI } if o.WebrtcSettings.TurnUsername == nil { diff --git a/vendor/github.com/mattermost/platform/model/file.go b/vendor/github.com/mattermost/platform/model/file.go index c218c424..20f6236d 100644 --- a/vendor/github.com/mattermost/platform/model/file.go +++ b/vendor/github.com/mattermost/platform/model/file.go @@ -8,6 +8,10 @@ import ( "io" ) +const ( + MaxImageSize = 6048 * 4032 // 24 megapixels, roughly 36MB as a raw image +) + var ( IMAGE_EXTENSIONS = [5]string{".jpg", ".jpeg", ".gif", ".bmp", ".png"} IMAGE_MIME_TYPES = map[string]string{".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".bmp": "image/bmp", ".png": "image/png", ".tiff": "image/tiff"} diff --git a/vendor/github.com/mattermost/platform/model/gitlab/gitlab.go b/vendor/github.com/mattermost/platform/model/gitlab/gitlab.go index e189ee68..270d62d8 100644 --- a/vendor/github.com/mattermost/platform/model/gitlab/gitlab.go +++ b/vendor/github.com/mattermost/platform/model/gitlab/gitlab.go @@ -65,6 +65,15 @@ func gitLabUserFromJson(data io.Reader) *GitLabUser { } } +func (glu *GitLabUser) ToJson() string { + b, err := json.Marshal(glu) + if err != nil { + return "" + } else { + return string(b) + } +} + func (glu *GitLabUser) IsValid() bool { if glu.Id == 0 { return false diff --git a/vendor/github.com/mattermost/platform/model/incoming_webhook.go b/vendor/github.com/mattermost/platform/model/incoming_webhook.go index 72fa3e54..2cc26cbc 100644 --- a/vendor/github.com/mattermost/platform/model/incoming_webhook.go +++ b/vendor/github.com/mattermost/platform/model/incoming_webhook.go @@ -29,13 +29,13 @@ type IncomingWebhook struct { } type IncomingWebhookRequest struct { - Text string `json:"text"` - Username string `json:"username"` - IconURL string `json:"icon_url"` - ChannelName string `json:"channel"` - Props StringInterface `json:"props"` - Attachments interface{} `json:"attachments"` - Type string `json:"type"` + Text string `json:"text"` + Username string `json:"username"` + IconURL string `json:"icon_url"` + ChannelName string `json:"channel"` + Props StringInterface `json:"props"` + Attachments []*SlackAttachment `json:"attachments"` + Type string `json:"type"` } func (o *IncomingWebhook) ToJson() string { @@ -212,31 +212,15 @@ func expandAnnouncement(text string) string { func expandAnnouncements(i *IncomingWebhookRequest) { i.Text = expandAnnouncement(i.Text) - if i.Attachments != nil { - attachments := i.Attachments.([]interface{}) - for _, attachment := range attachments { - a := attachment.(map[string]interface{}) + for _, attachment := range i.Attachments { + attachment.Pretext = expandAnnouncement(attachment.Pretext) + attachment.Text = expandAnnouncement(attachment.Text) + attachment.Title = expandAnnouncement(attachment.Title) - if a["pretext"] != nil { - a["pretext"] = expandAnnouncement(a["pretext"].(string)) - } - - if a["text"] != nil { - a["text"] = expandAnnouncement(a["text"].(string)) - } - - if a["title"] != nil { - a["title"] = expandAnnouncement(a["title"].(string)) - } - - if a["fields"] != nil { - fields := a["fields"].([]interface{}) - for _, field := range fields { - f := field.(map[string]interface{}) - if f["value"] != nil { - f["value"] = expandAnnouncement(fmt.Sprintf("%v", f["value"])) - } - } + for _, field := range attachment.Fields { + if field.Value != nil { + // Ensure the value is set to a string if it is set + field.Value = expandAnnouncement(fmt.Sprintf("%v", field.Value)) } } } diff --git a/vendor/github.com/mattermost/platform/model/job.go b/vendor/github.com/mattermost/platform/model/job.go index 229d5efd..09d74aa0 100644 --- a/vendor/github.com/mattermost/platform/model/job.go +++ b/vendor/github.com/mattermost/platform/model/job.go @@ -14,8 +14,8 @@ type ScheduledTask struct { Name string `json:"name"` Interval time.Duration `json:"interval"` Recurring bool `json:"recurring"` - function TaskFunc `json:",omitempty"` - timer *time.Timer `json:",omitempty"` + function TaskFunc + timer *time.Timer } var tasks = make(map[string]*ScheduledTask) diff --git a/vendor/github.com/mattermost/platform/model/license.go b/vendor/github.com/mattermost/platform/model/license.go index 7115aa1a..09da61eb 100644 --- a/vendor/github.com/mattermost/platform/model/license.go +++ b/vendor/github.com/mattermost/platform/model/license.go @@ -8,6 +8,11 @@ import ( "io" ) +const ( + EXPIRED_LICENSE_ERROR = "api.license.add_license.expired.app_error" + INVALID_LICENSE_ERROR = "api.license.add_license.invalid.app_error" +) + type LicenseRecord struct { Id string `json:"id"` CreateAt int64 `json:"create_at"` diff --git a/vendor/github.com/mattermost/platform/model/post.go b/vendor/github.com/mattermost/platform/model/post.go index 7097e031..a7729e0c 100644 --- a/vendor/github.com/mattermost/platform/model/post.go +++ b/vendor/github.com/mattermost/platform/model/post.go @@ -14,10 +14,15 @@ const ( POST_DEFAULT = "" POST_SLACK_ATTACHMENT = "slack_attachment" POST_SYSTEM_GENERIC = "system_generic" - POST_JOIN_LEAVE = "system_join_leave" - POST_ADD_REMOVE = "system_add_remove" + POST_JOIN_LEAVE = "system_join_leave" // Deprecated, use POST_JOIN_CHANNEL or POST_LEAVE_CHANNEL instead + POST_JOIN_CHANNEL = "system_join_channel" + POST_LEAVE_CHANNEL = "system_leave_channel" + POST_ADD_REMOVE = "system_add_remove" // Deprecated, use POST_ADD_TO_CHANNEL or POST_REMOVE_FROM_CHANNEL instead + POST_ADD_TO_CHANNEL = "system_add_to_channel" + POST_REMOVE_FROM_CHANNEL = "system_remove_from_channel" POST_HEADER_CHANGE = "system_header_change" POST_DISPLAYNAME_CHANGE = "system_displayname_change" + POST_PURPOSE_CHANGE = "system_purpose_change" POST_CHANNEL_DELETED = "system_channel_deleted" POST_EPHEMERAL = "system_ephemeral" POST_FILEIDS_MAX_RUNES = 150 @@ -31,6 +36,7 @@ type Post struct { Id string `json:"id"` CreateAt int64 `json:"create_at"` UpdateAt int64 `json:"update_at"` + EditAt int64 `json:"edit_at"` DeleteAt int64 `json:"delete_at"` UserId string `json:"user_id"` ChannelId string `json:"channel_id"` @@ -119,7 +125,9 @@ func (o *Post) IsValid() *AppError { // should be removed once more message types are supported if !(o.Type == POST_DEFAULT || o.Type == POST_JOIN_LEAVE || o.Type == POST_ADD_REMOVE || - o.Type == POST_SLACK_ATTACHMENT || o.Type == POST_HEADER_CHANGE || + o.Type == POST_JOIN_CHANNEL || o.Type == POST_LEAVE_CHANNEL || + o.Type == POST_REMOVE_FROM_CHANNEL || o.Type == POST_ADD_TO_CHANNEL || + o.Type == POST_SLACK_ATTACHMENT || o.Type == POST_HEADER_CHANGE || o.Type == POST_PURPOSE_CHANGE || o.Type == POST_DISPLAYNAME_CHANGE || o.Type == POST_CHANNEL_DELETED) { return NewLocAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type) } diff --git a/vendor/github.com/mattermost/platform/model/post_list.go b/vendor/github.com/mattermost/platform/model/post_list.go index 4c0f5408..9b56f023 100644 --- a/vendor/github.com/mattermost/platform/model/post_list.go +++ b/vendor/github.com/mattermost/platform/model/post_list.go @@ -13,6 +13,13 @@ type PostList struct { Posts map[string]*Post `json:"posts"` } +func NewPostList() *PostList { + return &PostList{ + Order: make([]string, 0), + Posts: make(map[string]*Post), + } +} + func (o *PostList) ToJson() string { b, err := json.Marshal(o) if err != nil { @@ -72,10 +79,18 @@ func (o *PostList) Etag() string { if v.UpdateAt > t { t = v.UpdateAt id = v.Id + } else if v.UpdateAt == t && v.Id > id { + t = v.UpdateAt + id = v.Id } } - return Etag(id, t) + orderId := "" + if len(o.Order) > 0 { + orderId = o.Order[0] + } + + return Etag(orderId, id, t) } func (o *PostList) IsChannelId(channelId string) bool { diff --git a/vendor/github.com/mattermost/platform/model/push_notification.go b/vendor/github.com/mattermost/platform/model/push_notification.go index 3c010fb7..753495b2 100644 --- a/vendor/github.com/mattermost/platform/model/push_notification.go +++ b/vendor/github.com/mattermost/platform/model/push_notification.go @@ -10,8 +10,10 @@ import ( ) const ( - PUSH_NOTIFY_APPLE = "apple" - PUSH_NOTIFY_ANDROID = "android" + PUSH_NOTIFY_APPLE = "apple" + PUSH_NOTIFY_ANDROID = "android" + PUSH_NOTIFY_APPLE_REACT_NATIVE = "apple_rn" + PUSH_NOTIFY_ANDROID_REACT_NATIVE = "android_rn" PUSH_TYPE_MESSAGE = "message" PUSH_TYPE_CLEAR = "clear" @@ -46,12 +48,12 @@ func (me *PushNotification) ToJson() string { } func (me *PushNotification) SetDeviceIdAndPlatform(deviceId string) { - if strings.HasPrefix(deviceId, PUSH_NOTIFY_APPLE+":") { - me.Platform = PUSH_NOTIFY_APPLE - me.DeviceId = strings.TrimPrefix(deviceId, PUSH_NOTIFY_APPLE+":") - } else if strings.HasPrefix(deviceId, PUSH_NOTIFY_ANDROID+":") { - me.Platform = PUSH_NOTIFY_ANDROID - me.DeviceId = strings.TrimPrefix(deviceId, PUSH_NOTIFY_ANDROID+":") + + index := strings.Index(deviceId, ":") + + if index > -1 { + me.Platform = deviceId[:index] + me.DeviceId = deviceId[index+1:] } } diff --git a/vendor/github.com/mattermost/platform/model/push_response.go b/vendor/github.com/mattermost/platform/model/push_response.go new file mode 100644 index 00000000..0271bc94 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/push_response.go @@ -0,0 +1,57 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +const ( + PUSH_STATUS = "status" + PUSH_STATUS_OK = "OK" + PUSH_STATUS_FAIL = "FAIL" + PUSH_STATUS_REMOVE = "REMOVE" + PUSH_STATUS_ERROR_MSG = "error" +) + +type PushResponse map[string]string + +func NewOkPushResponse() PushResponse { + m := make(map[string]string) + m[PUSH_STATUS] = PUSH_STATUS_OK + return m +} + +func NewRemovePushResponse() PushResponse { + m := make(map[string]string) + m[PUSH_STATUS] = PUSH_STATUS_REMOVE + return m +} + +func NewErrorPushResponse(message string) PushResponse { + m := make(map[string]string) + m[PUSH_STATUS] = PUSH_STATUS_FAIL + m[PUSH_STATUS_ERROR_MSG] = message + return m +} + +func (me *PushResponse) ToJson() string { + if b, err := json.Marshal(me); err != nil { + return "" + } else { + return string(b) + } +} + +func PushResponseFromJson(data io.Reader) PushResponse { + decoder := json.NewDecoder(data) + + var objmap PushResponse + if err := decoder.Decode(&objmap); err != nil { + return make(map[string]string) + } else { + return objmap + } +} diff --git a/vendor/github.com/mattermost/platform/model/session.go b/vendor/github.com/mattermost/platform/model/session.go index a6a753e7..277e910f 100644 --- a/vendor/github.com/mattermost/platform/model/session.go +++ b/vendor/github.com/mattermost/platform/model/session.go @@ -11,7 +11,7 @@ import ( const ( SESSION_COOKIE_TOKEN = "MMAUTHTOKEN" - SESSION_CACHE_SIZE = 25000 + SESSION_CACHE_SIZE = 35000 SESSION_PROP_PLATFORM = "platform" SESSION_PROP_OS = "os" SESSION_PROP_BROWSER = "browser" @@ -111,8 +111,7 @@ func (me *Session) GetTeamByTeamId(teamId string) *TeamMember { } func (me *Session) IsMobileApp() bool { - return len(me.DeviceId) > 0 && - (strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_ANDROID+":")) + return len(me.DeviceId) > 0 } func (me *Session) GetUserRoles() []string { diff --git a/vendor/github.com/mattermost/platform/model/slack_attachment.go b/vendor/github.com/mattermost/platform/model/slack_attachment.go new file mode 100644 index 00000000..e1639f2a --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/slack_attachment.go @@ -0,0 +1,29 @@ +// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +type SlackAttachment struct { + Id int64 `json:"id"` + Fallback string `json:"fallback"` + Color string `json:"color"` + Pretext string `json:"pretext"` + AuthorName string `json:"author_name"` + AuthorLink string `json:"author_link"` + AuthorIcon string `json:"author_icon"` + Title string `json:"title"` + TitleLink string `json:"title_link"` + Text string `json:"text"` + Fields []*SlackAttachmentField `json:"fields"` + ImageURL string `json:"image_url"` + ThumbURL string `json:"thumb_url"` + Footer string `json:"footer"` + FooterIcon string `json:"footer_icon"` + Timestamp interface{} `json:"ts"` // This is either a string or an int64 +} + +type SlackAttachmentField struct { + Title string `json:"title"` + Value interface{} `json:"value"` + Short bool `json:"short"` +} diff --git a/vendor/github.com/mattermost/platform/model/status.go b/vendor/github.com/mattermost/platform/model/status.go index fec3a5f7..fc155788 100644 --- a/vendor/github.com/mattermost/platform/model/status.go +++ b/vendor/github.com/mattermost/platform/model/status.go @@ -12,7 +12,7 @@ const ( STATUS_OFFLINE = "offline" STATUS_AWAY = "away" STATUS_ONLINE = "online" - STATUS_CACHE_SIZE = 25000 + STATUS_CACHE_SIZE = SESSION_CACHE_SIZE STATUS_CHANNEL_TIMEOUT = 20000 // 20 seconds STATUS_MIN_UPDATE_TIME = 120000 // 2 minutes ) diff --git a/vendor/github.com/mattermost/platform/model/team.go b/vendor/github.com/mattermost/platform/model/team.go index 3f05ce83..99444bc5 100644 --- a/vendor/github.com/mattermost/platform/model/team.go +++ b/vendor/github.com/mattermost/platform/model/team.go @@ -7,14 +7,22 @@ import ( "encoding/json" "fmt" "io" + "net/http" "regexp" "strings" "unicode/utf8" ) const ( - TEAM_OPEN = "O" - TEAM_INVITE = "I" + TEAM_OPEN = "O" + TEAM_INVITE = "I" + TEAM_ALLOWED_DOMAINS_MAX_LENGTH = 500 + TEAM_COMPANY_NAME_MAX_LENGTH = 64 + TEAM_DESCRIPTION_MAX_LENGTH = 255 + TEAM_DISPLAY_NAME_MAX_RUNES = 64 + TEAM_EMAIL_MAX_LENGTH = 128 + TEAM_NAME_MAX_LENGTH = 64 + TEAM_NAME_MIN_LENGTH = 2 ) type Team struct { @@ -48,6 +56,14 @@ func InvitesFromJson(data io.Reader) *Invites { } } +func (o *Invites) ToEmailList() []string { + emailList := make([]string, len(o.Invites)) + for _, invite := range o.Invites { + emailList = append(emailList, invite["email"]) + } + return emailList +} + func (o *Invites) ToJson() string { b, err := json.Marshal(o) if err != nil { @@ -97,6 +113,26 @@ func TeamMapFromJson(data io.Reader) map[string]*Team { } } +func TeamListToJson(t []*Team) string { + b, err := json.Marshal(t) + if err != nil { + return "" + } else { + return string(b) + } +} + +func TeamListFromJson(data io.Reader) []*Team { + decoder := json.NewDecoder(data) + var teams []*Team + err := decoder.Decode(&teams) + if err == nil { + return teams + } else { + return nil + } +} + func (o *Team) Etag() string { return Etag(o.Id, o.UpdateAt) } @@ -104,55 +140,55 @@ func (o *Team) Etag() string { func (o *Team) IsValid() *AppError { if len(o.Id) != 26 { - return NewLocAppError("Team.IsValid", "model.team.is_valid.id.app_error", nil, "") + return NewAppError("Team.IsValid", "model.team.is_valid.id.app_error", nil, "", http.StatusBadRequest) } if o.CreateAt == 0 { - return NewLocAppError("Team.IsValid", "model.team.is_valid.create_at.app_error", nil, "id="+o.Id) + return NewAppError("Team.IsValid", "model.team.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) } if o.UpdateAt == 0 { - return NewLocAppError("Team.IsValid", "model.team.is_valid.update_at.app_error", nil, "id="+o.Id) + return NewAppError("Team.IsValid", "model.team.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) } - if len(o.Email) > 128 { - return NewLocAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id) + if len(o.Email) > TEAM_EMAIL_MAX_LENGTH { + 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) { - return NewLocAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id) + return NewAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id, http.StatusBadRequest) } - if utf8.RuneCountInString(o.DisplayName) == 0 || utf8.RuneCountInString(o.DisplayName) > 64 { - return NewLocAppError("Team.IsValid", "model.team.is_valid.name.app_error", nil, "id="+o.Id) + if utf8.RuneCountInString(o.DisplayName) == 0 || utf8.RuneCountInString(o.DisplayName) > TEAM_DISPLAY_NAME_MAX_RUNES { + return NewAppError("Team.IsValid", "model.team.is_valid.name.app_error", nil, "id="+o.Id, http.StatusBadRequest) } - if len(o.Name) > 64 { - return NewLocAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id) + if len(o.Name) > TEAM_NAME_MAX_LENGTH { + return NewAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id, http.StatusBadRequest) } - if len(o.Description) > 255 { - return NewLocAppError("Team.IsValid", "model.team.is_valid.description.app_error", nil, "id="+o.Id) + if len(o.Description) > TEAM_DESCRIPTION_MAX_LENGTH { + return NewAppError("Team.IsValid", "model.team.is_valid.description.app_error", nil, "id="+o.Id, http.StatusBadRequest) } if IsReservedTeamName(o.Name) { - return NewLocAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id) + return NewAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id, http.StatusBadRequest) } if !IsValidTeamName(o.Name) { - return NewLocAppError("Team.IsValid", "model.team.is_valid.characters.app_error", nil, "id="+o.Id) + return NewAppError("Team.IsValid", "model.team.is_valid.characters.app_error", nil, "id="+o.Id, http.StatusBadRequest) } if !(o.Type == TEAM_OPEN || o.Type == TEAM_INVITE) { - return NewLocAppError("Team.IsValid", "model.team.is_valid.type.app_error", nil, "id="+o.Id) + return NewAppError("Team.IsValid", "model.team.is_valid.type.app_error", nil, "id="+o.Id, http.StatusBadRequest) } - if len(o.CompanyName) > 64 { - return NewLocAppError("Team.IsValid", "model.team.is_valid.company.app_error", nil, "id="+o.Id) + if len(o.CompanyName) > TEAM_COMPANY_NAME_MAX_LENGTH { + return NewAppError("Team.IsValid", "model.team.is_valid.company.app_error", nil, "id="+o.Id, http.StatusBadRequest) } - if len(o.AllowedDomains) > 500 { - return NewLocAppError("Team.IsValid", "model.team.is_valid.domains.app_error", nil, "id="+o.Id) + if len(o.AllowedDomains) > TEAM_ALLOWED_DOMAINS_MAX_LENGTH { + return NewAppError("Team.IsValid", "model.team.is_valid.domains.app_error", nil, "id="+o.Id, http.StatusBadRequest) } return nil @@ -193,7 +229,7 @@ func IsValidTeamName(s string) bool { return false } - if len(s) <= 1 { + if len(s) < TEAM_NAME_MIN_LENGTH { return false } diff --git a/vendor/github.com/mattermost/platform/model/user.go b/vendor/github.com/mattermost/platform/model/user.go index 76c3772c..d62ccccd 100644 --- a/vendor/github.com/mattermost/platform/model/user.go +++ b/vendor/github.com/mattermost/platform/model/user.go @@ -7,20 +7,36 @@ import ( "encoding/json" "fmt" "io" + "net/http" "regexp" "strings" + "unicode" "unicode/utf8" "golang.org/x/crypto/bcrypt" ) const ( - USER_NOTIFY_ALL = "all" - USER_NOTIFY_MENTION = "mention" - USER_NOTIFY_NONE = "none" + USER_NOTIFY_ALL = "all" + USER_NOTIFY_MENTION = "mention" + USER_NOTIFY_NONE = "none" + DESKTOP_NOTIFY_PROP = "desktop" + MARK_UNREAD_NOTIFY_PROP = "mark_unread" + PUSH_NOTIFY_PROP = "push" + EMAIL_NOTIFY_PROP = "email" + DEFAULT_LOCALE = "en" USER_AUTH_SERVICE_EMAIL = "email" USER_AUTH_SERVICE_USERNAME = "username" + + USER_EMAIL_MAX_LENGTH = 128 + USER_NICKNAME_MAX_RUNES = 64 + USER_POSITION_MAX_RUNES = 35 + USER_FIRST_NAME_MAX_RUNES = 64 + USER_LAST_NAME_MAX_RUNES = 64 + USER_AUTH_DATA_MAX_LENGTH = 128 + USER_NAME_MAX_LENGTH = 64 + USER_NAME_MIN_LENGTH = 1 ) type User struct { @@ -51,56 +67,68 @@ type User struct { LastActivityAt int64 `db:"-" json:"last_activity_at,omitempty"` } +type UserPatch struct { + Username *string `json:"username"` + Nickname *string `json:"nickname"` + FirstName *string `json:"first_name"` + LastName *string `json:"last_name"` + Position *string `json:"position"` + Email *string `json:"email"` + Props *StringMap `json:"props,omitempty"` + NotifyProps *StringMap `json:"notify_props,omitempty"` + Locale *string `json:"locale"` +} + // IsValid validates the user and returns an error if it isn't configured // correctly. func (u *User) IsValid() *AppError { if len(u.Id) != 26 { - return NewLocAppError("User.IsValid", "model.user.is_valid.id.app_error", nil, "") + return NewAppError("User.IsValid", "model.user.is_valid.id.app_error", nil, "", http.StatusBadRequest) } if u.CreateAt == 0 { - return NewLocAppError("User.IsValid", "model.user.is_valid.create_at.app_error", nil, "user_id="+u.Id) + return NewAppError("User.IsValid", "model.user.is_valid.create_at.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) } if u.UpdateAt == 0 { - return NewLocAppError("User.IsValid", "model.user.is_valid.update_at.app_error", nil, "user_id="+u.Id) + return NewAppError("User.IsValid", "model.user.is_valid.update_at.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) } if !IsValidUsername(u.Username) { - return NewLocAppError("User.IsValid", "model.user.is_valid.username.app_error", nil, "user_id="+u.Id) + return NewAppError("User.IsValid", "model.user.is_valid.username.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) } - if len(u.Email) > 128 || len(u.Email) == 0 { - return NewLocAppError("User.IsValid", "model.user.is_valid.email.app_error", nil, "user_id="+u.Id) + if len(u.Email) > USER_EMAIL_MAX_LENGTH || len(u.Email) == 0 { + return NewAppError("User.IsValid", "model.user.is_valid.email.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) } - if utf8.RuneCountInString(u.Nickname) > 64 { - return NewLocAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id) + if utf8.RuneCountInString(u.Nickname) > USER_NICKNAME_MAX_RUNES { + return NewAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) } - if utf8.RuneCountInString(u.Position) > 35 { - return NewLocAppError("User.IsValid", "model.user.is_valid.position.app_error", nil, "user_id="+u.Id) + if utf8.RuneCountInString(u.Position) > USER_POSITION_MAX_RUNES { + return NewAppError("User.IsValid", "model.user.is_valid.position.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) } - if utf8.RuneCountInString(u.FirstName) > 64 { - return NewLocAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id) + if utf8.RuneCountInString(u.FirstName) > USER_FIRST_NAME_MAX_RUNES { + return NewAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) } - if utf8.RuneCountInString(u.LastName) > 64 { - return NewLocAppError("User.IsValid", "model.user.is_valid.last_name.app_error", nil, "user_id="+u.Id) + if utf8.RuneCountInString(u.LastName) > USER_LAST_NAME_MAX_RUNES { + return NewAppError("User.IsValid", "model.user.is_valid.last_name.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) } - if u.AuthData != nil && len(*u.AuthData) > 128 { - return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data.app_error", nil, "user_id="+u.Id) + if u.AuthData != nil && len(*u.AuthData) > USER_AUTH_DATA_MAX_LENGTH { + return NewAppError("User.IsValid", "model.user.is_valid.auth_data.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) } if u.AuthData != nil && len(*u.AuthData) > 0 && len(u.AuthService) == 0 { - return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_type.app_error", nil, "user_id="+u.Id) + return NewAppError("User.IsValid", "model.user.is_valid.auth_data_type.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) } if len(u.Password) > 0 && u.AuthData != nil && len(*u.AuthData) > 0 { - return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id) + return NewAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) } return nil @@ -115,7 +143,7 @@ func (u *User) PreSave() { } if u.Username == "" { - u.Username = NewId() + u.Username = "n" + NewId() } if u.AuthData != nil && *u.AuthData == "" { @@ -205,6 +233,44 @@ func (user *User) UpdateMentionKeysFromUsername(oldUsername string) { } } +func (u *User) Patch(patch *UserPatch) { + if patch.Username != nil { + u.Username = *patch.Username + } + + if patch.Nickname != nil { + u.Nickname = *patch.Nickname + } + + if patch.FirstName != nil { + u.FirstName = *patch.FirstName + } + + if patch.LastName != nil { + u.LastName = *patch.LastName + } + + if patch.Position != nil { + u.Position = *patch.Position + } + + if patch.Email != nil { + u.Email = *patch.Email + } + + if patch.Props != nil { + u.Props = *patch.Props + } + + if patch.NotifyProps != nil { + u.NotifyProps = *patch.NotifyProps + } + + if patch.Locale != nil { + u.Locale = *patch.Locale + } +} + // ToJson convert a User to a json string func (u *User) ToJson() string { b, err := json.Marshal(u) @@ -215,6 +281,15 @@ func (u *User) ToJson() string { } } +func (u *UserPatch) ToJson() string { + b, err := json.Marshal(u) + if err != nil { + return "" + } else { + return string(b) + } +} + // Generate a valid strong etag so the browser can cache the results func (u *User) Etag(showFullName, showEmail bool) string { return Etag(u.Id, u.UpdateAt, showFullName, showEmail) @@ -376,6 +451,13 @@ func IsInRole(userRoles string, inRole string) bool { return false } +func (u *User) IsSSOUser() bool { + if u.AuthService != "" && u.AuthService != USER_AUTH_SERVICE_EMAIL { + return true + } + return false +} + func (u *User) IsOAuthUser() bool { if u.AuthService == USER_AUTH_SERVICE_GITLAB { return true @@ -402,6 +484,17 @@ func UserFromJson(data io.Reader) *User { } } +func UserPatchFromJson(data io.Reader) *UserPatch { + decoder := json.NewDecoder(data) + var user UserPatch + err := decoder.Decode(&user) + if err == nil { + return &user + } else { + return nil + } +} + func UserMapToJson(u map[string]*User) string { b, err := json.Marshal(u) if err != nil { @@ -472,7 +565,7 @@ var restrictedUsernames = []string{ } func IsValidUsername(s string) bool { - if len(s) == 0 || len(s) > 64 { + if len(s) < USER_NAME_MIN_LENGTH || len(s) > USER_NAME_MAX_LENGTH { return false } @@ -480,6 +573,10 @@ func IsValidUsername(s string) bool { return false } + if !unicode.IsLetter(rune(s[0])) { + return false + } + for _, restrictedUsername := range restrictedUsernames { if s == restrictedUsername { return false diff --git a/vendor/github.com/mattermost/platform/model/utils.go b/vendor/github.com/mattermost/platform/model/utils.go index 0ce243fe..08809a47 100644 --- a/vendor/github.com/mattermost/platform/model/utils.go +++ b/vendor/github.com/mattermost/platform/model/utils.go @@ -34,14 +34,14 @@ type StringArray []string type EncryptStringMap map[string]string type AppError struct { - Id string `json:"id"` - Message string `json:"message"` // Message to be display to the end user without debugging information - DetailedError string `json:"detailed_error"` // Internal error string to help the developer - RequestId string `json:"request_id,omitempty"` // The RequestId that's also set in the header - StatusCode int `json:"status_code,omitempty"` // The http status code - Where string `json:"-"` // The function where it happened in the form of Struct.Func - IsOAuth bool `json:"is_oauth,omitempty"` // Whether the error is OAuth specific - params map[string]interface{} `json:"-"` + Id string `json:"id"` + Message string `json:"message"` // Message to be display to the end user without debugging information + DetailedError string `json:"detailed_error"` // Internal error string to help the developer + RequestId string `json:"request_id,omitempty"` // The RequestId that's also set in the header + StatusCode int `json:"status_code,omitempty"` // The http status code + Where string `json:"-"` // The function where it happened in the form of Struct.Func + IsOAuth bool `json:"is_oauth,omitempty"` // Whether the error is OAuth specific + params map[string]interface{} } func (er *AppError) Error() string { @@ -93,6 +93,18 @@ func AppErrorFromJson(data io.Reader) *AppError { } } +func NewAppError(where string, id string, params map[string]interface{}, details string, status int) *AppError { + ap := &AppError{} + ap.Id = id + ap.params = params + ap.Message = id + ap.Where = where + ap.DetailedError = details + ap.StatusCode = status + ap.IsOAuth = false + return ap +} + func NewLocAppError(where string, id string, params map[string]interface{}, details string) *AppError { ap := &AppError{} ap.Id = id @@ -268,7 +280,7 @@ func IsValidChannelIdentifier(s string) bool { return false } - if len(s) < 2 { + if len(s) < CHANNEL_NAME_MIN_LENGTH { return false } @@ -370,7 +382,7 @@ func ClearMentionTags(post string) string { var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(?:\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(?:\?[a-z0-9+_~\-\.%=&]*)?)?(?:#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?:\s+|$)$`) var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+(?:\.[A-Za-z0-9]{3,})?)`) -var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true} +var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true, '^': true, '#': true, '$': true, '&': true} func IsValidHttpUrl(rawUrl string) bool { if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 { diff --git a/vendor/github.com/mattermost/platform/model/version.go b/vendor/github.com/mattermost/platform/model/version.go index 2a034dec..32b1c3f5 100644 --- a/vendor/github.com/mattermost/platform/model/version.go +++ b/vendor/github.com/mattermost/platform/model/version.go @@ -13,6 +13,7 @@ import ( // It should be maitained in chronological order with most current // release at the front of the list. var versions = []string{ + "3.7.0", "3.6.0", "3.5.0", "3.4.0", diff --git a/vendor/github.com/mattermost/platform/model/websocket_client.go b/vendor/github.com/mattermost/platform/model/websocket_client.go index 453ae49b..083fe110 100644 --- a/vendor/github.com/mattermost/platform/model/websocket_client.go +++ b/vendor/github.com/mattermost/platform/model/websocket_client.go @@ -8,6 +8,10 @@ import ( "github.com/gorilla/websocket" ) +const ( + SOCKET_MAX_MESSAGE_SIZE_KB = 8 * 1024 // 8KB +) + type WebSocketClient struct { Url string // The location of the server like "ws://localhost:8065" ApiUrl string // The api location of the server like "ws://localhost:8065/api/v3" @@ -22,14 +26,14 @@ type WebSocketClient struct { // NewWebSocketClient constructs a new WebSocket client with convienence // methods for talking to the server. func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) { - conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX+"/users/websocket", nil) + conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX_V3+"/users/websocket", nil) if err != nil { return nil, NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error()) } client := &WebSocketClient{ url, - url + API_URL_SUFFIX, + url + API_URL_SUFFIX_V3, conn, authToken, 1, diff --git a/vendor/github.com/mattermost/platform/model/websocket_message.go b/vendor/github.com/mattermost/platform/model/websocket_message.go index 5c956d57..23820470 100644 --- a/vendor/github.com/mattermost/platform/model/websocket_message.go +++ b/vendor/github.com/mattermost/platform/model/websocket_message.go @@ -14,8 +14,9 @@ const ( WEBSOCKET_EVENT_POST_EDITED = "post_edited" WEBSOCKET_EVENT_POST_DELETED = "post_deleted" WEBSOCKET_EVENT_CHANNEL_DELETED = "channel_deleted" - WEBSOCKET_EVENT_CHANNEL_VIEWED = "channel_viewed" + WEBSOCKET_EVENT_CHANNEL_CREATED = "channel_created" WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added" + WEBSOCKET_EVENT_GROUP_ADDED = "group_added" WEBSOCKET_EVENT_NEW_USER = "new_user" WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team" WEBSOCKET_EVENT_UPDATE_TEAM = "update_team" |