diff options
Diffstat (limited to 'api/post.go')
-rw-r--r-- | api/post.go | 201 |
1 files changed, 170 insertions, 31 deletions
diff --git a/api/post.go b/api/post.go index ef70e1336..a102cdf4d 100644 --- a/api/post.go +++ b/api/post.go @@ -23,6 +23,7 @@ func InitPost(r *mux.Router) { l4g.Debug("Initializing post api routes") r.Handle("/posts/search", ApiUserRequired(searchPosts)).Methods("GET") + r.Handle("/posts/{post_id}", ApiUserRequired(getPostById)).Methods("GET") sr := r.PathPrefix("/channels/{id:[A-Za-z0-9]+}").Subrouter() sr.Handle("/create", ApiUserRequired(createPost)).Methods("POST") @@ -152,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") @@ -176,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) } } @@ -224,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 { @@ -406,9 +477,9 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team, } // Add @all to keywords if user has them turned on - if profile.NotifyProps["all"] == "true" { - keywordMap["@all"] = append(keywordMap["@all"], profile.Id) - } + // if profile.NotifyProps["all"] == "true" { + // keywordMap["@all"] = append(keywordMap["@all"], profile.Id) + // } // Add @channel to keywords if user has them turned on if profile.NotifyProps["channel"] == "true" { @@ -420,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 - } - sendEmail := true - if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" { - sendEmail = false + for _, splitWord := range splitWords { + // Non-case-sensitive check for regular keys + if ids, match := keywordMap[strings.ToLower(splitWord)]; match { + userIds = append(userIds, ids...) } - 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 { @@ -465,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() @@ -498,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 @@ -535,7 +623,7 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team, l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err) } - if len(utils.Cfg.EmailSettings.ApplePushServer) > 0 { + if *utils.Cfg.EmailSettings.SendPushNotifications { sessionChan := Srv.Store.Session().GetSessions(id) if result := <-sessionChan; result.Err != nil { l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err) @@ -544,11 +632,27 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team, alreadySeen := make(map[string]string) for _, session := range sessions { - if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" { - + if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" && strings.HasPrefix(session.DeviceId, "apple:") { alreadySeen[session.DeviceId] = session.DeviceId - utils.SendAppleNotifyAndForget(session.DeviceId, subjectPage.Render(), 1) + msg := model.PushNotification{} + msg.Platform = model.PUSH_NOTIFY_APPLE + 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())) + + if _, err := httpClient.Do(request); err != nil { + l4g.Error("Failed to send push notificationid=%v, err=%v", id, err) + } } } } @@ -768,6 +872,41 @@ func getPost(c *Context, w http.ResponseWriter, r *http.Request) { } } +func getPostById(c *Context, w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + + postId := params["post_id"] + if len(postId) != 26 { + c.SetInvalidParam("getPostById", "postId") + return + } + + if result := <-Srv.Store.Post().Get(postId); result.Err != nil { + c.Err = result.Err + return + } else { + list := result.Data.(*model.PostList) + + if len(list.Order) != 1 { + c.Err = model.NewAppError("getPostById", "Unable to get post", "") + return + } + post := list.Posts[list.Order[0]] + + cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, post.ChannelId, c.Session.UserId) + if !c.HasPermissionsToChannel(cchan, "getPostById") { + return + } + + if HandleEtag(list.Etag(), w, r) { + return + } + + w.Header().Set(model.HEADER_ETAG_SERVER, list.Etag()) + w.Write([]byte(list.ToJson())) + } +} + func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) |