summaryrefslogtreecommitdiffstats
path: root/model
diff options
context:
space:
mode:
Diffstat (limited to 'model')
-rw-r--r--model/access.go2
-rw-r--r--model/client.go73
-rw-r--r--model/config.go1
-rw-r--r--model/incoming_webhook.go (renamed from model/webhook.go)5
-rw-r--r--model/incoming_webhook_test.go (renamed from model/webhook_test.go)0
-rw-r--r--model/message.go16
-rw-r--r--model/outgoing_webhook.go135
-rw-r--r--model/outgoing_webhook_test.go97
-rw-r--r--model/preference.go60
-rw-r--r--model/preference_test.go56
-rw-r--r--model/preferences.go31
-rw-r--r--model/search_params.go130
-rw-r--r--model/search_params_test.go70
-rw-r--r--model/system.go6
-rw-r--r--model/team.go6
-rw-r--r--model/utils.go4
16 files changed, 680 insertions, 12 deletions
diff --git a/model/access.go b/model/access.go
index 89a1271c1..6c9254004 100644
--- a/model/access.go
+++ b/model/access.go
@@ -16,7 +16,7 @@ const (
type AccessData struct {
AuthCode string `json:"auth_code"`
- Token string `json"token"`
+ Token string `json:"token"`
RefreshToken string `json:"refresh_token"`
RedirectUri string `json:"redirect_uri"`
}
diff --git a/model/client.go b/model/client.go
index 11beb9a87..9183dcacb 100644
--- a/model/client.go
+++ b/model/client.go
@@ -185,7 +185,7 @@ func (c *Client) FindTeams(email string) (*Result, *AppError) {
} else {
return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ArrayFromJson(r.Body)}, nil
+ r.Header.Get(HEADER_ETAG_SERVER), TeamMapFromJson(r.Body)}, nil
}
}
@@ -844,6 +844,77 @@ func (c *Client) ListIncomingWebhooks() (*Result, *AppError) {
}
}
+func (c *Client) GetAllPreferences() (*Result, *AppError) {
+ if r, err := c.DoApiGet("/preferences/", "", ""); err != nil {
+ return nil, err
+ } else {
+ preferences, _ := PreferencesFromJson(r.Body)
+ return &Result{r.Header.Get(HEADER_REQUEST_ID), r.Header.Get(HEADER_ETAG_SERVER), preferences}, nil
+ }
+}
+
+func (c *Client) SetPreferences(preferences *Preferences) (*Result, *AppError) {
+ if r, err := c.DoApiPost("/preferences/save", preferences.ToJson()); err != nil {
+ return nil, err
+ } else {
+ return &Result{r.Header.Get(HEADER_REQUEST_ID),
+ r.Header.Get(HEADER_ETAG_SERVER), preferences}, nil
+ }
+}
+
+func (c *Client) GetPreference(category string, name string) (*Result, *AppError) {
+ if r, err := c.DoApiGet("/preferences/"+category+"/"+name, "", ""); err != nil {
+ return nil, err
+ } else {
+ return &Result{r.Header.Get(HEADER_REQUEST_ID), r.Header.Get(HEADER_ETAG_SERVER), PreferenceFromJson(r.Body)}, nil
+ }
+}
+
+func (c *Client) GetPreferenceCategory(category string) (*Result, *AppError) {
+ if r, err := c.DoApiGet("/preferences/"+category, "", ""); err != nil {
+ return nil, err
+ } else {
+ preferences, _ := PreferencesFromJson(r.Body)
+ return &Result{r.Header.Get(HEADER_REQUEST_ID), r.Header.Get(HEADER_ETAG_SERVER), preferences}, nil
+ }
+}
+
+func (c *Client) CreateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppError) {
+ if r, err := c.DoApiPost("/hooks/outgoing/create", hook.ToJson()); err != nil {
+ return nil, err
+ } else {
+ return &Result{r.Header.Get(HEADER_REQUEST_ID),
+ r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookFromJson(r.Body)}, nil
+ }
+}
+
+func (c *Client) DeleteOutgoingWebhook(data map[string]string) (*Result, *AppError) {
+ if r, err := c.DoApiPost("/hooks/outgoing/delete", MapToJson(data)); err != nil {
+ return nil, err
+ } else {
+ return &Result{r.Header.Get(HEADER_REQUEST_ID),
+ r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
+ }
+}
+
+func (c *Client) ListOutgoingWebhooks() (*Result, *AppError) {
+ if r, err := c.DoApiGet("/hooks/outgoing/list", "", ""); err != nil {
+ return nil, err
+ } else {
+ return &Result{r.Header.Get(HEADER_REQUEST_ID),
+ r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookListFromJson(r.Body)}, nil
+ }
+}
+
+func (c *Client) RegenOutgoingWebhookToken(data map[string]string) (*Result, *AppError) {
+ if r, err := c.DoApiPost("/hooks/outgoing/regen_token", MapToJson(data)); err != nil {
+ return nil, err
+ } else {
+ return &Result{r.Header.Get(HEADER_REQUEST_ID),
+ r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookFromJson(r.Body)}, nil
+ }
+}
+
func (c *Client) MockSession(sessionToken string) {
c.AuthToken = sessionToken
c.AuthType = HEADER_BEARER
diff --git a/model/config.go b/model/config.go
index 59caf0884..3a39df2f1 100644
--- a/model/config.go
+++ b/model/config.go
@@ -29,6 +29,7 @@ type ServiceSettings struct {
GoogleDeveloperKey string
EnableOAuthServiceProvider bool
EnableIncomingWebhooks bool
+ EnableOutgoingWebhooks bool
EnablePostUsernameOverride bool
EnablePostIconOverride bool
EnableTesting bool
diff --git a/model/webhook.go b/model/incoming_webhook.go
index 3bf034908..9b9969b96 100644
--- a/model/webhook.go
+++ b/model/incoming_webhook.go
@@ -8,6 +8,11 @@ import (
"io"
)
+const (
+ DEFAULT_WEBHOOK_USERNAME = "webhook"
+ DEFAULT_WEBHOOK_ICON = "/static/images/webhook_icon.jpg"
+)
+
type IncomingWebhook struct {
Id string `json:"id"`
CreateAt int64 `json:"create_at"`
diff --git a/model/webhook_test.go b/model/incoming_webhook_test.go
index 5297d7d90..5297d7d90 100644
--- a/model/webhook_test.go
+++ b/model/incoming_webhook_test.go
diff --git a/model/message.go b/model/message.go
index 122af4d9c..2725353ac 100644
--- a/model/message.go
+++ b/model/message.go
@@ -9,14 +9,14 @@ import (
)
const (
- ACTION_TYPING = "typing"
- ACTION_POSTED = "posted"
- ACTION_POST_EDITED = "post_edited"
- ACTION_POST_DELETED = "post_deleted"
- ACTION_VIEWED = "viewed"
- ACTION_NEW_USER = "new_user"
- ACTION_USER_ADDED = "user_added"
- ACTION_USER_REMOVED = "user_removed"
+ ACTION_TYPING = "typing"
+ ACTION_POSTED = "posted"
+ ACTION_POST_EDITED = "post_edited"
+ ACTION_POST_DELETED = "post_deleted"
+ ACTION_CHANNEL_VIEWED = "channel_viewed"
+ ACTION_NEW_USER = "new_user"
+ ACTION_USER_ADDED = "user_added"
+ ACTION_USER_REMOVED = "user_removed"
)
type Message struct {
diff --git a/model/outgoing_webhook.go b/model/outgoing_webhook.go
new file mode 100644
index 000000000..8958dd5b0
--- /dev/null
+++ b/model/outgoing_webhook.go
@@ -0,0 +1,135 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+)
+
+type OutgoingWebhook struct {
+ Id string `json:"id"`
+ Token string `json:"token"`
+ CreateAt int64 `json:"create_at"`
+ UpdateAt int64 `json:"update_at"`
+ DeleteAt int64 `json:"delete_at"`
+ CreatorId string `json:"creator_id"`
+ ChannelId string `json:"channel_id"`
+ TeamId string `json:"team_id"`
+ TriggerWords StringArray `json:"trigger_words"`
+ CallbackURLs StringArray `json:"callback_urls"`
+}
+
+func (o *OutgoingWebhook) ToJson() string {
+ b, err := json.Marshal(o)
+ if err != nil {
+ return ""
+ } else {
+ return string(b)
+ }
+}
+
+func OutgoingWebhookFromJson(data io.Reader) *OutgoingWebhook {
+ decoder := json.NewDecoder(data)
+ var o OutgoingWebhook
+ err := decoder.Decode(&o)
+ if err == nil {
+ return &o
+ } else {
+ return nil
+ }
+}
+
+func OutgoingWebhookListToJson(l []*OutgoingWebhook) string {
+ b, err := json.Marshal(l)
+ if err != nil {
+ return ""
+ } else {
+ return string(b)
+ }
+}
+
+func OutgoingWebhookListFromJson(data io.Reader) []*OutgoingWebhook {
+ decoder := json.NewDecoder(data)
+ var o []*OutgoingWebhook
+ err := decoder.Decode(&o)
+ if err == nil {
+ return o
+ } else {
+ return nil
+ }
+}
+
+func (o *OutgoingWebhook) IsValid() *AppError {
+
+ if len(o.Id) != 26 {
+ return NewAppError("OutgoingWebhook.IsValid", "Invalid Id", "")
+ }
+
+ if len(o.Token) != 26 {
+ return NewAppError("OutgoingWebhook.IsValid", "Invalid token", "")
+ }
+
+ if o.CreateAt == 0 {
+ return NewAppError("OutgoingWebhook.IsValid", "Create at must be a valid time", "id="+o.Id)
+ }
+
+ if o.UpdateAt == 0 {
+ return NewAppError("OutgoingWebhook.IsValid", "Update at must be a valid time", "id="+o.Id)
+ }
+
+ if len(o.CreatorId) != 26 {
+ return NewAppError("OutgoingWebhook.IsValid", "Invalid user id", "")
+ }
+
+ if len(o.ChannelId) != 0 && len(o.ChannelId) != 26 {
+ return NewAppError("OutgoingWebhook.IsValid", "Invalid channel id", "")
+ }
+
+ if len(o.TeamId) != 26 {
+ return NewAppError("OutgoingWebhook.IsValid", "Invalid team id", "")
+ }
+
+ if len(fmt.Sprintf("%s", o.TriggerWords)) > 1024 {
+ return NewAppError("OutgoingWebhook.IsValid", "Invalid trigger words", "")
+ }
+
+ if len(o.CallbackURLs) == 0 || len(fmt.Sprintf("%s", o.CallbackURLs)) > 1024 {
+ return NewAppError("OutgoingWebhook.IsValid", "Invalid callback urls", "")
+ }
+
+ return nil
+}
+
+func (o *OutgoingWebhook) PreSave() {
+ if o.Id == "" {
+ o.Id = NewId()
+ }
+
+ if o.Token == "" {
+ o.Token = NewId()
+ }
+
+ o.CreateAt = GetMillis()
+ o.UpdateAt = o.CreateAt
+}
+
+func (o *OutgoingWebhook) PreUpdate() {
+ o.UpdateAt = GetMillis()
+}
+
+func (o *OutgoingWebhook) HasTriggerWord(word string) bool {
+ if len(o.TriggerWords) == 0 || len(word) == 0 {
+ return false
+ }
+
+ for _, trigger := range o.TriggerWords {
+ if trigger == word {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/model/outgoing_webhook_test.go b/model/outgoing_webhook_test.go
new file mode 100644
index 000000000..2ca48c291
--- /dev/null
+++ b/model/outgoing_webhook_test.go
@@ -0,0 +1,97 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestOutgoingWebhookJson(t *testing.T) {
+ o := OutgoingWebhook{Id: NewId()}
+ json := o.ToJson()
+ ro := OutgoingWebhookFromJson(strings.NewReader(json))
+
+ if o.Id != ro.Id {
+ t.Fatal("Ids do not match")
+ }
+}
+
+func TestOutgoingWebhookIsValid(t *testing.T) {
+ o := OutgoingWebhook{}
+
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.Id = NewId()
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.CreateAt = GetMillis()
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.UpdateAt = GetMillis()
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.CreatorId = "123"
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.CreatorId = NewId()
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.Token = "123"
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.Token = NewId()
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.ChannelId = "123"
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.ChannelId = NewId()
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.TeamId = "123"
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.TeamId = NewId()
+ 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)
+ }
+}
+
+func TestOutgoingWebhookPreSave(t *testing.T) {
+ o := OutgoingWebhook{}
+ o.PreSave()
+}
+
+func TestOutgoingWebhookPreUpdate(t *testing.T) {
+ o := OutgoingWebhook{}
+ o.PreUpdate()
+}
diff --git a/model/preference.go b/model/preference.go
new file mode 100644
index 000000000..44279f71a
--- /dev/null
+++ b/model/preference.go
@@ -0,0 +1,60 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+)
+
+const (
+ PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show"
+)
+
+type Preference struct {
+ UserId string `json:"user_id"`
+ Category string `json:"category"`
+ Name string `json:"name"`
+ Value string `json:"value"`
+}
+
+func (o *Preference) ToJson() string {
+ b, err := json.Marshal(o)
+ if err != nil {
+ return ""
+ } else {
+ return string(b)
+ }
+}
+
+func PreferenceFromJson(data io.Reader) *Preference {
+ decoder := json.NewDecoder(data)
+ var o Preference
+ err := decoder.Decode(&o)
+ if err == nil {
+ return &o
+ } else {
+ return nil
+ }
+}
+
+func (o *Preference) IsValid() *AppError {
+ if len(o.UserId) != 26 {
+ return NewAppError("Preference.IsValid", "Invalid user id", "user_id="+o.UserId)
+ }
+
+ if len(o.Category) == 0 || len(o.Category) > 32 {
+ return NewAppError("Preference.IsValid", "Invalid category", "category="+o.Category)
+ }
+
+ if len(o.Name) == 0 || len(o.Name) > 32 {
+ return NewAppError("Preference.IsValid", "Invalid name", "name="+o.Name)
+ }
+
+ if len(o.Value) > 128 {
+ return NewAppError("Preference.IsValid", "Value is too long", "value="+o.Value)
+ }
+
+ return nil
+}
diff --git a/model/preference_test.go b/model/preference_test.go
new file mode 100644
index 000000000..66b7ac50b
--- /dev/null
+++ b/model/preference_test.go
@@ -0,0 +1,56 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestPreferenceIsValid(t *testing.T) {
+ preference := Preference{
+ UserId: "1234garbage",
+ Category: PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW,
+ Name: NewId(),
+ }
+
+ if err := preference.IsValid(); err == nil {
+ t.Fatal()
+ }
+
+ preference.UserId = NewId()
+ if err := preference.IsValid(); err != nil {
+ t.Fatal(err)
+ }
+
+ preference.Category = strings.Repeat("01234567890", 20)
+ if err := preference.IsValid(); err == nil {
+ t.Fatal()
+ }
+
+ preference.Category = PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW
+ if err := preference.IsValid(); err != nil {
+ t.Fatal()
+ }
+
+ preference.Name = strings.Repeat("01234567890", 20)
+ if err := preference.IsValid(); err == nil {
+ t.Fatal()
+ }
+
+ preference.Name = NewId()
+ if err := preference.IsValid(); err != nil {
+ t.Fatal()
+ }
+
+ preference.Value = strings.Repeat("01234567890", 20)
+ if err := preference.IsValid(); err == nil {
+ t.Fatal()
+ }
+
+ preference.Value = "1234garbage"
+ if err := preference.IsValid(); err != nil {
+ t.Fatal()
+ }
+}
diff --git a/model/preferences.go b/model/preferences.go
new file mode 100644
index 000000000..1ef16151f
--- /dev/null
+++ b/model/preferences.go
@@ -0,0 +1,31 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+)
+
+type Preferences []Preference
+
+func (o *Preferences) ToJson() string {
+ b, err := json.Marshal(o)
+ if err != nil {
+ return ""
+ } else {
+ return string(b)
+ }
+}
+
+func PreferencesFromJson(data io.Reader) (Preferences, error) {
+ decoder := json.NewDecoder(data)
+ var o Preferences
+ err := decoder.Decode(&o)
+ if err == nil {
+ return o, nil
+ } else {
+ return nil, err
+ }
+}
diff --git a/model/search_params.go b/model/search_params.go
new file mode 100644
index 000000000..7eeeed10f
--- /dev/null
+++ b/model/search_params.go
@@ -0,0 +1,130 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "strings"
+)
+
+type SearchParams struct {
+ Terms string
+ IsHashtag bool
+ InChannel string
+ FromUser string
+}
+
+var searchFlags = [...]string{"from", "channel", "in"}
+
+func splitWords(text string) []string {
+ words := []string{}
+
+ for _, word := range strings.Fields(text) {
+ word = puncStart.ReplaceAllString(word, "")
+ word = puncEnd.ReplaceAllString(word, "")
+
+ if len(word) != 0 {
+ words = append(words, word)
+ }
+ }
+
+ return words
+}
+
+func parseSearchFlags(input []string) ([]string, map[string]string) {
+ words := []string{}
+ flags := make(map[string]string)
+
+ skipNextWord := false
+ for i, word := range input {
+ if skipNextWord {
+ skipNextWord = false
+ continue
+ }
+
+ isFlag := false
+
+ if colon := strings.Index(word, ":"); colon != -1 {
+ flag := word[:colon]
+ value := word[colon+1:]
+
+ for _, searchFlag := range searchFlags {
+ // check for case insensitive equality
+ if strings.EqualFold(flag, searchFlag) {
+ if value != "" {
+ flags[searchFlag] = value
+ isFlag = true
+ } else if i < len(input)-1 {
+ flags[searchFlag] = input[i+1]
+ skipNextWord = true
+ isFlag = true
+ }
+
+ if isFlag {
+ break
+ }
+ }
+ }
+ }
+
+ if !isFlag {
+ words = append(words, word)
+ }
+ }
+
+ return words, flags
+}
+
+func ParseSearchParams(text string) (*SearchParams, *SearchParams) {
+ words, flags := parseSearchFlags(splitWords(text))
+
+ hashtagTerms := []string{}
+ plainTerms := []string{}
+
+ for _, word := range words {
+ if validHashtag.MatchString(word) {
+ hashtagTerms = append(hashtagTerms, word)
+ } else {
+ plainTerms = append(plainTerms, word)
+ }
+ }
+
+ inChannel := flags["channel"]
+ if inChannel == "" {
+ inChannel = flags["in"]
+ }
+
+ fromUser := flags["from"]
+
+ var plainParams *SearchParams
+ if len(plainTerms) > 0 {
+ plainParams = &SearchParams{
+ Terms: strings.Join(plainTerms, " "),
+ IsHashtag: false,
+ InChannel: inChannel,
+ FromUser: fromUser,
+ }
+ }
+
+ var hashtagParams *SearchParams
+ if len(hashtagTerms) > 0 {
+ hashtagParams = &SearchParams{
+ Terms: strings.Join(hashtagTerms, " "),
+ IsHashtag: true,
+ InChannel: inChannel,
+ FromUser: fromUser,
+ }
+ }
+
+ // 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,
+ }
+ }
+
+ return plainParams, hashtagParams
+}
diff --git a/model/search_params_test.go b/model/search_params_test.go
new file mode 100644
index 000000000..2eba20f4c
--- /dev/null
+++ b/model/search_params_test.go
@@ -0,0 +1,70 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "testing"
+)
+
+func TestParseSearchFlags(t *testing.T) {
+ if words, flags := parseSearchFlags(splitWords("")); len(words) != 0 {
+ t.Fatal("got words from empty input")
+ } else if len(flags) != 0 {
+ t.Fatal("got flags from empty input")
+ }
+
+ if words, flags := parseSearchFlags(splitWords("word")); len(words) != 1 || words[0] != "word" {
+ t.Fatalf("got incorrect words %v", words)
+ } else if len(flags) != 0 {
+ t.Fatalf("got incorrect flags %v", flags)
+ }
+
+ if words, flags := parseSearchFlags(splitWords("apple banana cherry")); len(words) != 3 || words[0] != "apple" || words[1] != "banana" || words[2] != "cherry" {
+ t.Fatalf("got incorrect words %v", words)
+ } else if len(flags) != 0 {
+ 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" {
+ 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" {
+ 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" {
+ 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" {
+ t.Fatalf("got incorrect flags %v", flags)
+ }
+
+ if words, flags := parseSearchFlags(splitWords("fruit: cherry")); len(words) != 2 || words[0] != "fruit:" || words[1] != "cherry" {
+ t.Fatalf("got incorrect words %v", words)
+ } else if len(flags) != 0 {
+ t.Fatalf("got incorrect flags %v", flags)
+ }
+
+ if words, flags := parseSearchFlags(splitWords("channel:")); len(words) != 1 || words[0] != "channel:" {
+ t.Fatalf("got incorrect words %v", words)
+ } else if len(flags) != 0 {
+ t.Fatalf("got incorrect flags %v", flags)
+ }
+
+ 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" {
+ t.Fatalf("got incorrect flags %v", flags)
+ }
+}
diff --git a/model/system.go b/model/system.go
index 033f660b3..70db529d5 100644
--- a/model/system.go
+++ b/model/system.go
@@ -8,6 +8,12 @@ import (
"io"
)
+const (
+ SYSTEM_DIAGNOSTIC_ID = "DiagnosticId"
+ SYSTEM_RAN_UNIT_TESTS = "RanUnitTests"
+ SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime"
+)
+
type System struct {
Name string `json:"name"`
Value string `json:"value"`
diff --git a/model/team.go b/model/team.go
index c0f6524cd..584c78f8d 100644
--- a/model/team.go
+++ b/model/team.go
@@ -219,3 +219,9 @@ func CleanTeamName(s string) string {
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 269144afc..bb0669df7 100644
--- a/model/utils.go
+++ b/model/utils.go
@@ -242,10 +242,10 @@ func Etag(parts ...interface{}) string {
var validHashtag = regexp.MustCompile(`^(#[A-Za-z]+[A-Za-z0-9_\-]*[A-Za-z0-9])$`)
var puncStart = regexp.MustCompile(`^[.,()&$!\[\]{}"':;\\]+`)
-var puncEnd = regexp.MustCompile(`[.,()&$#!\[\]{}"':;\\]+$`)
+var puncEnd = regexp.MustCompile(`[.,()&$#!\[\]{}"';\\]+$`)
func ParseHashtags(text string) (string, string) {
- words := strings.Split(strings.Replace(text, "\n", " ", -1), " ")
+ words := strings.Fields(text)
hashtagString := ""
plainString := ""