summaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/channel.go38
-rw-r--r--api/post.go142
-rw-r--r--api/post_test.go48
-rw-r--r--api/preference.go2
-rw-r--r--api/preference_test.go2
-rw-r--r--api/templates/post_body.html2
-rw-r--r--api/user.go8
-rw-r--r--api/web_team_hub.go4
8 files changed, 210 insertions, 36 deletions
diff --git a/api/channel.go b/api/channel.go
index 99640e71a..f17594c0a 100644
--- a/api/channel.go
+++ b/api/channel.go
@@ -268,19 +268,51 @@ func updateChannelHeader(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.HasPermissionsToTeam(channel.TeamId, "updateChannelHeader") {
return
}
-
+ oldChannelHeader := channel.Header
channel.Header = channelHeader
if ucresult := <-Srv.Store.Channel().Update(channel); ucresult.Err != nil {
c.Err = ucresult.Err
return
} else {
+ PostUpdateChannelHeaderMessageAndForget(c, channel.Id, oldChannelHeader, channelHeader)
c.LogAudit("name=" + channel.Name)
w.Write([]byte(channel.ToJson()))
}
}
}
+func PostUpdateChannelHeaderMessageAndForget(c *Context, channelId string, oldChannelHeader, newChannelHeader string) {
+ go func() {
+ uc := Srv.Store.User().Get(c.Session.UserId)
+
+ if uresult := <-uc; uresult.Err != nil {
+ l4g.Error("Failed to retrieve user while trying to save update channel header message %v", uresult.Err)
+ return
+ } else {
+ user := uresult.Data.(*model.User)
+
+ var message string
+ if oldChannelHeader == "" {
+ message = fmt.Sprintf("%s updated the channel header to: %s", user.Username, newChannelHeader)
+ } else if newChannelHeader == "" {
+ message = fmt.Sprintf("%s removed the channel header (was: %s)", user.Username, oldChannelHeader)
+ } else {
+ message = fmt.Sprintf("%s updated the channel header from: %s to: %s", user.Username, oldChannelHeader, newChannelHeader)
+ }
+
+ post := &model.Post{
+ ChannelId: channelId,
+ Message: message,
+ Type: model.POST_HEADER_CHANGE,
+ }
+ if _, err := CreatePost(c, post, false); err != nil {
+ l4g.Error("Failed to post join/leave message %v", err)
+ }
+ }
+ }()
+}
+
func updateChannelPurpose(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)
channelId := props["channel_id"]
@@ -421,7 +453,7 @@ func JoinChannel(c *Context, channelId string, role string) {
c.Err = err
return
}
- PostUserAddRemoveMessageAndForget(c, channel.Id, fmt.Sprintf(`User %v has joined this channel.`, user.Username))
+ PostUserAddRemoveMessageAndForget(c, channel.Id, fmt.Sprintf(`%v has joined the channel.`, user.Username))
} else {
c.Err = model.NewAppError("join", "You do not have the appropriate permissions", "")
c.Err.StatusCode = http.StatusForbidden
@@ -708,7 +740,7 @@ func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) {
}
scm := Srv.Store.Channel().GetMember(id, c.Session.UserId)
- ecm := Srv.Store.Channel().GetExtraMembers(id, 20)
+ ecm := Srv.Store.Channel().GetExtraMembers(id, 100)
ccm := Srv.Store.Channel().GetMemberCount(id)
if cmresult := <-scm; cmresult.Err != nil {
diff --git a/api/post.go b/api/post.go
index 81cc9a1c6..a102cdf4d 100644
--- a/api/post.go
+++ b/api/post.go
@@ -153,9 +153,6 @@ func CreateWebhookPost(c *Context, channelId, text, overrideUsername, overrideIc
linkWithTextRegex := regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`)
text = linkWithTextRegex.ReplaceAllString(text, "[${2}](${1})")
- linkRegex := regexp.MustCompile(`<\s*(\S*)\s*>`)
- text = linkRegex.ReplaceAllString(text, "${1}")
-
post := &model.Post{UserId: c.Session.UserId, ChannelId: channelId, Message: text, Type: postType}
post.AddProp("from_webhook", "true")
@@ -177,7 +174,21 @@ func CreateWebhookPost(c *Context, channelId, text, overrideUsername, overrideIc
if len(props) > 0 {
for key, val := range props {
- if key != "override_icon_url" && key != "override_username" && key != "from_webhook" {
+ if key == "attachments" {
+ if list, success := val.([]interface{}); success {
+ // parse attachment links into Markdown format
+ for i, aInt := range list {
+ attachment := aInt.(map[string]interface{})
+ if _, ok := attachment["text"]; ok {
+ aText := attachment["text"].(string)
+ aText = linkWithTextRegex.ReplaceAllString(aText, "[${2}](${1})")
+ attachment["text"] = aText
+ list[i] = attachment
+ }
+ }
+ post.AddProp(key, list)
+ }
+ } else if key != "override_icon_url" && key != "override_username" && key != "from_webhook" {
post.AddProp(key, val)
}
}
@@ -225,9 +236,68 @@ func handlePostEventsAndForget(c *Context, post *model.Post, triggerWebhooks boo
if triggerWebhooks {
handleWebhookEventsAndForget(c, post, team, channel, user)
}
+
+ if channel.Type == model.CHANNEL_DIRECT {
+ go makeDirectChannelVisible(c.Session.TeamId, post.ChannelId)
+ }
}()
}
+func makeDirectChannelVisible(teamId string, channelId string) {
+ var members []model.ChannelMember
+ if result := <-Srv.Store.Channel().GetMembers(channelId); result.Err != nil {
+ l4g.Error("Failed to get channel members channel_id=%v err=%v", channelId, result.Err.Message)
+ return
+ } else {
+ members = result.Data.([]model.ChannelMember)
+ }
+
+ if len(members) != 2 {
+ l4g.Error("Failed to get 2 members for a direct channel channel_id=%v", channelId)
+ return
+ }
+
+ // make sure the channel is visible to both members
+ for i, member := range members {
+ otherUserId := members[1-i].UserId
+
+ if result := <-Srv.Store.Preference().Get(member.UserId, model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, otherUserId); result.Err != nil {
+ // create a new preference since one doesn't exist yet
+ preference := &model.Preference{
+ UserId: member.UserId,
+ Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW,
+ Name: otherUserId,
+ Value: "true",
+ }
+
+ if saveResult := <-Srv.Store.Preference().Save(&model.Preferences{*preference}); saveResult.Err != nil {
+ l4g.Error("Failed to save direct channel preference user_id=%v other_user_id=%v err=%v", member.UserId, otherUserId, saveResult.Err.Message)
+ } else {
+ message := model.NewMessage(teamId, channelId, member.UserId, model.ACTION_PREFERENCE_CHANGED)
+ message.Add("preference", preference.ToJson())
+
+ PublishAndForget(message)
+ }
+ } else {
+ preference := result.Data.(model.Preference)
+
+ if preference.Value != "true" {
+ // update the existing preference to make the channel visible
+ preference.Value = "true"
+
+ if updateResult := <-Srv.Store.Preference().Save(&model.Preferences{preference}); updateResult.Err != nil {
+ l4g.Error("Failed to update direct channel preference user_id=%v other_user_id=%v err=%v", member.UserId, otherUserId, updateResult.Err.Message)
+ } else {
+ message := model.NewMessage(teamId, channelId, member.UserId, model.ACTION_PREFERENCE_CHANGED)
+ message.Add("preference", preference.ToJson())
+
+ PublishAndForget(message)
+ }
+ }
+ }
+ }
+}
+
func handleWebhookEventsAndForget(c *Context, post *model.Post, team *model.Team, channel *model.Channel, user *model.User) {
go func() {
if !utils.Cfg.ServiceSettings.EnableOutgoingWebhooks {
@@ -421,35 +491,52 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
splitF := func(c rune) bool {
return model.SplitRunes[c]
}
- splitMessage := strings.FieldsFunc(post.Message, splitF)
+ splitMessage := strings.Fields(post.Message)
for _, word := range splitMessage {
+ var userIds []string
// Non-case-sensitive check for regular keys
- userIds1, keyMatch := keywordMap[strings.ToLower(word)]
+ if ids, match := keywordMap[strings.ToLower(word)]; match {
+ userIds = append(userIds, ids...)
+ }
// Case-sensitive check for first name
- userIds2, firstNameMatch := keywordMap[word]
+ if ids, match := keywordMap[word]; match {
+ userIds = append(userIds, ids...)
+ }
- userIds := append(userIds1, userIds2...)
+ if len(userIds) == 0 {
+ // No matches were found with the string split just on whitespace so try further splitting
+ // the message on punctuation
+ splitWords := strings.FieldsFunc(word, splitF)
- // If one of the non-case-senstive keys or the first name matches the word
- // then we add en entry to the sendEmail map
- if keyMatch || firstNameMatch {
- for _, userId := range userIds {
- if post.UserId == userId {
- continue
+ for _, splitWord := range splitWords {
+ // Non-case-sensitive check for regular keys
+ if ids, match := keywordMap[strings.ToLower(splitWord)]; match {
+ userIds = append(userIds, ids...)
}
- sendEmail := true
- if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" {
- sendEmail = false
- }
- if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) {
- toEmailMap[userId] = true
- } else {
- toEmailMap[userId] = false
+
+ // Case-sensitive check for first name
+ if ids, match := keywordMap[splitWord]; match {
+ userIds = append(userIds, ids...)
}
}
}
+
+ for _, userId := range userIds {
+ if post.UserId == userId {
+ continue
+ }
+ sendEmail := true
+ if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" {
+ sendEmail = false
+ }
+ if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) {
+ toEmailMap[userId] = true
+ } else {
+ toEmailMap[userId] = false
+ }
+ }
}
for id := range toEmailMap {
@@ -466,8 +553,7 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
teamURL := c.GetSiteURL() + "/" + team.Name
// Build and send the emails
- location, _ := time.LoadLocation("UTC")
- tm := time.Unix(post.CreateAt/1000, 0).In(location)
+ tm := time.Unix(post.CreateAt/1000, 0)
subjectPage := NewServerTemplatePage("post_subject")
subjectPage.Props["SiteURL"] = c.GetSiteURL()
@@ -499,6 +585,7 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
bodyPage.Props["Minute"] = fmt.Sprintf("%02d", tm.Minute())
bodyPage.Props["Month"] = tm.Month().String()[:3]
bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
+ bodyPage.Props["TimeZone"], _ = tm.Zone()
bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message)
bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name
@@ -550,11 +637,16 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
msg := model.PushNotification{}
msg.Platform = model.PUSH_NOTIFY_APPLE
- msg.Message = subjectPage.Render()
msg.Badge = 1
msg.DeviceId = strings.TrimPrefix(session.DeviceId, "apple:")
msg.ServerId = utils.CfgDiagnosticId
+ if channel.Type == model.CHANNEL_DIRECT {
+ msg.Message = channelName + " sent you a direct message"
+ } else {
+ msg.Message = profileMap[id].FirstName + " mentioned you in " + channelName
+ }
+
httpClient := http.Client{}
request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+"/api/v1/send_push", strings.NewReader(msg.ToJson()))
diff --git a/api/post_test.go b/api/post_test.go
index 0cb437e88..8e09ca76d 100644
--- a/api/post_test.go
+++ b/api/post_test.go
@@ -805,3 +805,51 @@ func TestFuzzyPosts(t *testing.T) {
}
}
}
+
+func TestMakeDirectChannelVisible(t *testing.T) {
+ Setup()
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user2.Id))
+
+ // user2 will be created with prefs created to show user1 in the sidebar so set that to false to get rid of it
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
+
+ preferences := &model.Preferences{
+ {
+ UserId: user2.Id,
+ Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW,
+ Name: user1.Id,
+ Value: "false",
+ },
+ }
+ Client.Must(Client.SetPreferences(preferences))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel := Client.Must(Client.CreateDirectChannel(map[string]string{"user_id": user2.Id})).Data.(*model.Channel)
+
+ makeDirectChannelVisible(team.Id, channel.Id)
+
+ if result, err := Client.GetPreference(model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, user2.Id); err != nil {
+ t.Fatal("Errored trying to set direct channel to be visible for user1")
+ } else if pref := result.Data.(*model.Preference); pref.Value != "true" {
+ t.Fatal("Failed to set direct channel to be visible for user1")
+ }
+
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
+
+ if result, err := Client.GetPreference(model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, user1.Id); err != nil {
+ t.Fatal("Errored trying to set direct channel to be visible for user2")
+ } else if pref := result.Data.(*model.Preference); pref.Value != "true" {
+ t.Fatal("Failed to set direct channel to be visible for user2")
+ }
+}
diff --git a/api/preference.go b/api/preference.go
index 6d6ac1a7f..e9c74aafe 100644
--- a/api/preference.go
+++ b/api/preference.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package api
diff --git a/api/preference_test.go b/api/preference_test.go
index 2f6204246..310b4dcdb 100644
--- a/api/preference_test.go
+++ b/api/preference_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package api
diff --git a/api/templates/post_body.html b/api/templates/post_body.html
index 182134b1a..00d4a563d 100644
--- a/api/templates/post_body.html
+++ b/api/templates/post_body.html
@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You were mentioned</h2>
- <p>CHANNEL: {{.Props.ChannelName}}<br>{{.Props.SenderName}} - {{.Props.Hour}}:{{.Props.Minute}} GMT, {{.Props.Month}} {{.Props.Day}}<br><pre style="text-align:left;font-family: 'Lato', sans-serif; white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; word-wrap: break-word;">{{.Props.PostMessage}}</pre></p>
+ <p>CHANNEL: {{.Props.ChannelName}}<br>{{.Props.SenderName}} - {{.Props.Hour}}:{{.Props.Minute}} {{.Props.TimeZone}}, {{.Props.Month}} {{.Props.Day}}<br><pre style="text-align:left;font-family: 'Lato', sans-serif; white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; word-wrap: break-word;">{{.Props.PostMessage}}</pre></p>
<p style="margin: 20px 0 15px">
<a href="{{.Props.TeamLink}}" style="background: #2389D7; display: inline-block; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 170px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Go To Channel</a>
</p>
diff --git a/api/user.go b/api/user.go
index 62947d8fd..886e38c91 100644
--- a/api/user.go
+++ b/api/user.go
@@ -661,7 +661,7 @@ func getProfiles(c *Context, w http.ResponseWriter, r *http.Request) {
profiles := result.Data.(map[string]*model.User)
for k, p := range profiles {
- options := utils.SanitizeOptions
+ options := utils.Cfg.GetSanitizeOptions()
options["passwordupdate"] = false
if c.IsSystemAdmin() {
@@ -1102,7 +1102,7 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
- options := utils.SanitizeOptions
+ options := utils.Cfg.GetSanitizeOptions()
options["passwordupdate"] = false
ruser.Sanitize(options)
w.Write([]byte(ruser.ToJson()))
@@ -1222,7 +1222,7 @@ func UpdateActive(c *Context, user *model.User, active bool) *model.User {
}
ruser := result.Data.([2]*model.User)[0]
- options := utils.SanitizeOptions
+ options := utils.Cfg.GetSanitizeOptions()
options["passwordupdate"] = false
ruser.Sanitize(options)
return ruser
@@ -1548,7 +1548,7 @@ func updateUserNotify(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAuditWithUserId(user.Id, "")
ruser := result.Data.([2]*model.User)[0]
- options := utils.SanitizeOptions
+ options := utils.Cfg.GetSanitizeOptions()
options["passwordupdate"] = false
ruser.Sanitize(options)
w.Write([]byte(ruser.ToJson()))
diff --git a/api/web_team_hub.go b/api/web_team_hub.go
index 6a25b7d3d..2c2386317 100644
--- a/api/web_team_hub.go
+++ b/api/web_team_hub.go
@@ -95,9 +95,11 @@ func ShouldSendEvent(webCon *WebConn, msg *model.Message) bool {
return false
}
} else {
- // Don't share a user's view events with other users
+ // Don't share a user's view or preference events with other users
if msg.Action == model.ACTION_CHANNEL_VIEWED {
return false
+ } else if msg.Action == model.ACTION_PREFERENCE_CHANGED {
+ return false
}
// Only report events to a user who is the subject of the event, or is in the channel of the event