summaryrefslogtreecommitdiffstats
path: root/model
diff options
context:
space:
mode:
Diffstat (limited to 'model')
-rw-r--r--model/analytics_row.go55
-rw-r--r--model/analytics_row_test.go37
-rw-r--r--model/channel.go11
-rw-r--r--model/channel_test.go20
-rw-r--r--model/client.go26
-rw-r--r--model/command.go3
-rw-r--r--model/config.go6
-rw-r--r--model/incoming_webhook.go18
-rw-r--r--model/outgoing_webhook.go6
-rw-r--r--model/outgoing_webhook_test.go5
-rw-r--r--model/post_list.go9
-rw-r--r--model/post_list_test.go34
-rw-r--r--model/search_params.go88
-rw-r--r--model/search_params_test.go17
-rw-r--r--model/team.go30
-rw-r--r--model/utils.go13
16 files changed, 314 insertions, 64 deletions
diff --git a/model/analytics_row.go b/model/analytics_row.go
new file mode 100644
index 000000000..ed1d69dd2
--- /dev/null
+++ b/model/analytics_row.go
@@ -0,0 +1,55 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+)
+
+type AnalyticsRow struct {
+ Name string `json:"name"`
+ Value float64 `json:"value"`
+}
+
+type AnalyticsRows []*AnalyticsRow
+
+func (me *AnalyticsRow) ToJson() string {
+ b, err := json.Marshal(me)
+ if err != nil {
+ return ""
+ } else {
+ return string(b)
+ }
+}
+
+func AnalyticsRowFromJson(data io.Reader) *AnalyticsRow {
+ decoder := json.NewDecoder(data)
+ var me AnalyticsRow
+ err := decoder.Decode(&me)
+ if err == nil {
+ return &me
+ } else {
+ return nil
+ }
+}
+
+func (me AnalyticsRows) ToJson() string {
+ if b, err := json.Marshal(me); err != nil {
+ return "[]"
+ } else {
+ return string(b)
+ }
+}
+
+func AnalyticsRowsFromJson(data io.Reader) AnalyticsRows {
+ decoder := json.NewDecoder(data)
+ var me AnalyticsRows
+ err := decoder.Decode(&me)
+ if err == nil {
+ return me
+ } else {
+ return nil
+ }
+}
diff --git a/model/analytics_row_test.go b/model/analytics_row_test.go
new file mode 100644
index 000000000..1202d5b52
--- /dev/null
+++ b/model/analytics_row_test.go
@@ -0,0 +1,37 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestAnalyticsRowJson(t *testing.T) {
+ a1 := AnalyticsRow{}
+ a1.Name = "2015-10-12"
+ a1.Value = 12345.0
+ json := a1.ToJson()
+ ra1 := AnalyticsRowFromJson(strings.NewReader(json))
+
+ if a1.Name != ra1.Name {
+ t.Fatal("days didn't match")
+ }
+}
+
+func TestAnalyticsRowsJson(t *testing.T) {
+ a1 := AnalyticsRow{}
+ a1.Name = "2015-10-12"
+ a1.Value = 12345.0
+
+ var a1s AnalyticsRows = make([]*AnalyticsRow, 1)
+ a1s[0] = &a1
+
+ ljson := a1s.ToJson()
+ results := AnalyticsRowsFromJson(strings.NewReader(ljson))
+
+ if a1s[0].Name != results[0].Name {
+ t.Fatal("Ids do not match")
+ }
+}
diff --git a/model/channel.go b/model/channel.go
index 076ddf0b5..ac54a7e44 100644
--- a/model/channel.go
+++ b/model/channel.go
@@ -24,7 +24,8 @@ type Channel struct {
Type string `json:"type"`
DisplayName string `json:"display_name"`
Name string `json:"name"`
- Description string `json:"description"`
+ Header string `json:"header"`
+ Purpose string `json:"purpose"`
LastPostAt int64 `json:"last_post_at"`
TotalMsgCount int64 `json:"total_msg_count"`
ExtraUpdateAt int64 `json:"extra_update_at"`
@@ -89,8 +90,12 @@ func (o *Channel) IsValid() *AppError {
return NewAppError("Channel.IsValid", "Invalid type", "id="+o.Id)
}
- if len(o.Description) > 1024 {
- return NewAppError("Channel.IsValid", "Invalid description", "id="+o.Id)
+ if len(o.Header) > 1024 {
+ return NewAppError("Channel.IsValid", "Invalid header", "id="+o.Id)
+ }
+
+ if len(o.Purpose) > 128 {
+ return NewAppError("Channel.IsValid", "Invalid purpose", "id="+o.Id)
}
if len(o.CreatorId) > 26 {
diff --git a/model/channel_test.go b/model/channel_test.go
index e5dfa3734..590417cfe 100644
--- a/model/channel_test.go
+++ b/model/channel_test.go
@@ -67,6 +67,26 @@ func TestChannelIsValid(t *testing.T) {
if err := o.IsValid(); err != nil {
t.Fatal(err)
}
+
+ o.Header = strings.Repeat("01234567890", 100)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.Header = "1234"
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
+
+ o.Purpose = strings.Repeat("01234567890", 20)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.Purpose = "1234"
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
}
func TestChannelPreSave(t *testing.T) {
diff --git a/model/client.go b/model/client.go
index 48a560838..19183098e 100644
--- a/model/client.go
+++ b/model/client.go
@@ -211,8 +211,8 @@ func (c *Client) InviteMembers(invites *Invites) (*Result, *AppError) {
}
}
-func (c *Client) UpdateTeamDisplayName(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/teams/update_name", MapToJson(data)); err != nil {
+func (c *Client) UpdateTeam(team *Team) (*Result, *AppError) {
+ if r, err := c.DoApiPost("/teams/update", team.ToJson()); err != nil {
return nil, err
} else {
return &Result{r.Header.Get(HEADER_REQUEST_ID),
@@ -416,6 +416,15 @@ func (c *Client) TestEmail(config *Config) (*Result, *AppError) {
}
}
+func (c *Client) GetAnalytics(teamId, name string) (*Result, *AppError) {
+ if r, err := c.DoApiGet("/admin/analytics/"+teamId+"/"+name, "", ""); err != nil {
+ return nil, err
+ } else {
+ return &Result{r.Header.Get(HEADER_REQUEST_ID),
+ r.Header.Get(HEADER_ETAG_SERVER), AnalyticsRowsFromJson(r.Body)}, nil
+ }
+}
+
func (c *Client) CreateChannel(channel *Channel) (*Result, *AppError) {
if r, err := c.DoApiPost("/channels/create", channel.ToJson()); err != nil {
return nil, err
@@ -443,8 +452,17 @@ func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) {
}
}
-func (c *Client) UpdateChannelDesc(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/channels/update_desc", MapToJson(data)); err != nil {
+func (c *Client) UpdateChannelHeader(data map[string]string) (*Result, *AppError) {
+ if r, err := c.DoApiPost("/channels/update_header", MapToJson(data)); err != nil {
+ return nil, err
+ } else {
+ return &Result{r.Header.Get(HEADER_REQUEST_ID),
+ r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
+ }
+}
+
+func (c *Client) UpdateChannelPurpose(data map[string]string) (*Result, *AppError) {
+ if r, err := c.DoApiPost("/channels/update_purpose", MapToJson(data)); err != nil {
return nil, err
} else {
return &Result{r.Header.Get(HEADER_REQUEST_ID),
diff --git a/model/command.go b/model/command.go
index 2b26aad1c..5aec5f534 100644
--- a/model/command.go
+++ b/model/command.go
@@ -9,7 +9,8 @@ import (
)
const (
- RESP_EXECUTED = "executed"
+ RESP_EXECUTED = "executed"
+ RESP_NOT_IMPLEMENTED = "not implemented"
)
type Command struct {
diff --git a/model/config.go b/model/config.go
index 216b1de86..50a8dc133 100644
--- a/model/config.go
+++ b/model/config.go
@@ -123,6 +123,7 @@ type TeamSettings struct {
EnableUserCreation bool
RestrictCreationToDomains string
RestrictTeamNames *bool
+ EnableTeamListing *bool
}
type Config struct {
@@ -175,6 +176,11 @@ func (o *Config) SetDefaults() {
o.TeamSettings.RestrictTeamNames = new(bool)
*o.TeamSettings.RestrictTeamNames = true
}
+
+ if o.TeamSettings.EnableTeamListing == nil {
+ o.TeamSettings.EnableTeamListing = new(bool)
+ *o.TeamSettings.EnableTeamListing = false
+ }
}
func (o *Config) IsValid() *AppError {
diff --git a/model/incoming_webhook.go b/model/incoming_webhook.go
index 9b9969b96..be1984244 100644
--- a/model/incoming_webhook.go
+++ b/model/incoming_webhook.go
@@ -23,6 +23,13 @@ type IncomingWebhook struct {
TeamId string `json:"team_id"`
}
+type IncomingWebhookRequest struct {
+ Text string `json:"text"`
+ Username string `json:"username"`
+ IconURL string `json:"icon_url"`
+ ChannelName string `json:"channel"`
+}
+
func (o *IncomingWebhook) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
@@ -104,3 +111,14 @@ func (o *IncomingWebhook) PreSave() {
func (o *IncomingWebhook) PreUpdate() {
o.UpdateAt = GetMillis()
}
+
+func IncomingWebhookRequestFromJson(data io.Reader) *IncomingWebhookRequest {
+ decoder := json.NewDecoder(data)
+ var o IncomingWebhookRequest
+ err := decoder.Decode(&o)
+ if err == nil {
+ return &o
+ } else {
+ return nil
+ }
+}
diff --git a/model/outgoing_webhook.go b/model/outgoing_webhook.go
index 8958dd5b0..9a1b89a85 100644
--- a/model/outgoing_webhook.go
+++ b/model/outgoing_webhook.go
@@ -100,6 +100,12 @@ func (o *OutgoingWebhook) IsValid() *AppError {
return NewAppError("OutgoingWebhook.IsValid", "Invalid callback urls", "")
}
+ for _, callback := range o.CallbackURLs {
+ if !IsValidHttpUrl(callback) {
+ return NewAppError("OutgoingWebhook.IsValid", "Invalid callback URLs. Each must be a valid URL and start with http:// or https://", "")
+ }
+ }
+
return nil
}
diff --git a/model/outgoing_webhook_test.go b/model/outgoing_webhook_test.go
index 2ca48c291..0d1cd773e 100644
--- a/model/outgoing_webhook_test.go
+++ b/model/outgoing_webhook_test.go
@@ -80,6 +80,11 @@ func TestOutgoingWebhookIsValid(t *testing.T) {
t.Fatal("should be invalid")
}
+ o.CallbackURLs = []string{"nowhere.com/"}
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
o.CallbackURLs = []string{"http://nowhere.com/"}
if err := o.IsValid(); err != nil {
t.Fatal(err)
diff --git a/model/post_list.go b/model/post_list.go
index 862673ef3..4c0f5408e 100644
--- a/model/post_list.go
+++ b/model/post_list.go
@@ -54,6 +54,15 @@ func (o *PostList) AddPost(post *Post) {
o.Posts[post.Id] = post
}
+func (o *PostList) Extend(other *PostList) {
+ for _, postId := range other.Order {
+ if _, ok := o.Posts[postId]; !ok {
+ o.AddPost(other.Posts[postId])
+ o.AddOrder(postId)
+ }
+ }
+}
+
func (o *PostList) Etag() string {
id := "0"
diff --git a/model/post_list_test.go b/model/post_list_test.go
index 8a34327ce..9ce6447e1 100644
--- a/model/post_list_test.go
+++ b/model/post_list_test.go
@@ -34,3 +34,37 @@ func TestPostListJson(t *testing.T) {
t.Fatal("failed to serialize")
}
}
+
+func TestPostListExtend(t *testing.T) {
+ l1 := PostList{}
+
+ p1 := &Post{Id: NewId(), Message: NewId()}
+ l1.AddPost(p1)
+ l1.AddOrder(p1.Id)
+
+ p2 := &Post{Id: NewId(), Message: NewId()}
+ l1.AddPost(p2)
+ l1.AddOrder(p2.Id)
+
+ l2 := PostList{}
+
+ p3 := &Post{Id: NewId(), Message: NewId()}
+ l2.AddPost(p3)
+ l2.AddOrder(p3.Id)
+
+ l2.Extend(&l1)
+
+ if len(l1.Posts) != 2 || len(l1.Order) != 2 {
+ t.Fatal("extending l2 changed l1")
+ } else if len(l2.Posts) != 3 {
+ t.Fatal("failed to extend posts l2")
+ } else if l2.Order[0] != p3.Id || l2.Order[1] != p1.Id || l2.Order[2] != p2.Id {
+ t.Fatal("failed to extend order of l2")
+ }
+
+ if len(l1.Posts) != 2 || len(l1.Order) != 2 {
+ t.Fatal("extending l2 again changed l1")
+ } else if len(l2.Posts) != 3 || len(l2.Order) != 3 {
+ t.Fatal("extending l2 again changed l2")
+ }
+}
diff --git a/model/search_params.go b/model/search_params.go
index 7eeeed10f..144e8e461 100644
--- a/model/search_params.go
+++ b/model/search_params.go
@@ -8,10 +8,10 @@ import (
)
type SearchParams struct {
- Terms string
- IsHashtag bool
- InChannel string
- FromUser string
+ Terms string
+ IsHashtag bool
+ InChannels []string
+ FromUsers []string
}
var searchFlags = [...]string{"from", "channel", "in"}
@@ -31,9 +31,9 @@ func splitWords(text string) []string {
return words
}
-func parseSearchFlags(input []string) ([]string, map[string]string) {
+func parseSearchFlags(input []string) ([]string, [][2]string) {
words := []string{}
- flags := make(map[string]string)
+ flags := [][2]string{}
skipNextWord := false
for i, word := range input {
@@ -52,10 +52,10 @@ func parseSearchFlags(input []string) ([]string, map[string]string) {
// check for case insensitive equality
if strings.EqualFold(flag, searchFlag) {
if value != "" {
- flags[searchFlag] = value
+ flags = append(flags, [2]string{searchFlag, value})
isFlag = true
} else if i < len(input)-1 {
- flags[searchFlag] = input[i+1]
+ flags = append(flags, [2]string{searchFlag, input[i+1]})
skipNextWord = true
isFlag = true
}
@@ -75,56 +75,66 @@ func parseSearchFlags(input []string) ([]string, map[string]string) {
return words, flags
}
-func ParseSearchParams(text string) (*SearchParams, *SearchParams) {
+func ParseSearchParams(text string) []*SearchParams {
words, flags := parseSearchFlags(splitWords(text))
- hashtagTerms := []string{}
- plainTerms := []string{}
+ hashtagTermList := []string{}
+ plainTermList := []string{}
for _, word := range words {
if validHashtag.MatchString(word) {
- hashtagTerms = append(hashtagTerms, word)
+ hashtagTermList = append(hashtagTermList, word)
} else {
- plainTerms = append(plainTerms, word)
+ plainTermList = append(plainTermList, word)
}
}
- inChannel := flags["channel"]
- if inChannel == "" {
- inChannel = flags["in"]
+ hashtagTerms := strings.Join(hashtagTermList, " ")
+ plainTerms := strings.Join(plainTermList, " ")
+
+ inChannels := []string{}
+ fromUsers := []string{}
+
+ for _, flagPair := range flags {
+ flag := flagPair[0]
+ value := flagPair[1]
+
+ if flag == "in" || flag == "channel" {
+ inChannels = append(inChannels, value)
+ } else if flag == "from" {
+ fromUsers = append(fromUsers, value)
+ }
}
- fromUser := flags["from"]
+ paramsList := []*SearchParams{}
- var plainParams *SearchParams
if len(plainTerms) > 0 {
- plainParams = &SearchParams{
- Terms: strings.Join(plainTerms, " "),
- IsHashtag: false,
- InChannel: inChannel,
- FromUser: fromUser,
- }
+ paramsList = append(paramsList, &SearchParams{
+ Terms: plainTerms,
+ IsHashtag: false,
+ InChannels: inChannels,
+ FromUsers: fromUsers,
+ })
}
- var hashtagParams *SearchParams
if len(hashtagTerms) > 0 {
- hashtagParams = &SearchParams{
- Terms: strings.Join(hashtagTerms, " "),
- IsHashtag: true,
- InChannel: inChannel,
- FromUser: fromUser,
- }
+ paramsList = append(paramsList, &SearchParams{
+ Terms: hashtagTerms,
+ IsHashtag: true,
+ InChannels: inChannels,
+ FromUsers: fromUsers,
+ })
}
// special case for when no terms are specified but we still have a filter
- if plainParams == nil && hashtagParams == nil && (inChannel != "" || fromUser != "") {
- plainParams = &SearchParams{
- Terms: "",
- IsHashtag: false,
- InChannel: inChannel,
- FromUser: fromUser,
- }
+ if len(plainTerms) == 0 && len(hashtagTerms) == 0 {
+ paramsList = append(paramsList, &SearchParams{
+ Terms: "",
+ IsHashtag: true,
+ InChannels: inChannels,
+ FromUsers: fromUsers,
+ })
}
- return plainParams, hashtagParams
+ return paramsList
}
diff --git a/model/search_params_test.go b/model/search_params_test.go
index 2eba20f4c..e03e82c5a 100644
--- a/model/search_params_test.go
+++ b/model/search_params_test.go
@@ -28,25 +28,25 @@ func TestParseSearchFlags(t *testing.T) {
if words, flags := parseSearchFlags(splitWords("apple banana from:chan")); len(words) != 2 || words[0] != "apple" || words[1] != "banana" {
t.Fatalf("got incorrect words %v", words)
- } else if len(flags) != 1 || flags["from"] != "chan" {
+ } else if len(flags) != 1 || flags[0][0] != "from" || flags[0][1] != "chan" {
t.Fatalf("got incorrect flags %v", flags)
}
if words, flags := parseSearchFlags(splitWords("apple banana from: chan")); len(words) != 2 || words[0] != "apple" || words[1] != "banana" {
t.Fatalf("got incorrect words %v", words)
- } else if len(flags) != 1 || flags["from"] != "chan" {
+ } else if len(flags) != 1 || flags[0][0] != "from" || flags[0][1] != "chan" {
t.Fatalf("got incorrect flags %v", flags)
}
if words, flags := parseSearchFlags(splitWords("apple banana in: chan")); len(words) != 2 || words[0] != "apple" || words[1] != "banana" {
t.Fatalf("got incorrect words %v", words)
- } else if len(flags) != 1 || flags["in"] != "chan" {
+ } else if len(flags) != 1 || flags[0][0] != "in" || flags[0][1] != "chan" {
t.Fatalf("got incorrect flags %v", flags)
}
if words, flags := parseSearchFlags(splitWords("apple banana channel:chan")); len(words) != 2 || words[0] != "apple" || words[1] != "banana" {
t.Fatalf("got incorrect words %v", words)
- } else if len(flags) != 1 || flags["channel"] != "chan" {
+ } else if len(flags) != 1 || flags[0][0] != "channel" || flags[0][1] != "chan" {
t.Fatalf("got incorrect flags %v", flags)
}
@@ -64,7 +64,14 @@ func TestParseSearchFlags(t *testing.T) {
if words, flags := parseSearchFlags(splitWords("channel: first in: second from:")); len(words) != 1 || words[0] != "from:" {
t.Fatalf("got incorrect words %v", words)
- } else if len(flags) != 2 || flags["channel"] != "first" || flags["in"] != "second" {
+ } else if len(flags) != 2 || flags[0][0] != "channel" || flags[0][1] != "first" || flags[1][0] != "in" || flags[1][1] != "second" {
+ t.Fatalf("got incorrect flags %v", flags)
+ }
+
+ if words, flags := parseSearchFlags(splitWords("channel: first channel: second from: third from: fourth")); len(words) != 0 {
+ t.Fatalf("got incorrect words %v", words)
+ } else if len(flags) != 4 || flags[0][0] != "channel" || flags[0][1] != "first" || flags[1][0] != "channel" || flags[1][1] != "second" ||
+ flags[2][0] != "from" || flags[2][1] != "third" || flags[3][0] != "from" || flags[3][1] != "fourth" {
t.Fatalf("got incorrect flags %v", flags)
}
}
diff --git a/model/team.go b/model/team.go
index 9da2cd5b2..5c9cf5a26 100644
--- a/model/team.go
+++ b/model/team.go
@@ -17,16 +17,19 @@ const (
)
type Team struct {
- Id string `json:"id"`
- CreateAt int64 `json:"create_at"`
- UpdateAt int64 `json:"update_at"`
- DeleteAt int64 `json:"delete_at"`
- DisplayName string `json:"display_name"`
- Name string `json:"name"`
- Email string `json:"email"`
- Type string `json:"type"`
- CompanyName string `json:"company_name"`
- AllowedDomains string `json:"allowed_domains"`
+ Id string `json:"id"`
+ CreateAt int64 `json:"create_at"`
+ UpdateAt int64 `json:"update_at"`
+ DeleteAt int64 `json:"delete_at"`
+ DisplayName string `json:"display_name"`
+ Name string `json:"name"`
+ Email string `json:"email"`
+ Type string `json:"type"`
+ CompanyName string `json:"company_name"`
+ AllowedDomains string `json:"allowed_domains"`
+ InviteId string `json:"invite_id"`
+ AllowOpenInvite bool `json:"allow_open_invite"`
+ AllowTeamListing bool `json:"allow_team_listing"`
}
type Invites struct {
@@ -119,7 +122,7 @@ func (o *Team) IsValid(restrictTeamNames bool) *AppError {
return NewAppError("Team.IsValid", "Invalid email", "id="+o.Id)
}
- if len(o.DisplayName) > 64 {
+ if len(o.DisplayName) == 0 || len(o.DisplayName) > 64 {
return NewAppError("Team.IsValid", "Invalid name", "id="+o.Id)
}
@@ -157,6 +160,10 @@ func (o *Team) PreSave() {
o.CreateAt = GetMillis()
o.UpdateAt = o.CreateAt
+
+ if len(o.InviteId) == 0 {
+ o.InviteId = NewId()
+ }
}
func (o *Team) PreUpdate() {
@@ -222,6 +229,5 @@ func (o *Team) PreExport() {
func (o *Team) Sanitize() {
o.Email = ""
- o.Type = ""
o.AllowedDomains = ""
}
diff --git a/model/utils.go b/model/utils.go
index bb0669df7..681ade870 100644
--- a/model/utils.go
+++ b/model/utils.go
@@ -11,6 +11,7 @@ import (
"fmt"
"io"
"net/mail"
+ "net/url"
"regexp"
"strings"
"time"
@@ -301,3 +302,15 @@ var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-
var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+(?:\.[A-Za-z0-9]{3,})?)`)
var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true}
+
+func IsValidHttpUrl(rawUrl string) bool {
+ if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 {
+ return false
+ }
+
+ if _, err := url.ParseRequestURI(rawUrl); err != nil {
+ return false
+ }
+
+ return true
+}