Sync with mattermost 3.6.0

This commit is contained in:
Wim 2017-01-16 23:59:50 +01:00
parent 9987ac3f13
commit 93114b7682
26 changed files with 710 additions and 91 deletions

View File

@ -11,13 +11,17 @@ type ClusterInterface interface {
StartInterNodeCommunication() StartInterNodeCommunication()
StopInterNodeCommunication() StopInterNodeCommunication()
GetClusterInfos() []*model.ClusterInfo GetClusterInfos() []*model.ClusterInfo
GetClusterStats() ([]*model.ClusterStats, *model.AppError)
RemoveAllSessionsForUserId(userId string) RemoveAllSessionsForUserId(userId string)
InvalidateCacheForUser(userId string) InvalidateCacheForUser(userId string)
InvalidateCacheForChannel(channelId string)
InvalidateCacheForChannelPosts(channelId string)
Publish(event *model.WebSocketEvent) Publish(event *model.WebSocketEvent)
UpdateStatus(status *model.Status) UpdateStatus(status *model.Status)
GetLogs() ([]string, *model.AppError) GetLogs() ([]string, *model.AppError)
GetClusterId() string GetClusterId() string
ConfigChanged(previousConfig *model.Config, newConfig *model.Config, sendToOtherServer bool) *model.AppError ConfigChanged(previousConfig *model.Config, newConfig *model.Config, sendToOtherServer bool) *model.AppError
InvalidateAllCaches() *model.AppError
} }
var theClusterInterface ClusterInterface var theClusterInterface ClusterInterface

View File

@ -0,0 +1,41 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package einterfaces
type MetricsInterface interface {
StartServer()
StopServer()
IncrementPostCreate()
IncrementPostSentEmail()
IncrementPostSentPush()
IncrementPostBroadcast()
IncrementPostFileAttachment(count int)
IncrementHttpRequest()
IncrementHttpError()
ObserveHttpRequestDuration(elapsed float64)
IncrementLogin()
IncrementLoginFail()
IncrementEtagHitCounter(route string)
IncrementEtagMissCounter(route string)
IncrementMemCacheHitCounter(cacheName string)
IncrementMemCacheMissCounter(cacheName string)
AddMemCacheHitCounter(cacheName string, amount float64)
AddMemCacheMissCounter(cacheName string, amount float64)
}
var theMetricsInterface MetricsInterface
func RegisterMetricsInterface(newInterface MetricsInterface) {
theMetricsInterface = newInterface
}
func GetMetricsInterface() MetricsInterface {
return theMetricsInterface
}

View File

@ -18,6 +18,14 @@ const (
CHANNEL_MARK_UNREAD_MENTION = "mention" CHANNEL_MARK_UNREAD_MENTION = "mention"
) )
type ChannelUnread struct {
TeamId string
TotalMsgCount int64
MsgCount int64
MentionCount int64
NotifyProps StringMap
}
type ChannelMember struct { type ChannelMember struct {
ChannelId string `json:"channel_id"` ChannelId string `json:"channel_id"`
UserId string `json:"user_id"` UserId string `json:"user_id"`

View File

@ -0,0 +1,35 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
type ChannelSearch struct {
Term string `json:"term"`
}
// ToJson convert a Channel to a json string
func (c *ChannelSearch) ToJson() string {
b, err := json.Marshal(c)
if err != nil {
return ""
} else {
return string(b)
}
}
// ChannelSearchFromJson will decode the input and return a Channel
func ChannelSearchFromJson(data io.Reader) *ChannelSearch {
decoder := json.NewDecoder(data)
var cs ChannelSearch
err := decoder.Decode(&cs)
if err == nil {
return &cs
} else {
return nil
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
type ChannelView struct {
ChannelId string `json:"channel_id"`
PrevChannelId string `json:"prev_channel_id"`
}
func (o *ChannelView) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func ChannelViewFromJson(data io.Reader) *ChannelView {
decoder := json.NewDecoder(data)
var o ChannelView
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}

View File

@ -6,7 +6,6 @@ package model
import ( import (
"bytes" "bytes"
"fmt" "fmt"
l4g "github.com/alecthomas/log4go"
"io" "io"
"io/ioutil" "io/ioutil"
"mime/multipart" "mime/multipart"
@ -15,6 +14,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
l4g "github.com/alecthomas/log4go"
) )
const ( const (
@ -48,6 +49,13 @@ type Result struct {
Data interface{} Data interface{}
} }
type ResponseMetadata struct {
StatusCode int
Error *AppError
RequestId string
Etag string
}
type Client struct { type Client struct {
Url string // The location of the server like "http://localhost:8065" Url string // The location of the server like "http://localhost:8065"
ApiUrl string // The api location of the server like "http://localhost:8065/api/v3" ApiUrl string // The api location of the server like "http://localhost:8065/api/v3"
@ -291,34 +299,6 @@ func (c *Client) GetPing() (map[string]string, *AppError) {
// Team Routes Section // Team Routes Section
// SignupTeam sends an email with a team sign-up link to the provided address if email
// verification is enabled, otherwise it returns a map with a "follow_link" entry
// containing the team sign-up link.
func (c *Client) SignupTeam(email string, displayName string) (*Result, *AppError) {
m := make(map[string]string)
m["email"] = email
m["display_name"] = displayName
if r, err := c.DoApiPost("/teams/signup", MapToJson(m)); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
}
}
// CreateTeamFromSignup creates a team based on the provided TeamSignup struct. On success
// it returns the TeamSignup struct.
func (c *Client) CreateTeamFromSignup(teamSignup *TeamSignup) (*Result, *AppError) {
if r, err := c.DoApiPost("/teams/create_from_signup", teamSignup.ToJson()); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), TeamSignupFromJson(r.Body)}, nil
}
}
// CreateTeam creates a team based on the provided Team struct. On success it returns // CreateTeam creates a team based on the provided Team struct. On success it returns
// the Team struct with the Id, CreateAt and other server-decided fields populated. // the Team struct with the Id, CreateAt and other server-decided fields populated.
func (c *Client) CreateTeam(team *Team) (*Result, *AppError) { func (c *Client) CreateTeam(team *Team) (*Result, *AppError) {
@ -500,6 +480,32 @@ func (c *Client) GetUser(id string, etag string) (*Result, *AppError) {
} }
} }
// getByUsername returns a user based on a provided username string. Must be authenticated.
func (c *Client) GetByUsername(username string, etag string) (*Result, *AppError) {
if r, err := c.DoApiGet(fmt.Sprintf("/users/name/%v", username), "", etag); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
}
}
// getByEmail returns a user based on a provided username string. Must be authenticated.
func (c *Client) GetByEmail(email string, etag string) (*User, *ResponseMetadata) {
if r, err := c.DoApiGet(fmt.Sprintf("/users/email/%v", email), "", etag); err != nil {
return nil, &ResponseMetadata{StatusCode: r.StatusCode, Error: err}
} else {
defer closeBody(r)
return UserFromJson(r.Body),
&ResponseMetadata{
StatusCode: r.StatusCode,
RequestId: r.Header.Get(HEADER_REQUEST_ID),
Etag: r.Header.Get(HEADER_ETAG_SERVER),
}
}
}
// GetMe returns the current user. // GetMe returns the current user.
func (c *Client) GetMe(etag string) (*Result, *AppError) { func (c *Client) GetMe(etag string) (*Result, *AppError) {
if r, err := c.DoApiGet("/users/me", "", etag); err != nil { if r, err := c.DoApiGet("/users/me", "", etag); err != nil {
@ -610,6 +616,19 @@ func (c *Client) AutocompleteUsersInTeam(term string) (*Result, *AppError) {
} }
} }
// AutocompleteUsers returns a list for autocompletion of users on the system that match the provided term,
// matching against username, full name and nickname. Must be authenticated.
func (c *Client) AutocompleteUsers(term string) (*Result, *AppError) {
url := fmt.Sprintf("/users/autocomplete?term=%s", url.QueryEscape(term))
if r, err := c.DoApiGet(url, "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), UserListFromJson(r.Body)}, nil
}
}
// LoginById authenticates a user by user id and password. // LoginById authenticates a user by user id and password.
func (c *Client) LoginById(id string, password string) (*Result, *AppError) { func (c *Client) LoginById(id string, password string) (*Result, *AppError) {
m := make(map[string]string) m := make(map[string]string)
@ -802,12 +821,9 @@ func (c *Client) EmailToLDAP(m map[string]string) (*Result, *AppError) {
} }
} }
func (c *Client) Command(channelId string, command string, suggest bool) (*Result, *AppError) { func (c *Client) Command(channelId string, command string) (*Result, *AppError) {
m := make(map[string]string) args := &CommandArgs{ChannelId: channelId, Command: command}
m["command"] = command if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/execute", args.ToJson()); err != nil {
m["channelId"] = channelId
m["suggest"] = strconv.FormatBool(suggest)
if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/execute", MapToJson(m)); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
@ -846,6 +862,16 @@ func (c *Client) CreateCommand(cmd *Command) (*Result, *AppError) {
} }
} }
func (c *Client) UpdateCommand(cmd *Command) (*Result, *AppError) {
if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/update", cmd.ToJson()); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), CommandFromJson(r.Body)}, nil
}
}
func (c *Client) RegenCommandToken(data map[string]string) (*Result, *AppError) { func (c *Client) RegenCommandToken(data map[string]string) (*Result, *AppError) {
if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/regen_token", MapToJson(data)); err != nil { if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/regen_token", MapToJson(data)); err != nil {
return nil, err return nil, err
@ -940,6 +966,16 @@ func (c *Client) ReloadConfig() (bool, *AppError) {
} }
} }
func (c *Client) InvalidateAllCaches() (bool, *AppError) {
c.clearExtraProperties()
if r, err := c.DoApiGet("/admin/invalidate_all_caches", "", ""); err != nil {
return false, err
} else {
c.fillInExtraProperties(r)
return c.CheckStatusOK(r), nil
}
}
func (c *Client) SaveConfig(config *Config) (*Result, *AppError) { func (c *Client) SaveConfig(config *Config) (*Result, *AppError) {
if r, err := c.DoApiPost("/admin/save_config", config.ToJson()); err != nil { if r, err := c.DoApiPost("/admin/save_config", config.ToJson()); err != nil {
return nil, err return nil, err
@ -1143,6 +1179,7 @@ func (c *Client) GetChannel(id, etag string) (*Result, *AppError) {
} }
} }
// SCHEDULED FOR DEPRECATION IN 3.7 - use GetMoreChannelsPage instead
func (c *Client) GetMoreChannels(etag string) (*Result, *AppError) { func (c *Client) GetMoreChannels(etag string) (*Result, *AppError) {
if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/more", "", etag); err != nil { if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/more", "", etag); err != nil {
return nil, err return nil, err
@ -1153,6 +1190,43 @@ func (c *Client) GetMoreChannels(etag string) (*Result, *AppError) {
} }
} }
// GetMoreChannelsPage will return a page of open channels the user is not in based on
// the provided offset and limit. Must be authenticated.
func (c *Client) GetMoreChannelsPage(offset int, limit int) (*Result, *AppError) {
if r, err := c.DoApiGet(fmt.Sprintf(c.GetTeamRoute()+"/channels/more/%v/%v", offset, limit), "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
}
}
// SearchMoreChannels will return a list of open channels the user is not in, that matches
// the search criteria provided. Must be authenticated.
func (c *Client) SearchMoreChannels(channelSearch ChannelSearch) (*Result, *AppError) {
if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/more/search", channelSearch.ToJson()); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
}
}
// AutocompleteChannels will return a list of open channels that match the provided
// string. Must be authenticated.
func (c *Client) AutocompleteChannels(term string) (*Result, *AppError) {
url := fmt.Sprintf("%s/channels/autocomplete?term=%s", c.GetTeamRoute(), url.QueryEscape(term))
if r, err := c.DoApiGet(url, "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
}
}
func (c *Client) GetChannelCounts(etag string) (*Result, *AppError) { func (c *Client) GetChannelCounts(etag string) (*Result, *AppError) {
if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/counts", "", etag); err != nil { if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/counts", "", etag); err != nil {
return nil, err return nil, err
@ -1173,6 +1247,16 @@ func (c *Client) GetChannels(etag string) (*Result, *AppError) {
} }
} }
func (c *Client) GetChannelByName(channelName string) (*Result, *AppError) {
if r, err := c.DoApiGet(c.GetChannelNameRoute(channelName), "", ""); 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) JoinChannel(id string) (*Result, *AppError) { func (c *Client) JoinChannel(id string) (*Result, *AppError) {
if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/join", ""); err != nil { if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/join", ""); err != nil {
return nil, err return nil, err
@ -1240,6 +1324,7 @@ func (c *Client) RemoveChannelMember(id, user_id string) (*Result, *AppError) {
// UpdateLastViewedAt will mark a channel as read. // UpdateLastViewedAt will mark a channel as read.
// The channelId indicates the channel to mark as read. If active is true, push notifications // The channelId indicates the channel to mark as read. If active is true, push notifications
// will be cleared if there are unread messages. The default for active is true. // will be cleared if there are unread messages. The default for active is true.
// SCHEDULED FOR DEPRECATION IN 3.8 - use ViewChannel instead
func (c *Client) UpdateLastViewedAt(channelId string, active bool) (*Result, *AppError) { func (c *Client) UpdateLastViewedAt(channelId string, active bool) (*Result, *AppError) {
data := make(map[string]interface{}) data := make(map[string]interface{})
data["active"] = active data["active"] = active
@ -1252,6 +1337,23 @@ func (c *Client) UpdateLastViewedAt(channelId string, active bool) (*Result, *Ap
} }
} }
// ViewChannel performs all the actions related to viewing a channel. This includes marking
// the channel and the previous one as read, and marking the channel as being actively viewed.
// ChannelId is required but may be blank to indicate no channel is being viewed.
// PrevChannelId is optional, populate to indicate a channel switch occurred.
func (c *Client) ViewChannel(params ChannelView) (bool, *ResponseMetadata) {
if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/view", params.ToJson()); err != nil {
return false, &ResponseMetadata{StatusCode: r.StatusCode, Error: err}
} else {
return c.CheckStatusOK(r),
&ResponseMetadata{
StatusCode: r.StatusCode,
RequestId: r.Header.Get(HEADER_REQUEST_ID),
Etag: r.Header.Get(HEADER_ETAG_SERVER),
}
}
}
func (c *Client) GetChannelStats(id string, etag string) (*Result, *AppError) { func (c *Client) GetChannelStats(id string, etag string) (*Result, *AppError) {
if r, err := c.DoApiGet(c.GetChannelRoute(id)+"/stats", "", etag); err != nil { if r, err := c.DoApiGet(c.GetChannelRoute(id)+"/stats", "", etag); err != nil {
return nil, err return nil, err
@ -1272,6 +1374,18 @@ func (c *Client) GetChannelMember(channelId string, userId string) (*Result, *Ap
} }
} }
// GetChannelMembersByIds will return channel member objects as an array based on the
// channel id and a list of user ids provided. Must be authenticated.
func (c *Client) GetChannelMembersByIds(channelId string, userIds []string) (*Result, *AppError) {
if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/members/ids", 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), ChannelMembersFromJson(r.Body)}, nil
}
}
func (c *Client) CreatePost(post *Post) (*Result, *AppError) { func (c *Client) CreatePost(post *Post) (*Result, *AppError) {
if r, err := c.DoApiPost(c.GetChannelRoute(post.ChannelId)+"/posts/create", post.ToJson()); err != nil { if r, err := c.DoApiPost(c.GetChannelRoute(post.ChannelId)+"/posts/create", post.ToJson()); err != nil {
return nil, err return nil, err
@ -1342,6 +1456,21 @@ func (c *Client) GetPost(channelId string, postId string, etag string) (*Result,
} }
} }
// GetPostById returns a post and any posts in the same thread by post id
func (c *Client) GetPostById(postId string, etag string) (*PostList, *ResponseMetadata) {
if r, err := c.DoApiGet(c.GetTeamRoute()+fmt.Sprintf("/posts/%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) { 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 { if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/delete", postId), ""); err != nil {
return nil, err return nil, err
@ -1629,6 +1758,7 @@ func (c *Client) GetStatusesByIds(userIds []string) (*Result, *AppError) {
// SetActiveChannel sets the the channel id the user is currently viewing. // SetActiveChannel sets the the channel id the user is currently viewing.
// The channelId key is required but the value can be blank. Returns standard // The channelId key is required but the value can be blank. Returns standard
// response. // response.
// SCHEDULED FOR DEPRECATION IN 3.8 - use ViewChannel instead
func (c *Client) SetActiveChannel(channelId string) (*Result, *AppError) { func (c *Client) SetActiveChannel(channelId string) (*Result, *AppError) {
data := map[string]string{} data := map[string]string{}
data["channel_id"] = channelId data["channel_id"] = channelId
@ -1663,6 +1793,36 @@ func (c *Client) GetTeamMembers(teamId string, offset int, limit int) (*Result,
} }
} }
// GetMyTeamMembers will return an array with team member objects that the current user
// is a member of. Must be authenticated.
func (c *Client) GetMyTeamMembers() (*Result, *AppError) {
if r, err := c.DoApiGet("/teams/members", "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), TeamMembersFromJson(r.Body)}, nil
}
}
// GetMyTeamsUnread 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 *Client) GetMyTeamsUnread(teamId string) (*Result, *AppError) {
endpoint := "/teams/unread"
if teamId != "" {
endpoint += fmt.Sprintf("?id=%s", url.QueryEscape(teamId))
}
if r, err := c.DoApiGet(endpoint, "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), TeamsUnreadFromJson(r.Body)}, nil
}
}
// GetTeamMember will return a team member object based on the team id and user id provided. // GetTeamMember will return a team member object based on the team id and user id provided.
// Must be authenticated. // Must be authenticated.
func (c *Client) GetTeamMember(teamId string, userId string) (*Result, *AppError) { func (c *Client) GetTeamMember(teamId string, userId string) (*Result, *AppError) {
@ -1687,6 +1847,18 @@ func (c *Client) GetTeamStats(teamId string) (*Result, *AppError) {
} }
} }
// GetTeamStats will return a team stats object containing the number of users on the team
// based on the team id provided. Must be authenticated.
func (c *Client) GetTeamByName(teamName string) (*Result, *AppError) {
if r, err := c.DoApiGet(fmt.Sprintf("/teams/name/%v", teamName), "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), TeamStatsFromJson(r.Body)}, nil
}
}
// GetTeamMembersByIds will return team member objects as an array based on the // GetTeamMembersByIds will return team member objects as an array based on the
// team id and a list of user ids provided. Must be authenticated. // team id and a list of user ids provided. Must be authenticated.
func (c *Client) GetTeamMembersByIds(teamId string, userIds []string) (*Result, *AppError) { func (c *Client) GetTeamMembersByIds(teamId string, userIds []string) (*Result, *AppError) {
@ -2030,6 +2202,7 @@ func (c *Client) DeleteEmoji(id string) (bool, *AppError) {
if r, err := c.DoApiPost(c.GetEmojiRoute()+"/delete", MapToJson(data)); err != nil { if r, err := c.DoApiPost(c.GetEmojiRoute()+"/delete", MapToJson(data)); err != nil {
return false, err return false, err
} else { } else {
defer closeBody(r)
c.fillInExtraProperties(r) c.fillInExtraProperties(r)
return c.CheckStatusOK(r), nil return c.CheckStatusOK(r), nil
} }
@ -2060,6 +2233,7 @@ func (c *Client) UploadCertificateFile(data []byte, contentType string) *AppErro
return AppErrorFromJson(rp.Body) return AppErrorFromJson(rp.Body)
} else { } else {
defer closeBody(rp) defer closeBody(rp)
c.fillInExtraProperties(rp)
return nil return nil
} }
} }
@ -2071,6 +2245,7 @@ func (c *Client) RemoveCertificateFile(filename string) *AppError {
return err return err
} else { } else {
defer closeBody(r) defer closeBody(r)
c.fillInExtraProperties(r)
return nil return nil
} }
} }
@ -2082,6 +2257,7 @@ func (c *Client) SamlCertificateStatus(filename string) (map[string]interface{},
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
c.fillInExtraProperties(r)
return StringInterfaceFromJson(r.Body), nil return StringInterfaceFromJson(r.Body), nil
} }
} }
@ -2110,3 +2286,36 @@ func (c *Client) GetFileInfosForPost(channelId string, postId string, etag strin
return FileInfosFromJson(r.Body), nil return FileInfosFromJson(r.Body), nil
} }
} }
// Saves an emoji reaction for a post in the given channel. Returns the saved reaction if successful, otherwise returns an AppError.
func (c *Client) SaveReaction(channelId string, reaction *Reaction) (*Reaction, *AppError) {
if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/reactions/save", reaction.PostId), reaction.ToJson()); err != nil {
return nil, err
} else {
defer closeBody(r)
c.fillInExtraProperties(r)
return ReactionFromJson(r.Body), nil
}
}
// Removes an emoji reaction for a post in the given channel. Returns nil if successful, otherwise returns an AppError.
func (c *Client) DeleteReaction(channelId string, reaction *Reaction) *AppError {
if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/reactions/delete", reaction.PostId), reaction.ToJson()); err != nil {
return err
} else {
defer closeBody(r)
c.fillInExtraProperties(r)
return nil
}
}
// Lists all emoji reactions made for the given post in the given channel. Returns a list of Reactions if successful, otherwise returns an AppError.
func (c *Client) ListReactions(channelId string, postId string) ([]*Reaction, *AppError) {
if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/reactions", postId), "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
c.fillInExtraProperties(r)
return ReactionsFromJson(r.Body), nil
}
}

View File

@ -0,0 +1,36 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
type ClusterStats struct {
Id string `json:"id"`
TotalWebsocketConnections int `json:"total_websocket_connections"`
TotalReadDbConnections int `json:"total_read_db_connections"`
TotalMasterDbConnections int `json:"total_master_db_connections"`
}
func (me *ClusterStats) ToJson() string {
b, err := json.Marshal(me)
if err != nil {
return ""
} else {
return string(b)
}
}
func ClusterStatsFromJson(data io.Reader) *ClusterStats {
decoder := json.NewDecoder(data)
var me ClusterStats
err := decoder.Decode(&me)
if err == nil {
return &me
} else {
return nil
}
}

View File

@ -0,0 +1,36 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
type CommandArgs struct {
ChannelId string `json:"channel_id"`
RootId string `json:"root_id"`
ParentId string `json:"parent_id"`
Command string `json:"command"`
}
func (o *CommandArgs) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func CommandArgsFromJson(data io.Reader) *CommandArgs {
decoder := json.NewDecoder(data)
var o CommandArgs
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}

View File

@ -16,6 +16,8 @@ const (
type CommandResponse struct { type CommandResponse struct {
ResponseType string `json:"response_type"` ResponseType string `json:"response_type"`
Text string `json:"text"` Text string `json:"text"`
Username string `json:"username"`
IconURL string `json:"icon_url"`
GotoLocation string `json:"goto_location"` GotoLocation string `json:"goto_location"`
Attachments interface{} `json:"attachments"` Attachments interface{} `json:"attachments"`
} }

View File

@ -38,9 +38,10 @@ const (
DIRECT_MESSAGE_ANY = "any" DIRECT_MESSAGE_ANY = "any"
DIRECT_MESSAGE_TEAM = "team" DIRECT_MESSAGE_TEAM = "team"
PERMISSIONS_ALL = "all" PERMISSIONS_ALL = "all"
PERMISSIONS_TEAM_ADMIN = "team_admin" PERMISSIONS_CHANNEL_ADMIN = "channel_admin"
PERMISSIONS_SYSTEM_ADMIN = "system_admin" PERMISSIONS_TEAM_ADMIN = "team_admin"
PERMISSIONS_SYSTEM_ADMIN = "system_admin"
FAKE_SETTING = "********************************" FAKE_SETTING = "********************************"
@ -80,6 +81,7 @@ type ServiceSettings struct {
EnableSecurityFixAlert *bool EnableSecurityFixAlert *bool
EnableInsecureOutgoingConnections *bool EnableInsecureOutgoingConnections *bool
EnableMultifactorAuthentication *bool EnableMultifactorAuthentication *bool
EnforceMultifactorAuthentication *bool
AllowCorsFrom *string AllowCorsFrom *string
SessionLengthWebInDays *int SessionLengthWebInDays *int
SessionLengthMobileInDays *int SessionLengthMobileInDays *int
@ -98,6 +100,16 @@ type ClusterSettings struct {
InterNodeUrls []string InterNodeUrls []string
} }
type MetricsSettings struct {
Enable *bool
BlockProfileRate *int
ListenAddress *string
}
type AnalyticsSettings struct {
MaxUsersForStatistics *int
}
type SSOSettings struct { type SSOSettings struct {
Enable bool Enable bool
Secret string Secret string
@ -219,8 +231,13 @@ type TeamSettings struct {
RestrictTeamInvite *string RestrictTeamInvite *string
RestrictPublicChannelManagement *string RestrictPublicChannelManagement *string
RestrictPrivateChannelManagement *string RestrictPrivateChannelManagement *string
RestrictPublicChannelCreation *string
RestrictPrivateChannelCreation *string
RestrictPublicChannelDeletion *string
RestrictPrivateChannelDeletion *string
UserStatusAwayTimeout *int64 UserStatusAwayTimeout *int64
MaxChannelsPerTeam *int64 MaxChannelsPerTeam *int64
MaxNotificationsPerChannel *int64
} }
type LdapSettings struct { type LdapSettings struct {
@ -243,6 +260,7 @@ type LdapSettings struct {
UsernameAttribute *string UsernameAttribute *string
NicknameAttribute *string NicknameAttribute *string
IdAttribute *string IdAttribute *string
PositionAttribute *string
// Syncronization // Syncronization
SyncIntervalMinutes *int SyncIntervalMinutes *int
@ -289,6 +307,7 @@ type SamlSettings struct {
UsernameAttribute *string UsernameAttribute *string
NicknameAttribute *string NicknameAttribute *string
LocaleAttribute *string LocaleAttribute *string
PositionAttribute *string
LoginButtonText *string LoginButtonText *string
} }
@ -330,6 +349,8 @@ type Config struct {
SamlSettings SamlSettings SamlSettings SamlSettings
NativeAppSettings NativeAppSettings NativeAppSettings NativeAppSettings
ClusterSettings ClusterSettings ClusterSettings ClusterSettings
MetricsSettings MetricsSettings
AnalyticsSettings AnalyticsSettings
WebrtcSettings WebrtcSettings WebrtcSettings WebrtcSettings
} }
@ -376,23 +397,32 @@ func (o *Config) SetDefaults() {
// Defaults to "s3.amazonaws.com" // Defaults to "s3.amazonaws.com"
o.FileSettings.AmazonS3Endpoint = "s3.amazonaws.com" o.FileSettings.AmazonS3Endpoint = "s3.amazonaws.com"
} }
if o.FileSettings.AmazonS3Region == "" { if o.FileSettings.AmazonS3Region == "" {
// Defaults to "us-east-1" region. // Defaults to "us-east-1" region.
o.FileSettings.AmazonS3Region = "us-east-1" o.FileSettings.AmazonS3Region = "us-east-1"
} }
if o.FileSettings.AmazonS3SSL == nil { if o.FileSettings.AmazonS3SSL == nil {
o.FileSettings.AmazonS3SSL = new(bool) o.FileSettings.AmazonS3SSL = new(bool)
*o.FileSettings.AmazonS3SSL = true // Secure by default. *o.FileSettings.AmazonS3SSL = true // Secure by default.
} }
if o.FileSettings.MaxFileSize == nil { if o.FileSettings.MaxFileSize == nil {
o.FileSettings.MaxFileSize = new(int64) o.FileSettings.MaxFileSize = new(int64)
*o.FileSettings.MaxFileSize = 52428800 // 50 MB *o.FileSettings.MaxFileSize = 52428800 // 50 MB
} }
if len(*o.FileSettings.PublicLinkSalt) == 0 { if len(*o.FileSettings.PublicLinkSalt) == 0 {
o.FileSettings.PublicLinkSalt = new(string) o.FileSettings.PublicLinkSalt = new(string)
*o.FileSettings.PublicLinkSalt = NewRandomString(32) *o.FileSettings.PublicLinkSalt = NewRandomString(32)
} }
if o.FileSettings.InitialFont == "" {
// Defaults to "luximbi.ttf"
o.FileSettings.InitialFont = "luximbi.ttf"
}
if len(o.EmailSettings.InviteSalt) == 0 { if len(o.EmailSettings.InviteSalt) == 0 {
o.EmailSettings.InviteSalt = NewRandomString(32) o.EmailSettings.InviteSalt = NewRandomString(32)
} }
@ -426,6 +456,11 @@ func (o *Config) SetDefaults() {
*o.ServiceSettings.EnableMultifactorAuthentication = false *o.ServiceSettings.EnableMultifactorAuthentication = false
} }
if o.ServiceSettings.EnforceMultifactorAuthentication == nil {
o.ServiceSettings.EnforceMultifactorAuthentication = new(bool)
*o.ServiceSettings.EnforceMultifactorAuthentication = false
}
if o.PasswordSettings.MinimumLength == nil { if o.PasswordSettings.MinimumLength == nil {
o.PasswordSettings.MinimumLength = new(int) o.PasswordSettings.MinimumLength = new(int)
*o.PasswordSettings.MinimumLength = PASSWORD_MINIMUM_LENGTH *o.PasswordSettings.MinimumLength = PASSWORD_MINIMUM_LENGTH
@ -491,6 +526,30 @@ func (o *Config) SetDefaults() {
*o.TeamSettings.RestrictPrivateChannelManagement = PERMISSIONS_ALL *o.TeamSettings.RestrictPrivateChannelManagement = PERMISSIONS_ALL
} }
if o.TeamSettings.RestrictPublicChannelCreation == nil {
o.TeamSettings.RestrictPublicChannelCreation = new(string)
// If this setting does not exist, assume migration from <3.6, so use management setting as default.
*o.TeamSettings.RestrictPublicChannelCreation = *o.TeamSettings.RestrictPublicChannelManagement
}
if o.TeamSettings.RestrictPrivateChannelCreation == nil {
o.TeamSettings.RestrictPrivateChannelCreation = new(string)
// If this setting does not exist, assume migration from <3.6, so use management setting as default.
*o.TeamSettings.RestrictPrivateChannelCreation = *o.TeamSettings.RestrictPrivateChannelManagement
}
if o.TeamSettings.RestrictPublicChannelDeletion == nil {
o.TeamSettings.RestrictPublicChannelDeletion = new(string)
// If this setting does not exist, assume migration from <3.6, so use management setting as default.
*o.TeamSettings.RestrictPublicChannelDeletion = *o.TeamSettings.RestrictPublicChannelManagement
}
if o.TeamSettings.RestrictPrivateChannelDeletion == nil {
o.TeamSettings.RestrictPrivateChannelDeletion = new(string)
// If this setting does not exist, assume migration from <3.6, so use management setting as default.
*o.TeamSettings.RestrictPrivateChannelDeletion = *o.TeamSettings.RestrictPrivateChannelManagement
}
if o.TeamSettings.UserStatusAwayTimeout == nil { if o.TeamSettings.UserStatusAwayTimeout == nil {
o.TeamSettings.UserStatusAwayTimeout = new(int64) o.TeamSettings.UserStatusAwayTimeout = new(int64)
*o.TeamSettings.UserStatusAwayTimeout = 300 *o.TeamSettings.UserStatusAwayTimeout = 300
@ -501,6 +560,11 @@ func (o *Config) SetDefaults() {
*o.TeamSettings.MaxChannelsPerTeam = 2000 *o.TeamSettings.MaxChannelsPerTeam = 2000
} }
if o.TeamSettings.MaxNotificationsPerChannel == nil {
o.TeamSettings.MaxNotificationsPerChannel = new(int64)
*o.TeamSettings.MaxNotificationsPerChannel = 1000
}
if o.EmailSettings.EnableSignInWithEmail == nil { if o.EmailSettings.EnableSignInWithEmail == nil {
o.EmailSettings.EnableSignInWithEmail = new(bool) o.EmailSettings.EnableSignInWithEmail = new(bool)
@ -671,6 +735,11 @@ func (o *Config) SetDefaults() {
*o.LdapSettings.IdAttribute = "" *o.LdapSettings.IdAttribute = ""
} }
if o.LdapSettings.PositionAttribute == nil {
o.LdapSettings.PositionAttribute = new(string)
*o.LdapSettings.PositionAttribute = ""
}
if o.LdapSettings.SyncIntervalMinutes == nil { if o.LdapSettings.SyncIntervalMinutes == nil {
o.LdapSettings.SyncIntervalMinutes = new(int) o.LdapSettings.SyncIntervalMinutes = new(int)
*o.LdapSettings.SyncIntervalMinutes = 60 *o.LdapSettings.SyncIntervalMinutes = 60
@ -772,6 +841,21 @@ func (o *Config) SetDefaults() {
o.ClusterSettings.InterNodeUrls = []string{} o.ClusterSettings.InterNodeUrls = []string{}
} }
if o.MetricsSettings.ListenAddress == nil {
o.MetricsSettings.ListenAddress = new(string)
*o.MetricsSettings.ListenAddress = ":8067"
}
if o.MetricsSettings.Enable == nil {
o.MetricsSettings.Enable = new(bool)
*o.MetricsSettings.Enable = false
}
if o.AnalyticsSettings.MaxUsersForStatistics == nil {
o.AnalyticsSettings.MaxUsersForStatistics = new(int)
*o.AnalyticsSettings.MaxUsersForStatistics = 2500
}
if o.ComplianceSettings.Enable == nil { if o.ComplianceSettings.Enable == nil {
o.ComplianceSettings.Enable = new(bool) o.ComplianceSettings.Enable = new(bool)
*o.ComplianceSettings.Enable = false *o.ComplianceSettings.Enable = false
@ -882,6 +966,11 @@ func (o *Config) SetDefaults() {
*o.SamlSettings.NicknameAttribute = "" *o.SamlSettings.NicknameAttribute = ""
} }
if o.SamlSettings.PositionAttribute == nil {
o.SamlSettings.PositionAttribute = new(string)
*o.SamlSettings.PositionAttribute = ""
}
if o.SamlSettings.LocaleAttribute == nil { if o.SamlSettings.LocaleAttribute == nil {
o.SamlSettings.LocaleAttribute = new(string) o.SamlSettings.LocaleAttribute = new(string)
*o.SamlSettings.LocaleAttribute = "" *o.SamlSettings.LocaleAttribute = ""
@ -952,6 +1041,11 @@ func (o *Config) SetDefaults() {
*o.ServiceSettings.Forward80To443 = false *o.ServiceSettings.Forward80To443 = false
} }
if o.MetricsSettings.BlockProfileRate == nil {
o.MetricsSettings.BlockProfileRate = new(int)
*o.MetricsSettings.BlockProfileRate = 0
}
o.defaultWebrtcSettings() o.defaultWebrtcSettings()
} }
@ -987,6 +1081,10 @@ func (o *Config) IsValid() *AppError {
return NewLocAppError("Config.IsValid", "model.config.is_valid.max_channels.app_error", nil, "") return NewLocAppError("Config.IsValid", "model.config.is_valid.max_channels.app_error", nil, "")
} }
if *o.TeamSettings.MaxNotificationsPerChannel <= 0 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.max_notify_per_channel.app_error", nil, "")
}
if !(*o.TeamSettings.RestrictDirectMessage == DIRECT_MESSAGE_ANY || *o.TeamSettings.RestrictDirectMessage == DIRECT_MESSAGE_TEAM) { if !(*o.TeamSettings.RestrictDirectMessage == DIRECT_MESSAGE_ANY || *o.TeamSettings.RestrictDirectMessage == DIRECT_MESSAGE_TEAM) {
return NewLocAppError("Config.IsValid", "model.config.is_valid.restrict_direct_message.app_error", nil, "") return NewLocAppError("Config.IsValid", "model.config.is_valid.restrict_direct_message.app_error", nil, "")
} }

View File

@ -6,6 +6,7 @@ package model
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"regexp" "regexp"
"strings" "strings"
@ -233,7 +234,7 @@ func expandAnnouncements(i *IncomingWebhookRequest) {
for _, field := range fields { for _, field := range fields {
f := field.(map[string]interface{}) f := field.(map[string]interface{})
if f["value"] != nil { if f["value"] != nil {
f["value"] = expandAnnouncement(f["value"].(string)) f["value"] = expandAnnouncement(fmt.Sprintf("%v", f["value"]))
} }
} }
} }

View File

@ -39,6 +39,7 @@ type Features struct {
Office365OAuth *bool `json:"office365_oauth"` Office365OAuth *bool `json:"office365_oauth"`
Compliance *bool `json:"compliance"` Compliance *bool `json:"compliance"`
Cluster *bool `json:"cluster"` Cluster *bool `json:"cluster"`
Metrics *bool `json:"metrics"`
CustomBrand *bool `json:"custom_brand"` CustomBrand *bool `json:"custom_brand"`
MHPNS *bool `json:"mhpns"` MHPNS *bool `json:"mhpns"`
SAML *bool `json:"saml"` SAML *bool `json:"saml"`
@ -55,6 +56,7 @@ func (f *Features) ToMap() map[string]interface{} {
"office365": *f.Office365OAuth, "office365": *f.Office365OAuth,
"compliance": *f.Compliance, "compliance": *f.Compliance,
"cluster": *f.Cluster, "cluster": *f.Cluster,
"metrics": *f.Metrics,
"custom_brand": *f.CustomBrand, "custom_brand": *f.CustomBrand,
"mhpns": *f.MHPNS, "mhpns": *f.MHPNS,
"saml": *f.SAML, "saml": *f.SAML,
@ -104,6 +106,11 @@ func (f *Features) SetDefaults() {
*f.Cluster = *f.FutureFeatures *f.Cluster = *f.FutureFeatures
} }
if f.Metrics == nil {
f.Metrics = new(bool)
*f.Metrics = *f.FutureFeatures
}
if f.CustomBrand == nil { if f.CustomBrand == nil {
f.CustomBrand = new(bool) f.CustomBrand = new(bool)
*f.CustomBrand = *f.FutureFeatures *f.CustomBrand = *f.FutureFeatures

View File

@ -17,8 +17,14 @@ const (
POST_JOIN_LEAVE = "system_join_leave" POST_JOIN_LEAVE = "system_join_leave"
POST_ADD_REMOVE = "system_add_remove" POST_ADD_REMOVE = "system_add_remove"
POST_HEADER_CHANGE = "system_header_change" POST_HEADER_CHANGE = "system_header_change"
POST_DISPLAYNAME_CHANGE = "system_displayname_change"
POST_CHANNEL_DELETED = "system_channel_deleted" POST_CHANNEL_DELETED = "system_channel_deleted"
POST_EPHEMERAL = "system_ephemeral" POST_EPHEMERAL = "system_ephemeral"
POST_FILEIDS_MAX_RUNES = 150
POST_FILENAMES_MAX_RUNES = 4000
POST_HASHTAGS_MAX_RUNES = 1000
POST_MESSAGE_MAX_RUNES = 4000
POST_PROPS_MAX_RUNES = 8000
) )
type Post struct { type Post struct {
@ -38,6 +44,7 @@ type Post struct {
Filenames StringArray `json:"filenames,omitempty"` // Deprecated, do not use this field any more Filenames StringArray `json:"filenames,omitempty"` // Deprecated, do not use this field any more
FileIds StringArray `json:"file_ids,omitempty"` FileIds StringArray `json:"file_ids,omitempty"`
PendingPostId string `json:"pending_post_id" db:"-"` PendingPostId string `json:"pending_post_id" db:"-"`
HasReactions bool `json:"has_reactions,omitempty"`
} }
func (o *Post) ToJson() string { func (o *Post) ToJson() string {
@ -102,28 +109,30 @@ func (o *Post) IsValid() *AppError {
return NewLocAppError("Post.IsValid", "model.post.is_valid.original_id.app_error", nil, "") return NewLocAppError("Post.IsValid", "model.post.is_valid.original_id.app_error", nil, "")
} }
if utf8.RuneCountInString(o.Message) > 4000 { if utf8.RuneCountInString(o.Message) > POST_MESSAGE_MAX_RUNES {
return NewLocAppError("Post.IsValid", "model.post.is_valid.msg.app_error", nil, "id="+o.Id) return NewLocAppError("Post.IsValid", "model.post.is_valid.msg.app_error", nil, "id="+o.Id)
} }
if utf8.RuneCountInString(o.Hashtags) > 1000 { if utf8.RuneCountInString(o.Hashtags) > POST_HASHTAGS_MAX_RUNES {
return NewLocAppError("Post.IsValid", "model.post.is_valid.hashtags.app_error", nil, "id="+o.Id) return NewLocAppError("Post.IsValid", "model.post.is_valid.hashtags.app_error", nil, "id="+o.Id)
} }
// should be removed once more message types are supported // 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) { 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_DISPLAYNAME_CHANGE || o.Type == POST_CHANNEL_DELETED) {
return NewLocAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type) return NewLocAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type)
} }
if utf8.RuneCountInString(ArrayToJson(o.Filenames)) > 4000 { if utf8.RuneCountInString(ArrayToJson(o.Filenames)) > POST_FILENAMES_MAX_RUNES {
return NewLocAppError("Post.IsValid", "model.post.is_valid.filenames.app_error", nil, "id="+o.Id) return NewLocAppError("Post.IsValid", "model.post.is_valid.filenames.app_error", nil, "id="+o.Id)
} }
if utf8.RuneCountInString(ArrayToJson(o.FileIds)) > 150 { if utf8.RuneCountInString(ArrayToJson(o.FileIds)) > POST_FILEIDS_MAX_RUNES {
return NewLocAppError("Post.IsValid", "model.post.is_valid.file_ids.app_error", nil, "id="+o.Id) return NewLocAppError("Post.IsValid", "model.post.is_valid.file_ids.app_error", nil, "id="+o.Id)
} }
if utf8.RuneCountInString(StringInterfaceToJson(o.Props)) > 8000 { if utf8.RuneCountInString(StringInterfaceToJson(o.Props)) > POST_PROPS_MAX_RUNES {
return NewLocAppError("Post.IsValid", "model.post.is_valid.props.app_error", nil, "id="+o.Id) return NewLocAppError("Post.IsValid", "model.post.is_valid.props.app_error", nil, "id="+o.Id)
} }

View File

@ -33,6 +33,7 @@ const (
PREFERENCE_CATEGORY_LAST = "last" PREFERENCE_CATEGORY_LAST = "last"
PREFERENCE_NAME_LAST_CHANNEL = "channel" PREFERENCE_NAME_LAST_CHANNEL = "channel"
PREFERENCE_NAME_LAST_TEAM = "team"
PREFERENCE_CATEGORY_NOTIFICATIONS = "notifications" PREFERENCE_CATEGORY_NOTIFICATIONS = "notifications"
PREFERENCE_NAME_EMAIL_INTERVAL = "email_interval" PREFERENCE_NAME_EMAIL_INTERVAL = "email_interval"

View File

@ -30,6 +30,7 @@ type PushNotification struct {
Message string `json:"message"` Message string `json:"message"`
Badge int `json:"badge"` Badge int `json:"badge"`
ContentAvailable int `json:"cont_ava"` ContentAvailable int `json:"cont_ava"`
TeamId string `json:"team_id"`
ChannelId string `json:"channel_id"` ChannelId string `json:"channel_id"`
ChannelName string `json:"channel_name"` ChannelName string `json:"channel_name"`
Type string `json:"type"` Type string `json:"type"`

View File

@ -0,0 +1,78 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
type Reaction struct {
UserId string `json:"user_id"`
PostId string `json:"post_id"`
EmojiName string `json:"emoji_name"`
CreateAt int64 `json:"create_at"`
}
func (o *Reaction) ToJson() string {
if b, err := json.Marshal(o); err != nil {
return ""
} else {
return string(b)
}
}
func ReactionFromJson(data io.Reader) *Reaction {
var o Reaction
if err := json.NewDecoder(data).Decode(&o); err != nil {
return nil
} else {
return &o
}
}
func ReactionsToJson(o []*Reaction) string {
if b, err := json.Marshal(o); err != nil {
return ""
} else {
return string(b)
}
}
func ReactionsFromJson(data io.Reader) []*Reaction {
var o []*Reaction
if err := json.NewDecoder(data).Decode(&o); err != nil {
return nil
} else {
return o
}
}
func (o *Reaction) IsValid() *AppError {
if len(o.UserId) != 26 {
return NewLocAppError("Reaction.IsValid", "model.reaction.is_valid.user_id.app_error", nil, "user_id="+o.UserId)
}
if len(o.PostId) != 26 {
return NewLocAppError("Reaction.IsValid", "model.reaction.is_valid.post_id.app_error", nil, "post_id="+o.PostId)
}
if len(o.EmojiName) == 0 || len(o.EmojiName) > 64 {
return NewLocAppError("Reaction.IsValid", "model.reaction.is_valid.emoji_name.app_error", nil, "emoji_name="+o.EmojiName)
}
if o.CreateAt == 0 {
return NewLocAppError("Reaction.IsValid", "model.reaction.is_valid.create_at.app_error", nil, "")
}
return nil
}
func (o *Reaction) PreSave() {
if o.CreateAt == 0 {
o.CreateAt = GetMillis()
}
}

View File

@ -22,7 +22,7 @@ type Status struct {
Status string `json:"status"` Status string `json:"status"`
Manual bool `json:"manual"` Manual bool `json:"manual"`
LastActivityAt int64 `json:"last_activity_at"` LastActivityAt int64 `json:"last_activity_at"`
ActiveChannel string `json:"active_channel"` ActiveChannel string `json:"active_channel" db:"-"`
} }
func (o *Status) ToJson() string { func (o *Status) ToJson() string {
@ -44,3 +44,14 @@ func StatusFromJson(data io.Reader) *Status {
return nil return nil
} }
} }
func StatusMapToInterfaceMap(statusMap map[string]*Status) map[string]interface{} {
interfaceMap := map[string]interface{}{}
for _, s := range statusMap {
// Omitted statues mean offline
if s.Status != STATUS_OFFLINE {
interfaceMap[s.UserId] = s.Status
}
}
return interfaceMap
}

View File

@ -24,6 +24,7 @@ type Team struct {
DeleteAt int64 `json:"delete_at"` DeleteAt int64 `json:"delete_at"`
DisplayName string `json:"display_name"` DisplayName string `json:"display_name"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"`
Email string `json:"email"` Email string `json:"email"`
Type string `json:"type"` Type string `json:"type"`
CompanyName string `json:"company_name"` CompanyName string `json:"company_name"`
@ -130,6 +131,10 @@ func (o *Team) IsValid() *AppError {
return NewLocAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id) return NewLocAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id)
} }
if len(o.Description) > 255 {
return NewLocAppError("Team.IsValid", "model.team.is_valid.description.app_error", nil, "id="+o.Id)
}
if IsReservedTeamName(o.Name) { if IsReservedTeamName(o.Name) {
return NewLocAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id) return NewLocAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id)
} }

View File

@ -16,6 +16,12 @@ type TeamMember struct {
DeleteAt int64 `json:"delete_at"` DeleteAt int64 `json:"delete_at"`
} }
type TeamUnread struct {
TeamId string `json:"team_id"`
MsgCount int64 `json:"msg_count"`
MentionCount int64 `json:"mention_count"`
}
func (o *TeamMember) ToJson() string { func (o *TeamMember) ToJson() string {
b, err := json.Marshal(o) b, err := json.Marshal(o)
if err != nil { if err != nil {
@ -55,6 +61,25 @@ func TeamMembersFromJson(data io.Reader) []*TeamMember {
} }
} }
func TeamsUnreadToJson(o []*TeamUnread) string {
if b, err := json.Marshal(o); err != nil {
return "[]"
} else {
return string(b)
}
}
func TeamsUnreadFromJson(data io.Reader) []*TeamUnread {
decoder := json.NewDecoder(data)
var o []*TeamUnread
err := decoder.Decode(&o)
if err == nil {
return o
} else {
return nil
}
}
func (o *TeamMember) IsValid() *AppError { func (o *TeamMember) IsValid() *AppError {
if len(o.TeamId) != 26 { if len(o.TeamId) != 26 {

View File

@ -1,40 +0,0 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"fmt"
"io"
)
type TeamSignup struct {
Team Team `json:"team"`
User User `json:"user"`
Invites []string `json:"invites"`
Data string `json:"data"`
Hash string `json:"hash"`
}
func TeamSignupFromJson(data io.Reader) *TeamSignup {
decoder := json.NewDecoder(data)
var o TeamSignup
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
fmt.Println(err)
return nil
}
}
func (o *TeamSignup) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}

View File

@ -37,6 +37,7 @@ type User struct {
Nickname string `json:"nickname"` Nickname string `json:"nickname"`
FirstName string `json:"first_name"` FirstName string `json:"first_name"`
LastName string `json:"last_name"` LastName string `json:"last_name"`
Position string `json:"position"`
Roles string `json:"roles"` Roles string `json:"roles"`
AllowMarketing bool `json:"allow_marketing,omitempty"` AllowMarketing bool `json:"allow_marketing,omitempty"`
Props StringMap `json:"props,omitempty"` Props StringMap `json:"props,omitempty"`
@ -78,6 +79,10 @@ func (u *User) IsValid() *AppError {
return NewLocAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id) return NewLocAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id)
} }
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.FirstName) > 64 { if utf8.RuneCountInString(u.FirstName) > 64 {
return NewLocAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id) return NewLocAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id)
} }

View File

@ -304,7 +304,7 @@ func Etag(parts ...interface{}) string {
return etag return etag
} }
var validHashtag = regexp.MustCompile(`^(#[A-Za-zäöüÄÖÜß]+[A-Za-z0-9äöüÄÖÜß_\-]*[A-Za-z0-9äöüÄÖÜß])$`) var validHashtag = regexp.MustCompile(`^(#\pL[\pL\d\-_.]*[\pL\d])$`)
var puncStart = regexp.MustCompile(`^[^\pL\d\s#]+`) var puncStart = regexp.MustCompile(`^[^\pL\d\s#]+`)
var hashtagStart = regexp.MustCompile(`^#{2,}`) var hashtagStart = regexp.MustCompile(`^#{2,}`)
var puncEnd = regexp.MustCompile(`[^\pL\d\s]+$`) var puncEnd = regexp.MustCompile(`[^\pL\d\s]+$`)

View File

@ -13,6 +13,7 @@ import (
// It should be maitained in chronological order with most current // It should be maitained in chronological order with most current
// release at the front of the list. // release at the front of the list.
var versions = []string{ var versions = []string{
"3.6.0",
"3.5.0", "3.5.0",
"3.4.0", "3.4.0",
"3.3.0", "3.3.0",

View File

@ -18,6 +18,7 @@ const (
WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added" WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added"
WEBSOCKET_EVENT_NEW_USER = "new_user" WEBSOCKET_EVENT_NEW_USER = "new_user"
WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team" WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team"
WEBSOCKET_EVENT_UPDATE_TEAM = "update_team"
WEBSOCKET_EVENT_USER_ADDED = "user_added" WEBSOCKET_EVENT_USER_ADDED = "user_added"
WEBSOCKET_EVENT_USER_UPDATED = "user_updated" WEBSOCKET_EVENT_USER_UPDATED = "user_updated"
WEBSOCKET_EVENT_USER_REMOVED = "user_removed" WEBSOCKET_EVENT_USER_REMOVED = "user_removed"
@ -27,6 +28,8 @@ const (
WEBSOCKET_EVENT_HELLO = "hello" WEBSOCKET_EVENT_HELLO = "hello"
WEBSOCKET_EVENT_WEBRTC = "webrtc" WEBSOCKET_EVENT_WEBRTC = "webrtc"
WEBSOCKET_AUTHENTICATION_CHALLENGE = "authentication_challenge" WEBSOCKET_AUTHENTICATION_CHALLENGE = "authentication_challenge"
WEBSOCKET_EVENT_REACTION_ADDED = "reaction_added"
WEBSOCKET_EVENT_REACTION_REMOVED = "reaction_removed"
) )
type WebSocketMessage interface { type WebSocketMessage interface {
@ -34,6 +37,7 @@ type WebSocketMessage interface {
IsValid() bool IsValid() bool
DoPreComputeJson() DoPreComputeJson()
GetPreComputeJson() []byte GetPreComputeJson() []byte
EventType() string
} }
type WebsocketBroadcast struct { type WebsocketBroadcast struct {
@ -63,6 +67,10 @@ func (o *WebSocketEvent) IsValid() bool {
return o.Event != "" return o.Event != ""
} }
func (o *WebSocketEvent) EventType() string {
return o.Event
}
func (o *WebSocketEvent) DoPreComputeJson() { func (o *WebSocketEvent) DoPreComputeJson() {
b, err := json.Marshal(o) b, err := json.Marshal(o)
if err != nil { if err != nil {
@ -120,6 +128,10 @@ func (o *WebSocketResponse) IsValid() bool {
return o.Status != "" return o.Status != ""
} }
func (o *WebSocketResponse) EventType() string {
return ""
}
func (o *WebSocketResponse) ToJson() string { func (o *WebSocketResponse) ToJson() string {
b, err := json.Marshal(o) b, err := json.Marshal(o)
if err != nil { if err != nil {

8
vendor/manifest vendored
View File

@ -95,8 +95,8 @@
"importpath": "github.com/mattermost/platform/einterfaces", "importpath": "github.com/mattermost/platform/einterfaces",
"repository": "https://github.com/mattermost/platform", "repository": "https://github.com/mattermost/platform",
"vcs": "git", "vcs": "git",
"revision": "b55ec6148caa93d54b660afe55408c643d217108", "revision": "976296cd52533ff565407e55e872339cc312a0cf",
"branch": "release-3.5", "branch": "release-3.6",
"path": "/einterfaces", "path": "/einterfaces",
"notests": true "notests": true
}, },
@ -104,8 +104,8 @@
"importpath": "github.com/mattermost/platform/model", "importpath": "github.com/mattermost/platform/model",
"repository": "https://github.com/mattermost/platform", "repository": "https://github.com/mattermost/platform",
"vcs": "git", "vcs": "git",
"revision": "b55ec6148caa93d54b660afe55408c643d217108", "revision": "976296cd52533ff565407e55e872339cc312a0cf",
"branch": "release-3.5", "branch": "release-3.6",
"path": "/model", "path": "/model",
"notests": true "notests": true
}, },