Sync with mattermost 3.3.0

This commit is contained in:
Wim 2016-08-15 18:47:31 +02:00
parent a1a11a88b3
commit 24defcb970
26 changed files with 879 additions and 165 deletions

View File

@ -0,0 +1,20 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package einterfaces
import "github.com/mattermost/platform/model"
type AccountMigrationInterface interface {
MigrateToLdap(fromAuthService string, forignUserFieldNameToMatch string) *model.AppError
}
var theAccountMigrationInterface AccountMigrationInterface
func RegisterAccountMigrationInterface(newInterface AccountMigrationInterface) {
theAccountMigrationInterface = newInterface
}
func GetAccountMigrationInterface() AccountMigrationInterface {
return theAccountMigrationInterface
}

View File

@ -0,0 +1,32 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package einterfaces
import (
"github.com/mattermost/platform/model"
)
type ClusterInterface interface {
StartInterNodeCommunication()
StopInterNodeCommunication()
GetClusterInfos() []*model.ClusterInfo
RemoveAllSessionsForUserId(userId string)
InvalidateCacheForUser(userId string)
InvalidateCacheForChannel(channelId string)
Publish(event *model.WebSocketEvent)
UpdateStatus(status *model.Status)
GetLogs() ([]string, *model.AppError)
GetClusterId() string
ConfigChanged(previousConfig *model.Config, newConfig *model.Config, sendToOtherServer bool) *model.AppError
}
var theClusterInterface ClusterInterface
func RegisterClusterInterface(newInterface ClusterInterface) {
theClusterInterface = newInterface
}
func GetClusterInterface() ClusterInterface {
return theClusterInterface
}

View File

@ -15,6 +15,8 @@ type LdapInterface interface {
ValidateFilter(filter string) *model.AppError ValidateFilter(filter string) *model.AppError
Syncronize() *model.AppError Syncronize() *model.AppError
StartLdapSyncJob() StartLdapSyncJob()
SyncNow()
GetAllLdapUsers() ([]*model.User, *model.AppError)
} }
var theLdapInterface LdapInterface var theLdapInterface LdapInterface

View File

@ -15,10 +15,12 @@ const (
) )
type AccessData struct { type AccessData struct {
AuthCode string `json:"auth_code"` ClientId string `json:"client_id"`
UserId string `json:"user_id"`
Token string `json:"token"` Token string `json:"token"`
RefreshToken string `json:"refresh_token"` RefreshToken string `json:"refresh_token"`
RedirectUri string `json:"redirect_uri"` RedirectUri string `json:"redirect_uri"`
ExpiresAt int64 `json:"expires_at"`
} }
type AccessResponse struct { type AccessResponse struct {
@ -33,8 +35,12 @@ type AccessResponse struct {
// correctly. // correctly.
func (ad *AccessData) IsValid() *AppError { func (ad *AccessData) IsValid() *AppError {
if len(ad.AuthCode) == 0 || len(ad.AuthCode) > 128 { if len(ad.ClientId) == 0 || len(ad.ClientId) > 26 {
return NewLocAppError("AccessData.IsValid", "model.access.is_valid.auth_code.app_error", nil, "") return NewLocAppError("AccessData.IsValid", "model.access.is_valid.client_id.app_error", nil, "")
}
if len(ad.UserId) == 0 || len(ad.UserId) > 26 {
return NewLocAppError("AccessData.IsValid", "model.access.is_valid.user_id.app_error", nil, "")
} }
if len(ad.Token) != 26 { if len(ad.Token) != 26 {
@ -52,6 +58,19 @@ func (ad *AccessData) IsValid() *AppError {
return nil return nil
} }
func (me *AccessData) IsExpired() bool {
if me.ExpiresAt <= 0 {
return false
}
if GetMillis() > me.ExpiresAt {
return true
}
return false
}
func (ad *AccessData) ToJson() string { func (ad *AccessData) ToJson() string {
b, err := json.Marshal(ad) b, err := json.Marshal(ad)
if err != nil { if err != nil {

View File

@ -11,6 +11,7 @@ import (
const ( const (
AUTHCODE_EXPIRE_TIME = 60 * 10 // 10 minutes AUTHCODE_EXPIRE_TIME = 60 * 10 // 10 minutes
AUTHCODE_RESPONSE_TYPE = "code" AUTHCODE_RESPONSE_TYPE = "code"
DEFAULT_SCOPE = "user"
) )
type AuthData struct { type AuthData struct {
@ -71,6 +72,10 @@ func (ad *AuthData) PreSave() {
if ad.CreateAt == 0 { if ad.CreateAt == 0 {
ad.CreateAt = GetMillis() ad.CreateAt = GetMillis()
} }
if len(ad.Scope) == 0 {
ad.Scope = DEFAULT_SCOPE
}
} }
func (ad *AuthData) ToJson() string { func (ad *AuthData) ToJson() string {

View File

@ -124,9 +124,6 @@ func (o *Channel) ExtraUpdated() {
o.ExtraUpdateAt = GetMillis() o.ExtraUpdateAt = GetMillis()
} }
func (o *Channel) PreExport() {
}
func GetDMNameFromIds(userId1, userId2 string) string { func GetDMNameFromIds(userId1, userId2 string) string {
if userId1 > userId2 { if userId1 > userId2 {
return userId2 + "__" + userId1 return userId2 + "__" + userId1

View File

@ -20,6 +20,7 @@ import (
const ( const (
HEADER_REQUEST_ID = "X-Request-ID" HEADER_REQUEST_ID = "X-Request-ID"
HEADER_VERSION_ID = "X-Version-ID" HEADER_VERSION_ID = "X-Version-ID"
HEADER_CLUSTER_ID = "X-Cluster-ID"
HEADER_ETAG_SERVER = "ETag" HEADER_ETAG_SERVER = "ETag"
HEADER_ETAG_CLIENT = "If-None-Match" HEADER_ETAG_CLIENT = "If-None-Match"
HEADER_FORWARDED = "X-Forwarded-For" HEADER_FORWARDED = "X-Forwarded-For"
@ -32,6 +33,7 @@ const (
HEADER_REQUESTED_WITH_XML = "XMLHttpRequest" HEADER_REQUESTED_WITH_XML = "XMLHttpRequest"
STATUS = "status" STATUS = "status"
STATUS_OK = "OK" STATUS_OK = "OK"
STATUS_FAIL = "FAIL"
API_URL_SUFFIX_V1 = "/api/v1" API_URL_SUFFIX_V1 = "/api/v1"
API_URL_SUFFIX_V3 = "/api/v3" API_URL_SUFFIX_V3 = "/api/v3"
@ -276,6 +278,9 @@ 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) { func (c *Client) SignupTeam(email string, displayName string) (*Result, *AppError) {
m := make(map[string]string) m := make(map[string]string)
m["email"] = email m["email"] = email
@ -289,6 +294,8 @@ func (c *Client) SignupTeam(email string, displayName string) (*Result, *AppErro
} }
} }
// 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) { func (c *Client) CreateTeamFromSignup(teamSignup *TeamSignup) (*Result, *AppError) {
if r, err := c.DoApiPost("/teams/create_from_signup", teamSignup.ToJson()); err != nil { if r, err := c.DoApiPost("/teams/create_from_signup", teamSignup.ToJson()); err != nil {
return nil, err return nil, err
@ -299,6 +306,8 @@ func (c *Client) CreateTeamFromSignup(teamSignup *TeamSignup) (*Result, *AppErro
} }
} }
// 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.
func (c *Client) CreateTeam(team *Team) (*Result, *AppError) { func (c *Client) CreateTeam(team *Team) (*Result, *AppError) {
if r, err := c.DoApiPost("/teams/create", team.ToJson()); err != nil { if r, err := c.DoApiPost("/teams/create", team.ToJson()); err != nil {
return nil, err return nil, err
@ -309,6 +318,7 @@ func (c *Client) CreateTeam(team *Team) (*Result, *AppError) {
} }
} }
// GetAllTeams returns a map of all teams using team ids as the key.
func (c *Client) GetAllTeams() (*Result, *AppError) { func (c *Client) GetAllTeams() (*Result, *AppError) {
if r, err := c.DoApiGet("/teams/all", "", ""); err != nil { if r, err := c.DoApiGet("/teams/all", "", ""); err != nil {
return nil, err return nil, err
@ -319,6 +329,8 @@ func (c *Client) GetAllTeams() (*Result, *AppError) {
} }
} }
// GetAllTeamListings returns a map of all teams that are available to join
// using team ids as the key. Must be authenticated.
func (c *Client) GetAllTeamListings() (*Result, *AppError) { func (c *Client) GetAllTeamListings() (*Result, *AppError) {
if r, err := c.DoApiGet("/teams/all_team_listings", "", ""); err != nil { if r, err := c.DoApiGet("/teams/all_team_listings", "", ""); err != nil {
return nil, err return nil, err
@ -329,6 +341,8 @@ func (c *Client) GetAllTeamListings() (*Result, *AppError) {
} }
} }
// FindTeamByName returns the strings "true" or "false" depending on if a team
// with the provided name was found.
func (c *Client) FindTeamByName(name string) (*Result, *AppError) { func (c *Client) FindTeamByName(name string) (*Result, *AppError) {
m := make(map[string]string) m := make(map[string]string)
m["name"] = name m["name"] = name
@ -365,6 +379,8 @@ func (c *Client) AddUserToTeam(teamId string, userId string) (*Result, *AppError
} }
} }
// AddUserToTeamFromInvite adds a user to a team based off data provided in an invite link.
// Either hash and dataToHash are required or inviteId is required.
func (c *Client) AddUserToTeamFromInvite(hash, dataToHash, inviteId string) (*Result, *AppError) { func (c *Client) AddUserToTeamFromInvite(hash, dataToHash, inviteId string) (*Result, *AppError) {
data := make(map[string]string) data := make(map[string]string)
data["hash"] = hash data["hash"] = hash
@ -409,6 +425,9 @@ func (c *Client) InviteMembers(invites *Invites) (*Result, *AppError) {
} }
} }
// UpdateTeam updates a team based on the changes in the provided team struct. On success
// it returns a sanitized version of the updated team. Must be authenticated as a team admin
// for that team or a system admin.
func (c *Client) UpdateTeam(team *Team) (*Result, *AppError) { func (c *Client) UpdateTeam(team *Team) (*Result, *AppError) {
if r, err := c.DoApiPost(c.GetTeamRoute()+"/update", team.ToJson()); err != nil { if r, err := c.DoApiPost(c.GetTeamRoute()+"/update", team.ToJson()); err != nil {
return nil, err return nil, err
@ -419,6 +438,9 @@ func (c *Client) UpdateTeam(team *Team) (*Result, *AppError) {
} }
} }
// User Routes Section
// CreateUser creates a user in the system based on the provided user struct.
func (c *Client) CreateUser(user *User, hash string) (*Result, *AppError) { func (c *Client) CreateUser(user *User, hash string) (*Result, *AppError) {
if r, err := c.DoApiPost("/users/create", user.ToJson()); err != nil { if r, err := c.DoApiPost("/users/create", user.ToJson()); err != nil {
return nil, err return nil, err
@ -429,6 +451,8 @@ func (c *Client) CreateUser(user *User, hash string) (*Result, *AppError) {
} }
} }
// CreateUserWithInvite creates a user based on the provided user struct. Either the hash and
// data strings or the inviteId is required from the invite.
func (c *Client) CreateUserWithInvite(user *User, hash string, data string, inviteId string) (*Result, *AppError) { func (c *Client) CreateUserWithInvite(user *User, hash string, data string, inviteId string) (*Result, *AppError) {
url := "/users/create?d=" + url.QueryEscape(data) + "&h=" + url.QueryEscape(hash) + "&iid=" + url.QueryEscape(inviteId) url := "/users/create?d=" + url.QueryEscape(data) + "&h=" + url.QueryEscape(hash) + "&iid=" + url.QueryEscape(inviteId)
@ -452,6 +476,7 @@ func (c *Client) CreateUserFromSignup(user *User, data string, hash string) (*Re
} }
} }
// GetUser returns a user based on a provided user id string. Must be authenticated.
func (c *Client) GetUser(id string, etag string) (*Result, *AppError) { func (c *Client) GetUser(id string, etag string) (*Result, *AppError) {
if r, err := c.DoApiGet("/users/"+id+"/get", "", etag); err != nil { if r, err := c.DoApiGet("/users/"+id+"/get", "", etag); err != nil {
return nil, err return nil, err
@ -462,6 +487,7 @@ func (c *Client) GetUser(id string, etag string) (*Result, *AppError) {
} }
} }
// 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 {
return nil, err return nil, err
@ -472,6 +498,8 @@ func (c *Client) GetMe(etag string) (*Result, *AppError) {
} }
} }
// GetProfilesForDirectMessageList returns a map of users for a team that can be direct
// messaged, using user id as the key. Must be authenticated.
func (c *Client) GetProfilesForDirectMessageList(teamId string) (*Result, *AppError) { func (c *Client) GetProfilesForDirectMessageList(teamId string) (*Result, *AppError) {
if r, err := c.DoApiGet("/users/profiles_for_dm_list/"+teamId, "", ""); err != nil { if r, err := c.DoApiGet("/users/profiles_for_dm_list/"+teamId, "", ""); err != nil {
return nil, err return nil, err
@ -482,6 +510,8 @@ func (c *Client) GetProfilesForDirectMessageList(teamId string) (*Result, *AppEr
} }
} }
// GetProfiles returns a map of users for a team using user id as the key. Must
// be authenticated.
func (c *Client) GetProfiles(teamId string, etag string) (*Result, *AppError) { func (c *Client) GetProfiles(teamId string, etag string) (*Result, *AppError) {
if r, err := c.DoApiGet("/users/profiles/"+teamId, "", etag); err != nil { if r, err := c.DoApiGet("/users/profiles/"+teamId, "", etag); err != nil {
return nil, err return nil, err
@ -492,6 +522,8 @@ func (c *Client) GetProfiles(teamId string, etag string) (*Result, *AppError) {
} }
} }
// GetDirectProfiles gets a map of users that are currently shown in the sidebar,
// using user id as the key. Must be authenticated.
func (c *Client) GetDirectProfiles(etag string) (*Result, *AppError) { func (c *Client) GetDirectProfiles(etag string) (*Result, *AppError) {
if r, err := c.DoApiGet("/users/direct_profiles", "", etag); err != nil { if r, err := c.DoApiGet("/users/direct_profiles", "", etag); err != nil {
return nil, err return nil, err
@ -502,6 +534,7 @@ func (c *Client) GetDirectProfiles(etag string) (*Result, *AppError) {
} }
} }
// 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)
m["id"] = id m["id"] = id
@ -509,6 +542,8 @@ func (c *Client) LoginById(id string, password string) (*Result, *AppError) {
return c.login(m) return c.login(m)
} }
// Login authenticates a user by login id, which can be username, email or some sort
// of SSO identifier based on configuration, and a password.
func (c *Client) Login(loginId string, password string) (*Result, *AppError) { func (c *Client) Login(loginId string, password string) (*Result, *AppError) {
m := make(map[string]string) m := make(map[string]string)
m["login_id"] = loginId m["login_id"] = loginId
@ -516,6 +551,7 @@ func (c *Client) Login(loginId string, password string) (*Result, *AppError) {
return c.login(m) return c.login(m)
} }
// LoginByLdap authenticates a user by LDAP id and password.
func (c *Client) LoginByLdap(loginId string, password string) (*Result, *AppError) { func (c *Client) LoginByLdap(loginId string, password string) (*Result, *AppError) {
m := make(map[string]string) m := make(map[string]string)
m["login_id"] = loginId m["login_id"] = loginId
@ -524,6 +560,9 @@ func (c *Client) LoginByLdap(loginId string, password string) (*Result, *AppErro
return c.login(m) 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 *Client) LoginWithDevice(loginId string, password string, deviceId string) (*Result, *AppError) { func (c *Client) LoginWithDevice(loginId string, password string, deviceId string) (*Result, *AppError) {
m := make(map[string]string) m := make(map[string]string)
m["login_id"] = loginId m["login_id"] = loginId
@ -550,6 +589,7 @@ func (c *Client) login(m map[string]string) (*Result, *AppError) {
} }
} }
// Logout terminates the current user's session.
func (c *Client) Logout() (*Result, *AppError) { func (c *Client) Logout() (*Result, *AppError) {
if r, err := c.DoApiPost("/users/logout", ""); err != nil { if r, err := c.DoApiPost("/users/logout", ""); err != nil {
return nil, err return nil, err
@ -564,6 +604,9 @@ func (c *Client) Logout() (*Result, *AppError) {
} }
} }
// CheckMfa returns a map with key "mfa_required" with the string value "true" or "false",
// indicating whether MFA is required to log the user in, based on a provided login id
// (username, email or some sort of SSO identifier based on configuration).
func (c *Client) CheckMfa(loginId string) (*Result, *AppError) { func (c *Client) CheckMfa(loginId string) (*Result, *AppError) {
m := make(map[string]string) m := make(map[string]string)
m["login_id"] = loginId m["login_id"] = loginId
@ -577,6 +620,8 @@ func (c *Client) CheckMfa(loginId string) (*Result, *AppError) {
} }
} }
// GenerateMfaQrCode returns a QR code imagem containing the secret, to be scanned
// by a multi-factor authentication mobile application. Must be authenticated.
func (c *Client) GenerateMfaQrCode() (*Result, *AppError) { func (c *Client) GenerateMfaQrCode() (*Result, *AppError) {
if r, err := c.DoApiGet("/users/generate_mfa_qr", "", ""); err != nil { if r, err := c.DoApiGet("/users/generate_mfa_qr", "", ""); err != nil {
return nil, err return nil, err
@ -587,6 +632,9 @@ func (c *Client) GenerateMfaQrCode() (*Result, *AppError) {
} }
} }
// UpdateMfa activates multi-factor authenticates for the current user if activate
// is true and a valid token is provided. If activate is false, then token is not
// required and multi-factor authentication is disabled for the current user.
func (c *Client) UpdateMfa(activate bool, token string) (*Result, *AppError) { func (c *Client) UpdateMfa(activate bool, token string) (*Result, *AppError) {
m := make(map[string]interface{}) m := make(map[string]interface{})
m["activate"] = activate m["activate"] = activate
@ -761,6 +809,15 @@ func (c *Client) GetLogs() (*Result, *AppError) {
} }
} }
func (c *Client) GetClusterStatus() ([]*ClusterInfo, *AppError) {
if r, err := c.DoApiGet("/admin/cluster_status", "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return ClusterInfosFromJson(r.Body), nil
}
}
func (c *Client) GetAllAudits() (*Result, *AppError) { func (c *Client) GetAllAudits() (*Result, *AppError) {
if r, err := c.DoApiGet("/admin/audits", "", ""); err != nil { if r, err := c.DoApiGet("/admin/audits", "", ""); err != nil {
return nil, err return nil, err
@ -1181,6 +1238,18 @@ func (c *Client) SearchPosts(terms string, isOrSearch bool) (*Result, *AppError)
} }
} }
// GetFlaggedPosts will return a post list of posts that have been flagged by the user.
// The page is set by the integer parameters offset and limit.
func (c *Client) GetFlaggedPosts(offset int, limit int) (*Result, *AppError) {
if r, err := c.DoApiGet(c.GetTeamRoute()+fmt.Sprintf("/posts/flagged/%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), PostListFromJson(r.Body)}, nil
}
}
func (c *Client) UploadProfileFile(data []byte, contentType string) (*Result, *AppError) { func (c *Client) UploadProfileFile(data []byte, contentType string) (*Result, *AppError) {
return c.uploadFile(c.ApiUrl+"/users/newimage", data, contentType) return c.uploadFile(c.ApiUrl+"/users/newimage", data, contentType)
} }
@ -1368,8 +1437,9 @@ func (c *Client) AdminResetPassword(userId, newPassword string) (*Result, *AppEr
} }
} }
func (c *Client) GetStatuses(data []string) (*Result, *AppError) { // GetStatuses returns a map of string statuses using user id as the key
if r, err := c.DoApiPost("/users/status", ArrayToJson(data)); err != nil { func (c *Client) GetStatuses() (*Result, *AppError) {
if r, err := c.DoApiGet("/users/status", "", ""); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
@ -1398,6 +1468,8 @@ func (c *Client) GetTeamMembers(teamId string) (*Result, *AppError) {
} }
} }
// RegisterApp creates a new OAuth2 app to be used with the OAuth2 Provider. On success
// it returns the created app. Must be authenticated as a user.
func (c *Client) RegisterApp(app *OAuthApp) (*Result, *AppError) { func (c *Client) RegisterApp(app *OAuthApp) (*Result, *AppError) {
if r, err := c.DoApiPost("/oauth/register", app.ToJson()); err != nil { if r, err := c.DoApiPost("/oauth/register", app.ToJson()); err != nil {
return nil, err return nil, err
@ -1408,6 +1480,9 @@ func (c *Client) RegisterApp(app *OAuthApp) (*Result, *AppError) {
} }
} }
// AllowOAuth allows a new session by an OAuth2 App. On success
// it returns the url to be redirected back to the app which initiated the oauth2 flow.
// Must be authenticated as a user.
func (c *Client) AllowOAuth(rspType, clientId, redirect, scope, state string) (*Result, *AppError) { func (c *Client) AllowOAuth(rspType, clientId, redirect, scope, state string) (*Result, *AppError) {
if r, err := c.DoApiGet("/oauth/allow?response_type="+rspType+"&client_id="+clientId+"&redirect_uri="+url.QueryEscape(redirect)+"&scope="+scope+"&state="+url.QueryEscape(state), "", ""); err != nil { if r, err := c.DoApiGet("/oauth/allow?response_type="+rspType+"&client_id="+clientId+"&redirect_uri="+url.QueryEscape(redirect)+"&scope="+scope+"&state="+url.QueryEscape(state), "", ""); err != nil {
return nil, err return nil, err
@ -1418,8 +1493,47 @@ func (c *Client) AllowOAuth(rspType, clientId, redirect, scope, state string) (*
} }
} }
// GetOAuthAppsByUser returns the OAuth2 Apps registered by the user. On success
// it returns a list of OAuth2 Apps from the same user or all the registered apps if the user
// is a System Administrator. Must be authenticated as a user.
func (c *Client) GetOAuthAppsByUser() (*Result, *AppError) {
if r, err := c.DoApiGet("/oauth/list", "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), OAuthAppListFromJson(r.Body)}, nil
}
}
// GetOAuthAppInfo lookup an OAuth2 App using the client_id. On success
// it returns a Sanitized OAuth2 App. Must be authenticated as a user.
func (c *Client) GetOAuthAppInfo(clientId string) (*Result, *AppError) {
if r, err := c.DoApiGet("/oauth/app/"+clientId, "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), OAuthAppFromJson(r.Body)}, nil
}
}
// DeleteOAuthApp deletes an OAuth2 app, the app must be deleted by the same user who created it or
// a System Administrator. On success returs Status OK. Must be authenticated as a user.
func (c *Client) DeleteOAuthApp(id string) (*Result, *AppError) {
data := make(map[string]string)
data["id"] = id
if r, err := c.DoApiPost("/oauth/delete", MapToJson(data)); 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
}
}
func (c *Client) GetAccessToken(data url.Values) (*Result, *AppError) { func (c *Client) GetAccessToken(data url.Values) (*Result, *AppError) {
if r, err := c.DoApiPost("/oauth/access_token", data.Encode()); err != nil { if r, err := c.DoPost("/oauth/access_token", data.Encode(), "application/x-www-form-urlencoded"); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
@ -1509,6 +1623,16 @@ func (c *Client) GetPreferenceCategory(category string) (*Result, *AppError) {
} }
} }
// DeletePreferences deletes a list of preferences owned by the current user. If successful,
// it will return status=ok. Otherwise, an error will be returned.
func (c *Client) DeletePreferences(preferences *Preferences) (bool, *AppError) {
if r, err := c.DoApiPost("/preferences/delete", preferences.ToJson()); err != nil {
return false, err
} else {
return c.CheckStatusOK(r), nil
}
}
func (c *Client) CreateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppError) { func (c *Client) CreateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppError) {
if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/create", hook.ToJson()); err != nil { if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/create", hook.ToJson()); err != nil {
return nil, err return nil, err
@ -1648,3 +1772,47 @@ func (c *Client) DeleteEmoji(id string) (bool, *AppError) {
func (c *Client) GetCustomEmojiImageUrl(id string) string { func (c *Client) GetCustomEmojiImageUrl(id string) string {
return c.GetEmojiRoute() + "/" + id return c.GetEmojiRoute() + "/" + id
} }
// Uploads a x509 base64 Certificate or Private Key file to be used with SAML.
// data byte array is required and needs to be a Multi-Part with 'certificate' as the field name
// contentType is also required. Returns nil if succesful, otherwise returns an AppError
func (c *Client) UploadCertificateFile(data []byte, contentType string) *AppError {
url := c.ApiUrl + "/admin/add_certificate"
rq, _ := http.NewRequest("POST", url, bytes.NewReader(data))
rq.Header.Set("Content-Type", contentType)
if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
}
if rp, err := c.HttpClient.Do(rq); err != nil {
return NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error())
} else if rp.StatusCode >= 300 {
return AppErrorFromJson(rp.Body)
} else {
defer closeBody(rp)
return nil
}
}
// Removes a x509 base64 Certificate or Private Key file used with SAML.
// filename is required. Returns nil if successful, otherwise returns an AppError
func (c *Client) RemoveCertificateFile(filename string) *AppError {
if r, err := c.DoApiPost("/admin/remove_certificate", MapToJson(map[string]string{"filename": filename})); err != nil {
return err
} else {
defer closeBody(r)
return nil
}
}
// Checks if the x509 base64 Certificates and Private Key files used with SAML exists on the file system.
// Returns a map[string]interface{} if successful, otherwise returns an AppError. Must be System Admin authenticated.
func (c *Client) SamlCertificateStatus(filename string) (map[string]interface{}, *AppError) {
if r, err := c.DoApiGet("/admin/remove_certificate", "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return StringInterfaceFromJson(r.Body), nil
}
}

View File

@ -0,0 +1,66 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
type ClusterInfo struct {
Id string `json:"id"`
Version string `json:"version"`
ConfigHash string `json:"config_hash"`
InterNodeUrl string `json:"internode_url"`
Hostname string `json:"hostname"`
LastSuccessfulPing int64 `json:"last_ping"`
IsAlive bool `json:"is_alive"`
}
func (me *ClusterInfo) ToJson() string {
b, err := json.Marshal(me)
if err != nil {
return ""
} else {
return string(b)
}
}
func ClusterInfoFromJson(data io.Reader) *ClusterInfo {
decoder := json.NewDecoder(data)
var me ClusterInfo
err := decoder.Decode(&me)
if err == nil {
return &me
} else {
return nil
}
}
func (me *ClusterInfo) HaveEstablishedInitialContact() bool {
if me.Id != "" {
return true
}
return false
}
func ClusterInfosToJson(objmap []*ClusterInfo) string {
if b, err := json.Marshal(objmap); err != nil {
return ""
} else {
return string(b)
}
}
func ClusterInfosFromJson(data io.Reader) []*ClusterInfo {
decoder := json.NewDecoder(data)
var objmap []*ClusterInfo
if err := decoder.Decode(&objmap); err != nil {
return make([]*ClusterInfo, 0)
} else {
return objmap
}
}

View File

@ -6,6 +6,7 @@ package model
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"net/url"
) )
const ( const (
@ -22,8 +23,9 @@ const (
PASSWORD_MAXIMUM_LENGTH = 64 PASSWORD_MAXIMUM_LENGTH = 64
PASSWORD_MINIMUM_LENGTH = 5 PASSWORD_MINIMUM_LENGTH = 5
SERVICE_GITLAB = "gitlab" SERVICE_GITLAB = "gitlab"
SERVICE_GOOGLE = "google" SERVICE_GOOGLE = "google"
SERVICE_OFFICE365 = "office365"
WEBSERVER_MODE_REGULAR = "regular" WEBSERVER_MODE_REGULAR = "regular"
WEBSERVER_MODE_GZIP = "gzip" WEBSERVER_MODE_GZIP = "gzip"
@ -44,9 +46,12 @@ const (
RESTRICT_EMOJI_CREATION_ALL = "all" RESTRICT_EMOJI_CREATION_ALL = "all"
RESTRICT_EMOJI_CREATION_ADMIN = "admin" RESTRICT_EMOJI_CREATION_ADMIN = "admin"
RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN = "system_admin" RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN = "system_admin"
SITENAME_MAX_LENGTH = 30
) )
type ServiceSettings struct { type ServiceSettings struct {
SiteURL *string
ListenAddress string ListenAddress string
MaximumLoginAttempts int MaximumLoginAttempts int
SegmentDeveloperKey string SegmentDeveloperKey string
@ -75,6 +80,12 @@ type ServiceSettings struct {
RestrictCustomEmojiCreation *string RestrictCustomEmojiCreation *string
} }
type ClusterSettings struct {
Enable *bool
InterNodeListenAddress *string
InterNodeUrls []string
}
type SSOSettings struct { type SSOSettings struct {
Enable bool Enable bool
Secret string Secret string
@ -189,10 +200,12 @@ type TeamSettings struct {
RestrictTeamNames *bool RestrictTeamNames *bool
EnableCustomBrand *bool EnableCustomBrand *bool
CustomBrandText *string CustomBrandText *string
CustomDescriptionText *string
RestrictDirectMessage *string RestrictDirectMessage *string
RestrictTeamInvite *string RestrictTeamInvite *string
RestrictPublicChannelManagement *string RestrictPublicChannelManagement *string
RestrictPrivateChannelManagement *string RestrictPrivateChannelManagement *string
UserStatusAwayTimeout *int64
} }
type LdapSettings struct { type LdapSettings struct {
@ -265,6 +278,12 @@ type SamlSettings struct {
LoginButtonText *string LoginButtonText *string
} }
type NativeAppSettings struct {
AppDownloadLink *string
AndroidAppDownloadLink *string
IosAppDownloadLink *string
}
type Config struct { type Config struct {
ServiceSettings ServiceSettings ServiceSettings ServiceSettings
TeamSettings TeamSettings TeamSettings TeamSettings
@ -278,10 +297,13 @@ type Config struct {
SupportSettings SupportSettings SupportSettings SupportSettings
GitLabSettings SSOSettings GitLabSettings SSOSettings
GoogleSettings SSOSettings GoogleSettings SSOSettings
Office365Settings SSOSettings
LdapSettings LdapSettings LdapSettings LdapSettings
ComplianceSettings ComplianceSettings ComplianceSettings ComplianceSettings
LocalizationSettings LocalizationSettings LocalizationSettings LocalizationSettings
SamlSettings SamlSettings SamlSettings SamlSettings
NativeAppSettings NativeAppSettings
ClusterSettings ClusterSettings
} }
func (o *Config) ToJson() string { func (o *Config) ToJson() string {
@ -299,6 +321,8 @@ func (o *Config) GetSSOService(service string) *SSOSettings {
return &o.GitLabSettings return &o.GitLabSettings
case SERVICE_GOOGLE: case SERVICE_GOOGLE:
return &o.GoogleSettings return &o.GoogleSettings
case SERVICE_OFFICE365:
return &o.Office365Settings
} }
return nil return nil
@ -348,6 +372,11 @@ func (o *Config) SetDefaults() {
o.EmailSettings.PasswordResetSalt = NewRandomString(32) o.EmailSettings.PasswordResetSalt = NewRandomString(32)
} }
if o.ServiceSettings.SiteURL == nil {
o.ServiceSettings.SiteURL = new(string)
*o.ServiceSettings.SiteURL = ""
}
if o.ServiceSettings.EnableDeveloper == nil { if o.ServiceSettings.EnableDeveloper == nil {
o.ServiceSettings.EnableDeveloper = new(bool) o.ServiceSettings.EnableDeveloper = new(bool)
*o.ServiceSettings.EnableDeveloper = false *o.ServiceSettings.EnableDeveloper = false
@ -408,6 +437,11 @@ func (o *Config) SetDefaults() {
*o.TeamSettings.CustomBrandText = "" *o.TeamSettings.CustomBrandText = ""
} }
if o.TeamSettings.CustomDescriptionText == nil {
o.TeamSettings.CustomDescriptionText = new(string)
*o.TeamSettings.CustomDescriptionText = ""
}
if o.TeamSettings.EnableOpenServer == nil { if o.TeamSettings.EnableOpenServer == nil {
o.TeamSettings.EnableOpenServer = new(bool) o.TeamSettings.EnableOpenServer = new(bool)
*o.TeamSettings.EnableOpenServer = false *o.TeamSettings.EnableOpenServer = false
@ -433,6 +467,11 @@ func (o *Config) SetDefaults() {
*o.TeamSettings.RestrictPrivateChannelManagement = PERMISSIONS_ALL *o.TeamSettings.RestrictPrivateChannelManagement = PERMISSIONS_ALL
} }
if o.TeamSettings.UserStatusAwayTimeout == nil {
o.TeamSettings.UserStatusAwayTimeout = new(int64)
*o.TeamSettings.UserStatusAwayTimeout = 300
}
if o.EmailSettings.EnableSignInWithEmail == nil { if o.EmailSettings.EnableSignInWithEmail == nil {
o.EmailSettings.EnableSignInWithEmail = new(bool) o.EmailSettings.EnableSignInWithEmail = new(bool)
@ -474,7 +513,7 @@ func (o *Config) SetDefaults() {
if o.SupportSettings.TermsOfServiceLink == nil { if o.SupportSettings.TermsOfServiceLink == nil {
o.SupportSettings.TermsOfServiceLink = new(string) o.SupportSettings.TermsOfServiceLink = new(string)
*o.SupportSettings.TermsOfServiceLink = "/static/help/terms.html" *o.SupportSettings.TermsOfServiceLink = "https://about.mattermost.com/default-terms/"
} }
if !IsSafeLink(o.SupportSettings.PrivacyPolicyLink) { if !IsSafeLink(o.SupportSettings.PrivacyPolicyLink) {
@ -483,7 +522,7 @@ func (o *Config) SetDefaults() {
if o.SupportSettings.PrivacyPolicyLink == nil { if o.SupportSettings.PrivacyPolicyLink == nil {
o.SupportSettings.PrivacyPolicyLink = new(string) o.SupportSettings.PrivacyPolicyLink = new(string)
*o.SupportSettings.PrivacyPolicyLink = "/static/help/privacy.html" *o.SupportSettings.PrivacyPolicyLink = ""
} }
if !IsSafeLink(o.SupportSettings.AboutLink) { if !IsSafeLink(o.SupportSettings.AboutLink) {
@ -492,7 +531,7 @@ func (o *Config) SetDefaults() {
if o.SupportSettings.AboutLink == nil { if o.SupportSettings.AboutLink == nil {
o.SupportSettings.AboutLink = new(string) o.SupportSettings.AboutLink = new(string)
*o.SupportSettings.AboutLink = "/static/help/about.html" *o.SupportSettings.AboutLink = ""
} }
if !IsSafeLink(o.SupportSettings.HelpLink) { if !IsSafeLink(o.SupportSettings.HelpLink) {
@ -501,7 +540,7 @@ func (o *Config) SetDefaults() {
if o.SupportSettings.HelpLink == nil { if o.SupportSettings.HelpLink == nil {
o.SupportSettings.HelpLink = new(string) o.SupportSettings.HelpLink = new(string)
*o.SupportSettings.HelpLink = "/static/help/help.html" *o.SupportSettings.HelpLink = ""
} }
if !IsSafeLink(o.SupportSettings.ReportAProblemLink) { if !IsSafeLink(o.SupportSettings.ReportAProblemLink) {
@ -510,7 +549,7 @@ func (o *Config) SetDefaults() {
if o.SupportSettings.ReportAProblemLink == nil { if o.SupportSettings.ReportAProblemLink == nil {
o.SupportSettings.ReportAProblemLink = new(string) o.SupportSettings.ReportAProblemLink = new(string)
*o.SupportSettings.ReportAProblemLink = "/static/help/report_problem.html" *o.SupportSettings.ReportAProblemLink = ""
} }
if o.SupportSettings.SupportEmail == nil { if o.SupportSettings.SupportEmail == nil {
@ -675,6 +714,20 @@ func (o *Config) SetDefaults() {
*o.ServiceSettings.RestrictCustomEmojiCreation = RESTRICT_EMOJI_CREATION_ALL *o.ServiceSettings.RestrictCustomEmojiCreation = RESTRICT_EMOJI_CREATION_ALL
} }
if o.ClusterSettings.InterNodeListenAddress == nil {
o.ClusterSettings.InterNodeListenAddress = new(string)
*o.ClusterSettings.InterNodeListenAddress = ":8075"
}
if o.ClusterSettings.Enable == nil {
o.ClusterSettings.Enable = new(bool)
*o.ClusterSettings.Enable = false
}
if o.ClusterSettings.InterNodeUrls == nil {
o.ClusterSettings.InterNodeUrls = []string{}
}
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
@ -784,6 +837,21 @@ func (o *Config) SetDefaults() {
o.SamlSettings.LocaleAttribute = new(string) o.SamlSettings.LocaleAttribute = new(string)
*o.SamlSettings.LocaleAttribute = "" *o.SamlSettings.LocaleAttribute = ""
} }
if o.NativeAppSettings.AppDownloadLink == nil {
o.NativeAppSettings.AppDownloadLink = new(string)
*o.NativeAppSettings.AppDownloadLink = "https://about.mattermost.com/downloads/"
}
if o.NativeAppSettings.AndroidAppDownloadLink == nil {
o.NativeAppSettings.AndroidAppDownloadLink = new(string)
*o.NativeAppSettings.AndroidAppDownloadLink = "https://about.mattermost.com/mattermost-android-app/"
}
if o.NativeAppSettings.IosAppDownloadLink == nil {
o.NativeAppSettings.IosAppDownloadLink = new(string)
*o.NativeAppSettings.IosAppDownloadLink = "https://about.mattermost.com/mattermost-ios-app/"
}
} }
func (o *Config) IsValid() *AppError { func (o *Config) IsValid() *AppError {
@ -792,6 +860,12 @@ func (o *Config) IsValid() *AppError {
return NewLocAppError("Config.IsValid", "model.config.is_valid.login_attempts.app_error", nil, "") return NewLocAppError("Config.IsValid", "model.config.is_valid.login_attempts.app_error", nil, "")
} }
if len(*o.ServiceSettings.SiteURL) != 0 {
if _, err := url.ParseRequestURI(*o.ServiceSettings.SiteURL); err != nil {
return NewLocAppError("Config.IsValid", "model.config.is_valid.site_url.app_error", nil, "")
}
}
if len(o.ServiceSettings.ListenAddress) == 0 { if len(o.ServiceSettings.ListenAddress) == 0 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.listen_address.app_error", nil, "") return NewLocAppError("Config.IsValid", "model.config.is_valid.listen_address.app_error", nil, "")
} }
@ -893,21 +967,37 @@ func (o *Config) IsValid() *AppError {
} }
if *o.LdapSettings.Enable { if *o.LdapSettings.Enable {
if *o.LdapSettings.LdapServer == "" || if *o.LdapSettings.LdapServer == "" {
*o.LdapSettings.BaseDN == "" || return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_server", nil, "")
*o.LdapSettings.BindUsername == "" || }
*o.LdapSettings.BindPassword == "" ||
*o.LdapSettings.FirstNameAttribute == "" || if *o.LdapSettings.BaseDN == "" {
*o.LdapSettings.LastNameAttribute == "" || return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_basedn", nil, "")
*o.LdapSettings.EmailAttribute == "" || }
*o.LdapSettings.UsernameAttribute == "" ||
*o.LdapSettings.IdAttribute == "" { if *o.LdapSettings.FirstNameAttribute == "" {
return NewLocAppError("Config.IsValid", "Required LDAP field missing", nil, "") return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_firstname", nil, "")
}
if *o.LdapSettings.LastNameAttribute == "" {
return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_lastname", nil, "")
}
if *o.LdapSettings.EmailAttribute == "" {
return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_email", nil, "")
}
if *o.LdapSettings.UsernameAttribute == "" {
return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_username", nil, "")
}
if *o.LdapSettings.IdAttribute == "" {
return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_id", nil, "")
} }
} }
if *o.SamlSettings.Enable { if *o.SamlSettings.Enable {
if len(*o.SamlSettings.IdpUrl) == 0 { if len(*o.SamlSettings.IdpUrl) == 0 || !IsValidHttpUrl(*o.SamlSettings.IdpUrl) {
return NewLocAppError("Config.IsValid", "model.config.is_valid.saml_idp_url.app_error", nil, "") return NewLocAppError("Config.IsValid", "model.config.is_valid.saml_idp_url.app_error", nil, "")
} }
@ -960,6 +1050,10 @@ func (o *Config) IsValid() *AppError {
return NewLocAppError("Config.IsValid", "model.config.is_valid.password_length.app_error", map[string]interface{}{"MinLength": PASSWORD_MINIMUM_LENGTH, "MaxLength": PASSWORD_MAXIMUM_LENGTH}, "") return NewLocAppError("Config.IsValid", "model.config.is_valid.password_length.app_error", map[string]interface{}{"MinLength": PASSWORD_MINIMUM_LENGTH, "MaxLength": PASSWORD_MAXIMUM_LENGTH}, "")
} }
if len(o.TeamSettings.SiteName) > SITENAME_MAX_LENGTH {
return NewLocAppError("Config.IsValid", "model.config.is_valid.sitename_length.app_error", map[string]interface{}{"MaxLength": SITENAME_MAX_LENGTH}, "")
}
return nil return nil
} }

View File

@ -84,6 +84,12 @@ func (task *ScheduledTask) Cancel() {
removeTaskByName(task.Name) removeTaskByName(task.Name)
} }
// Executes the task immediatly. A recurring task will be run regularally after interval.
func (task *ScheduledTask) Execute() {
task.function()
task.timer.Reset(task.Interval)
}
func (task *ScheduledTask) String() string { func (task *ScheduledTask) String() string {
return fmt.Sprintf( return fmt.Sprintf(
"%s\nInterval: %s\nRecurring: %t\n", "%s\nInterval: %s\nRecurring: %t\n",

View File

@ -35,8 +35,10 @@ type Features struct {
Users *int `json:"users"` Users *int `json:"users"`
LDAP *bool `json:"ldap"` LDAP *bool `json:"ldap"`
MFA *bool `json:"mfa"` MFA *bool `json:"mfa"`
GoogleSSO *bool `json:"google_sso"` GoogleOAuth *bool `json:"google_oauth"`
Office365OAuth *bool `json:"office365_oauth"`
Compliance *bool `json:"compliance"` Compliance *bool `json:"compliance"`
Cluster *bool `json:"cluster"`
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"`
@ -65,9 +67,14 @@ func (f *Features) SetDefaults() {
*f.MFA = *f.FutureFeatures *f.MFA = *f.FutureFeatures
} }
if f.GoogleSSO == nil { if f.GoogleOAuth == nil {
f.GoogleSSO = new(bool) f.GoogleOAuth = new(bool)
*f.GoogleSSO = *f.FutureFeatures *f.GoogleOAuth = *f.FutureFeatures
}
if f.Office365OAuth == nil {
f.Office365OAuth = new(bool)
*f.Office365OAuth = *f.FutureFeatures
} }
if f.Compliance == nil { if f.Compliance == nil {
@ -75,6 +82,11 @@ func (f *Features) SetDefaults() {
*f.Compliance = *f.FutureFeatures *f.Compliance = *f.FutureFeatures
} }
if f.Cluster == nil {
f.Cluster = new(bool)
*f.Cluster = *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

@ -1,61 +0,0 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
const (
ACTION_TYPING = "typing"
ACTION_POSTED = "posted"
ACTION_POST_EDITED = "post_edited"
ACTION_POST_DELETED = "post_deleted"
ACTION_CHANNEL_DELETED = "channel_deleted"
ACTION_CHANNEL_VIEWED = "channel_viewed"
ACTION_DIRECT_ADDED = "direct_added"
ACTION_NEW_USER = "new_user"
ACTION_LEAVE_TEAM = "leave_team"
ACTION_USER_ADDED = "user_added"
ACTION_USER_REMOVED = "user_removed"
ACTION_PREFERENCE_CHANGED = "preference_changed"
ACTION_EPHEMERAL_MESSAGE = "ephemeral_message"
)
type Message struct {
TeamId string `json:"team_id"`
ChannelId string `json:"channel_id"`
UserId string `json:"user_id"`
Action string `json:"action"`
Props map[string]string `json:"props"`
}
func (m *Message) Add(key string, value string) {
m.Props[key] = value
}
func NewMessage(teamId string, channelId string, userId string, action string) *Message {
return &Message{TeamId: teamId, ChannelId: channelId, UserId: userId, Action: action, Props: make(map[string]string)}
}
func (o *Message) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func MessageFromJson(data io.Reader) *Message {
decoder := json.NewDecoder(data)
var o Message
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}

View File

@ -25,8 +25,10 @@ type OAuthApp struct {
ClientSecret string `json:"client_secret"` ClientSecret string `json:"client_secret"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
IconURL string `json:"icon_url"`
CallbackUrls StringArray `json:"callback_urls"` CallbackUrls StringArray `json:"callback_urls"`
Homepage string `json:"homepage"` Homepage string `json:"homepage"`
IsTrusted bool `json:"is_trusted"`
} }
// IsValid validates the app and returns an error if it isn't configured // IsValid validates the app and returns an error if it isn't configured
@ -61,7 +63,13 @@ func (a *OAuthApp) IsValid() *AppError {
return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "app_id="+a.Id) return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "app_id="+a.Id)
} }
if len(a.Homepage) == 0 || len(a.Homepage) > 256 { for _, callback := range a.CallbackUrls {
if !IsValidHttpUrl(callback) {
return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "")
}
}
if len(a.Homepage) == 0 || len(a.Homepage) > 256 || !IsValidHttpUrl(a.Homepage) {
return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.homepage.app_error", nil, "app_id="+a.Id) return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.homepage.app_error", nil, "app_id="+a.Id)
} }
@ -69,6 +77,12 @@ func (a *OAuthApp) IsValid() *AppError {
return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.description.app_error", nil, "app_id="+a.Id) return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.description.app_error", nil, "app_id="+a.Id)
} }
if len(a.IconURL) > 0 {
if len(a.IconURL) > 512 || !IsValidHttpUrl(a.IconURL) {
return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.icon_url.app_error", nil, "app_id="+a.Id)
}
}
return nil return nil
} }
@ -85,10 +99,6 @@ func (a *OAuthApp) PreSave() {
a.CreateAt = GetMillis() a.CreateAt = GetMillis()
a.UpdateAt = a.CreateAt a.UpdateAt = a.CreateAt
if len(a.ClientSecret) > 0 {
a.ClientSecret = HashPassword(a.ClientSecret)
}
} }
// PreUpdate should be run before updating the app in the db. // PreUpdate should be run before updating the app in the db.
@ -157,3 +167,23 @@ func OAuthAppMapFromJson(data io.Reader) map[string]*OAuthApp {
return nil return nil
} }
} }
func OAuthAppListToJson(l []*OAuthApp) string {
b, err := json.Marshal(l)
if err != nil {
return ""
} else {
return string(b)
}
}
func OAuthAppListFromJson(data io.Reader) []*OAuthApp {
decoder := json.NewDecoder(data)
var o []*OAuthApp
err := decoder.Decode(&o)
if err == nil {
return o
} else {
return nil
}
}

View File

@ -9,6 +9,7 @@ import (
"io" "io"
"net/url" "net/url"
"strconv" "strconv"
"strings"
) )
type OutgoingWebhook struct { type OutgoingWebhook struct {
@ -21,6 +22,7 @@ type OutgoingWebhook struct {
ChannelId string `json:"channel_id"` ChannelId string `json:"channel_id"`
TeamId string `json:"team_id"` TeamId string `json:"team_id"`
TriggerWords StringArray `json:"trigger_words"` TriggerWords StringArray `json:"trigger_words"`
TriggerWhen int `json:"trigger_when"`
CallbackURLs StringArray `json:"callback_urls"` CallbackURLs StringArray `json:"callback_urls"`
DisplayName string `json:"display_name"` DisplayName string `json:"display_name"`
Description string `json:"description"` Description string `json:"description"`
@ -171,6 +173,10 @@ func (o *OutgoingWebhook) IsValid() *AppError {
return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.content_type.app_error", nil, "") return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.content_type.app_error", nil, "")
} }
if o.TriggerWhen > 1 {
return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.content_type.app_error", nil, "")
}
return nil return nil
} }
@ -204,3 +210,17 @@ func (o *OutgoingWebhook) HasTriggerWord(word string) bool {
return false return false
} }
func (o *OutgoingWebhook) TriggerWordStartsWith(word string) bool {
if len(o.TriggerWords) == 0 || len(word) == 0 {
return false
}
for _, trigger := range o.TriggerWords {
if strings.HasPrefix(word, trigger) {
return true
}
}
return false
}

View File

@ -162,9 +162,6 @@ func (o *Post) AddProp(key string, value interface{}) {
o.Props[key] = value o.Props[key] = value
} }
func (o *Post) PreExport() {
}
func (o *Post) IsSystemMessage() bool { func (o *Post) IsSystemMessage() bool {
return len(o.Type) >= len(POST_SYSTEM_MESSAGE_PREFIX) && o.Type[:len(POST_SYSTEM_MESSAGE_PREFIX)] == POST_SYSTEM_MESSAGE_PREFIX return len(o.Type) >= len(POST_SYSTEM_MESSAGE_PREFIX) && o.Type[:len(POST_SYSTEM_MESSAGE_PREFIX)] == POST_SYSTEM_MESSAGE_PREFIX
} }

View File

@ -6,6 +6,8 @@ package model
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"regexp"
"strings"
"unicode/utf8" "unicode/utf8"
) )
@ -13,10 +15,17 @@ const (
PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show" PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show"
PREFERENCE_CATEGORY_TUTORIAL_STEPS = "tutorial_step" PREFERENCE_CATEGORY_TUTORIAL_STEPS = "tutorial_step"
PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings" PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings"
PREFERENCE_CATEGORY_FLAGGED_POST = "flagged_post"
PREFERENCE_CATEGORY_DISPLAY_SETTINGS = "display_settings" PREFERENCE_CATEGORY_DISPLAY_SETTINGS = "display_settings"
PREFERENCE_NAME_COLLAPSE_SETTING = "collapse_previews" PREFERENCE_NAME_COLLAPSE_SETTING = "collapse_previews"
PREFERENCE_CATEGORY_THEME = "theme"
// the name for theme props is the team id
PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP = "oauth_app"
// the name for oauth_app is the client_id and value is the current scope
PREFERENCE_CATEGORY_LAST = "last" PREFERENCE_CATEGORY_LAST = "last"
PREFERENCE_NAME_LAST_CHANNEL = "channel" PREFERENCE_NAME_LAST_CHANNEL = "channel"
) )
@ -57,13 +66,48 @@ func (o *Preference) IsValid() *AppError {
return NewLocAppError("Preference.IsValid", "model.preference.is_valid.category.app_error", nil, "category="+o.Category) return NewLocAppError("Preference.IsValid", "model.preference.is_valid.category.app_error", nil, "category="+o.Category)
} }
if len(o.Name) == 0 || len(o.Name) > 32 { if len(o.Name) > 32 {
return NewLocAppError("Preference.IsValid", "model.preference.is_valid.name.app_error", nil, "name="+o.Name) return NewLocAppError("Preference.IsValid", "model.preference.is_valid.name.app_error", nil, "name="+o.Name)
} }
if utf8.RuneCountInString(o.Value) > 128 { if utf8.RuneCountInString(o.Value) > 2000 {
return NewLocAppError("Preference.IsValid", "model.preference.is_valid.value.app_error", nil, "value="+o.Value) return NewLocAppError("Preference.IsValid", "model.preference.is_valid.value.app_error", nil, "value="+o.Value)
} }
if o.Category == PREFERENCE_CATEGORY_THEME {
var unused map[string]string
if err := json.NewDecoder(strings.NewReader(o.Value)).Decode(&unused); err != nil {
return NewLocAppError("Preference.IsValid", "model.preference.is_valid.theme.app_error", nil, "value="+o.Value)
}
}
return nil return nil
} }
func (o *Preference) PreUpdate() {
if o.Category == PREFERENCE_CATEGORY_THEME {
// decode the value of theme (a map of strings to string) and eliminate any invalid values
var props map[string]string
if err := json.NewDecoder(strings.NewReader(o.Value)).Decode(&props); err != nil {
// just continue, the invalid preference value should get caught by IsValid before saving
return
}
colorPattern := regexp.MustCompile(`^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$`)
// blank out any invalid theme values
for name, value := range props {
if name == "image" || name == "type" || name == "codeTheme" {
continue
}
if !colorPattern.MatchString(value) {
props[name] = "#ffffff"
}
}
if b, err := json.Marshal(props); err == nil {
o.Value = string(b)
}
}
}

View File

@ -83,7 +83,11 @@ func (me *Session) IsExpired() bool {
} }
func (me *Session) SetExpireInDays(days int) { func (me *Session) SetExpireInDays(days int) {
me.ExpiresAt = GetMillis() + (1000 * 60 * 60 * 24 * int64(days)) if me.CreateAt == 0 {
me.ExpiresAt = GetMillis() + (1000 * 60 * 60 * 24 * int64(days))
} else {
me.ExpiresAt = me.CreateAt + (1000 * 60 * 60 * 24 * int64(days))
}
} }
func (me *Session) AddProp(key string, value string) { func (me *Session) AddProp(key string, value string) {

42
vendor/github.com/mattermost/platform/model/status.go generated vendored Normal file
View File

@ -0,0 +1,42 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
const (
STATUS_OFFLINE = "offline"
STATUS_AWAY = "away"
STATUS_ONLINE = "online"
STATUS_CACHE_SIZE = 10000
)
type Status struct {
UserId string `json:"user_id"`
Status string `json:"status"`
LastActivityAt int64 `json:"last_activity_at"`
}
func (o *Status) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func StatusFromJson(data io.Reader) *Status {
decoder := json.NewDecoder(data)
var o Status
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}

View File

@ -224,9 +224,6 @@ func CleanTeamName(s string) string {
return s return s
} }
func (o *Team) PreExport() {
}
func (o *Team) Sanitize() { func (o *Team) Sanitize() {
o.Email = "" o.Email = ""
o.AllowedDomains = "" o.AllowedDomains = ""

View File

@ -16,11 +16,6 @@ import (
const ( const (
ROLE_SYSTEM_ADMIN = "system_admin" ROLE_SYSTEM_ADMIN = "system_admin"
USER_AWAY_TIMEOUT = 5 * 60 * 1000 // 5 minutes
USER_OFFLINE_TIMEOUT = 1 * 60 * 1000 // 1 minute
USER_OFFLINE = "offline"
USER_AWAY = "away"
USER_ONLINE = "online"
USER_NOTIFY_ALL = "all" USER_NOTIFY_ALL = "all"
USER_NOTIFY_MENTION = "mention" USER_NOTIFY_MENTION = "mention"
USER_NOTIFY_NONE = "none" USER_NOTIFY_NONE = "none"
@ -44,12 +39,9 @@ type User struct {
FirstName string `json:"first_name"` FirstName string `json:"first_name"`
LastName string `json:"last_name"` LastName string `json:"last_name"`
Roles string `json:"roles"` Roles string `json:"roles"`
LastActivityAt int64 `json:"last_activity_at,omitempty"`
LastPingAt int64 `json:"last_ping_at,omitempty"`
AllowMarketing bool `json:"allow_marketing,omitempty"` AllowMarketing bool `json:"allow_marketing,omitempty"`
Props StringMap `json:"props,omitempty"` Props StringMap `json:"props,omitempty"`
NotifyProps StringMap `json:"notify_props,omitempty"` NotifyProps StringMap `json:"notify_props,omitempty"`
ThemeProps StringMap `json:"theme_props,omitempty"`
LastPasswordUpdate int64 `json:"last_password_update,omitempty"` LastPasswordUpdate int64 `json:"last_password_update,omitempty"`
LastPictureUpdate int64 `json:"last_picture_update,omitempty"` LastPictureUpdate int64 `json:"last_picture_update,omitempty"`
FailedAttempts int `json:"failed_attempts,omitempty"` FailedAttempts int `json:"failed_attempts,omitempty"`
@ -106,10 +98,6 @@ func (u *User) IsValid() *AppError {
return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id) return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id)
} }
if len(u.ThemeProps) > 2000 {
return NewLocAppError("User.IsValid", "model.user.is_valid.theme.app_error", nil, "user_id="+u.Id)
}
return nil return nil
} }
@ -179,21 +167,6 @@ func (u *User) PreUpdate() {
} }
u.NotifyProps["mention_keys"] = strings.Join(goodKeys, ",") u.NotifyProps["mention_keys"] = strings.Join(goodKeys, ",")
} }
if u.ThemeProps != nil {
colorPattern := regexp.MustCompile(`^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$`)
// blank out any invalid theme values
for name, value := range u.ThemeProps {
if name == "image" || name == "type" || name == "codeTheme" {
continue
}
if !colorPattern.MatchString(value) {
u.ThemeProps[name] = "#ffffff"
}
}
}
} }
func (u *User) SetDefaultNotifications() { func (u *User) SetDefaultNotifications() {
@ -242,14 +215,6 @@ func (u *User) Etag(showFullName, showEmail bool) string {
return Etag(u.Id, u.UpdateAt, showFullName, showEmail) return Etag(u.Id, u.UpdateAt, showFullName, showEmail)
} }
func (u *User) IsOffline() bool {
return (GetMillis()-u.LastPingAt) > USER_OFFLINE_TIMEOUT && (GetMillis()-u.LastActivityAt) > USER_OFFLINE_TIMEOUT
}
func (u *User) IsAway() bool {
return (GetMillis() - u.LastActivityAt) > USER_AWAY_TIMEOUT
}
// Remove any private data from the user object // Remove any private data from the user object
func (u *User) Sanitize(options map[string]bool) { func (u *User) Sanitize(options map[string]bool) {
u.Password = "" u.Password = ""
@ -278,11 +243,9 @@ func (u *User) ClearNonProfileFields() {
u.MfaActive = false u.MfaActive = false
u.MfaSecret = "" u.MfaSecret = ""
u.EmailVerified = false u.EmailVerified = false
u.LastPingAt = 0
u.AllowMarketing = false u.AllowMarketing = false
u.Props = StringMap{} u.Props = StringMap{}
u.NotifyProps = StringMap{} u.NotifyProps = StringMap{}
u.ThemeProps = StringMap{}
u.LastPasswordUpdate = 0 u.LastPasswordUpdate = 0
u.LastPictureUpdate = 0 u.LastPictureUpdate = 0
u.FailedAttempts = 0 u.FailedAttempts = 0
@ -392,17 +355,6 @@ func (u *User) IsLDAPUser() bool {
return false return false
} }
func (u *User) PreExport() {
u.Password = ""
u.AuthData = new(string)
*u.AuthData = ""
u.LastActivityAt = 0
u.LastPingAt = 0
u.LastPasswordUpdate = 0
u.LastPictureUpdate = 0
u.FailedAttempts = 0
}
// UserFromJson will decode the input and return a User // UserFromJson will decode the input and return a User
func UserFromJson(data io.Reader) *User { func UserFromJson(data io.Reader) *User {
decoder := json.NewDecoder(data) decoder := json.NewDecoder(data)
@ -461,6 +413,7 @@ var validUsernameChars = regexp.MustCompile(`^[a-z0-9\.\-_]+$`)
var restrictedUsernames = []string{ var restrictedUsernames = []string{
"all", "all",
"channel", "channel",
"matterbot",
} }
func IsValidUsername(s string) bool { func IsValidUsername(s string) bool {

View File

@ -34,12 +34,12 @@ type EncryptStringMap map[string]string
type AppError struct { type AppError struct {
Id string `json:"id"` Id string `json:"id"`
Message string `json:"message"` // Message to be display to the end user without debugging information 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 DetailedError string `json:"detailed_error"` // Internal error string to help the developer
RequestId string `json:"request_id"` // The RequestId that's also set in the header RequestId string `json:"request_id,omitempty"` // The RequestId that's also set in the header
StatusCode int `json:"status_code"` // The http status code StatusCode int `json:"status_code,omitempty"` // The http status code
Where string `json:"-"` // The function where it happened in the form of Struct.Func Where string `json:"-"` // The function where it happened in the form of Struct.Func
IsOAuth bool `json:"is_oauth"` // Whether the error is OAuth specific IsOAuth bool `json:"is_oauth,omitempty"` // Whether the error is OAuth specific
params map[string]interface{} `json:"-"` params map[string]interface{} `json:"-"`
} }

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.3.0",
"3.2.0", "3.2.0",
"3.1.0", "3.1.0",
"3.0.0", "3.0.0",

View File

@ -0,0 +1,109 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"github.com/gorilla/websocket"
"net/http"
)
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"
Conn *websocket.Conn // The WebSocket connection
AuthToken string // The token used to open the WebSocket
Sequence int64 // The ever-incrementing sequence attached to each WebSocket action
EventChannel chan *WebSocketEvent
ResponseChannel chan *WebSocketResponse
}
// NewWebSocketClient constructs a new WebSocket client with convienence
// methods for talking to the server.
func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) {
header := http.Header{}
header.Set(HEADER_AUTH, "BEARER "+authToken)
conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX+"/users/websocket", header)
if err != nil {
return nil, NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error())
}
return &WebSocketClient{
url,
url + API_URL_SUFFIX,
conn,
authToken,
1,
make(chan *WebSocketEvent, 100),
make(chan *WebSocketResponse, 100),
}, nil
}
func (wsc *WebSocketClient) Connect() *AppError {
header := http.Header{}
header.Set(HEADER_AUTH, "BEARER "+wsc.AuthToken)
var err error
wsc.Conn, _, err = websocket.DefaultDialer.Dial(wsc.ApiUrl+"/users/websocket", header)
if err != nil {
return NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error())
}
return nil
}
func (wsc *WebSocketClient) Close() {
wsc.Conn.Close()
}
func (wsc *WebSocketClient) Listen() {
go func() {
for {
var rawMsg json.RawMessage
var err error
if _, rawMsg, err = wsc.Conn.ReadMessage(); err != nil {
return
}
var event WebSocketEvent
if err := json.Unmarshal(rawMsg, &event); err == nil && event.IsValid() {
wsc.EventChannel <- &event
continue
}
var response WebSocketResponse
if err := json.Unmarshal(rawMsg, &response); err == nil && response.IsValid() {
wsc.ResponseChannel <- &response
continue
}
}
}()
}
func (wsc *WebSocketClient) SendMessage(action string, data map[string]interface{}) {
req := &WebSocketRequest{}
req.Seq = wsc.Sequence
req.Action = action
req.Data = data
wsc.Sequence++
wsc.Conn.WriteJSON(req)
}
// UserTyping will push a user_typing event out to all connected users
// who are in the specified channel
func (wsc *WebSocketClient) UserTyping(channelId, parentId string) {
data := map[string]interface{}{
"channel_id": channelId,
"parent_id": parentId,
}
wsc.SendMessage("user_typing", data)
}
// GetStatuses will return a map of string statuses using user id as the key
func (wsc *WebSocketClient) GetStatuses() {
wsc.SendMessage("get_statuses", nil)
}

View File

@ -0,0 +1,114 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
const (
WEBSOCKET_EVENT_TYPING = "typing"
WEBSOCKET_EVENT_POSTED = "posted"
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_DIRECT_ADDED = "direct_added"
WEBSOCKET_EVENT_NEW_USER = "new_user"
WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team"
WEBSOCKET_EVENT_USER_ADDED = "user_added"
WEBSOCKET_EVENT_USER_REMOVED = "user_removed"
WEBSOCKET_EVENT_PREFERENCE_CHANGED = "preference_changed"
WEBSOCKET_EVENT_EPHEMERAL_MESSAGE = "ephemeral_message"
WEBSOCKET_EVENT_STATUS_CHANGE = "status_change"
)
type WebSocketMessage interface {
ToJson() string
IsValid() bool
}
type WebSocketEvent struct {
TeamId string `json:"team_id"`
ChannelId string `json:"channel_id"`
UserId string `json:"user_id"`
Event string `json:"event"`
Data map[string]interface{} `json:"data"`
}
func (m *WebSocketEvent) Add(key string, value interface{}) {
m.Data[key] = value
}
func NewWebSocketEvent(teamId string, channelId string, userId string, event string) *WebSocketEvent {
return &WebSocketEvent{TeamId: teamId, ChannelId: channelId, UserId: userId, Event: event, Data: make(map[string]interface{})}
}
func (o *WebSocketEvent) IsValid() bool {
return o.Event != ""
}
func (o *WebSocketEvent) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func WebSocketEventFromJson(data io.Reader) *WebSocketEvent {
decoder := json.NewDecoder(data)
var o WebSocketEvent
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}
type WebSocketResponse struct {
Status string `json:"status"`
SeqReply int64 `json:"seq_reply,omitempty"`
Data map[string]interface{} `json:"data,omitempty"`
Error *AppError `json:"error,omitempty"`
}
func (m *WebSocketResponse) Add(key string, value interface{}) {
m.Data[key] = value
}
func NewWebSocketResponse(status string, seqReply int64, data map[string]interface{}) *WebSocketResponse {
return &WebSocketResponse{Status: status, SeqReply: seqReply, Data: data}
}
func NewWebSocketError(seqReply int64, err *AppError) *WebSocketResponse {
return &WebSocketResponse{Status: STATUS_FAIL, SeqReply: seqReply, Error: err}
}
func (o *WebSocketResponse) IsValid() bool {
return o.Status != ""
}
func (o *WebSocketResponse) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func WebSocketResponseFromJson(data io.Reader) *WebSocketResponse {
decoder := json.NewDecoder(data)
var o WebSocketResponse
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}

View File

@ -0,0 +1,43 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
goi18n "github.com/nicksnyder/go-i18n/i18n"
)
type WebSocketRequest struct {
// Client-provided fields
Seq int64 `json:"seq"`
Action string `json:"action"`
Data map[string]interface{} `json:"data"`
// Server-provided fields
Session Session `json:"-"`
T goi18n.TranslateFunc `json:"-"`
Locale string `json:"-"`
}
func (o *WebSocketRequest) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func WebSocketRequestFromJson(data io.Reader) *WebSocketRequest {
decoder := json.NewDecoder(data)
var o WebSocketRequest
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}

8
vendor/manifest vendored
View File

@ -63,8 +63,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": "ab52700aaa76a5623de23cd0156f5dbd9a24e264", "revision": "20735470185e0b0ac1d15b975041ed9a2e0e43bc",
"branch": "release-3.2", "branch": "release-3.3",
"path": "/einterfaces", "path": "/einterfaces",
"notests": true "notests": true
}, },
@ -72,8 +72,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": "ab52700aaa76a5623de23cd0156f5dbd9a24e264", "revision": "20735470185e0b0ac1d15b975041ed9a2e0e43bc",
"branch": "release-3.2", "branch": "release-3.3",
"path": "/model", "path": "/model",
"notests": true "notests": true
}, },