diff --git a/api/post.go b/api/post.go index d0ec5826ab..36fd4ee79e 100644 --- a/api/post.go +++ b/api/post.go @@ -670,8 +670,15 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel * alreadySeen[session.DeviceId] = session.DeviceId msg := model.PushNotification{} - msg.Badge = 1 + if badge := <-Srv.Store.User().GetUnreadCount(id); badge.Err != nil { + msg.Badge = 1 + l4g.Error(utils.T("store.sql_user.get_unread_count.app_error"), id, badge.Err) + } else { + msg.Badge = int(badge.Data.(int64)) + } msg.ServerId = utils.CfgDiagnosticId + msg.ChannelId = channel.Id + msg.ChannelName = channel.Name if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") { msg.Platform = model.PUSH_NOTIFY_APPLE @@ -681,10 +688,20 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel * msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":") } - if channel.Type == model.CHANNEL_DIRECT { - msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message") + if *utils.Cfg.EmailSettings.PushNotificationContents == model.FULL_NOTIFICATION { + if channel.Type == model.CHANNEL_DIRECT { + msg.Category = model.CATEGORY_DM + msg.Message = "@" + senderName + ": " + model.ClearMentionTags(post.Message) + } else { + msg.Message = "@" + senderName + " @ " + channelName + ": " + model.ClearMentionTags(post.Message) + } } else { - msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention") + channelName + if channel.Type == model.CHANNEL_DIRECT { + msg.Category = model.CATEGORY_DM + msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message") + } else { + msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention") + channelName + } } tr := &http.Transport{ diff --git a/config/config.json b/config/config.json index 449ed74ecd..a0b50cbf2d 100644 --- a/config/config.json +++ b/config/config.json @@ -87,6 +87,7 @@ "InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9YoS", "PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL", "SendPushNotifications": false, + "PushNotificationContents": "generic", "PushNotificationServer": "" }, "RateLimitSettings": { diff --git a/i18n/en.json b/i18n/en.json index 5e7cd2957d..25a435580a 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -3107,6 +3107,10 @@ "id": "store.sql_user.get_total_users_count.app_error", "translation": "We could not count the users" }, + { + "id": "store.sql_user.get_unread_count.app_error", + "translation": "We could not get the unread message count for the user" + }, { "id": "store.sql_user.missing_account.const", "translation": "We couldn't find an existing account matching your email address for this team. This team may require an invite from the team owner to join." diff --git a/i18n/es.json b/i18n/es.json index 894bf0c18e..231ebd9fbb 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -3107,6 +3107,10 @@ "id": "store.sql_user.get_total_users_count.app_error", "translation": "No pudimos contar los usuarios" }, + { + "id": "store.sql_user.get_unread_count.app_error", + "translation": "No pudimos obtener la cantidad de mensajes sin leer del usuario" + }, { "id": "store.sql_user.missing_account.const", "translation": "No pudimos encontrar una cuenta existente que coincida con tu dirección de correo electrónico para este equipo. Es posible que necesites una invitación del dueño del equipo para poder unirte." diff --git a/model/config.go b/model/config.go index 11ce260ee2..d684c72b29 100644 --- a/model/config.go +++ b/model/config.go @@ -25,6 +25,9 @@ const ( WEBSERVER_MODE_REGULAR = "regular" WEBSERVER_MODE_GZIP = "gzip" WEBSERVER_MODE_DISABLED = "disabled" + + GENERIC_NOTIFICATION = "generic" + FULL_NOTIFICATION = "full" ) type ServiceSettings struct { @@ -121,6 +124,7 @@ type EmailSettings struct { PasswordResetSalt string SendPushNotifications *bool PushNotificationServer *string + PushNotificationContents *string } type RateLimitSettings struct { @@ -299,6 +303,11 @@ func (o *Config) SetDefaults() { *o.EmailSettings.PushNotificationServer = "" } + if o.EmailSettings.PushNotificationContents == nil { + o.EmailSettings.PushNotificationContents = new(string) + *o.EmailSettings.PushNotificationContents = GENERIC_NOTIFICATION + } + if o.SupportSettings.TermsOfServiceLink == nil { o.SupportSettings.TermsOfServiceLink = new(string) *o.SupportSettings.TermsOfServiceLink = "/static/help/terms.html" diff --git a/model/push_notification.go b/model/push_notification.go index 76f5bd1258..9196a44dd8 100644 --- a/model/push_notification.go +++ b/model/push_notification.go @@ -11,6 +11,8 @@ import ( const ( PUSH_NOTIFY_APPLE = "apple" PUSH_NOTIFY_ANDROID = "android" + + CATEGORY_DM = "DIRECT_MESSAGE" ) type PushNotification struct { @@ -22,6 +24,8 @@ type PushNotification struct { Message string `json:"message"` Badge int `json:"badge"` ContentAvailable int `json:"cont_ava"` + ChannelId string `json:"channel_id"` + ChannelName string `json:"channel_name"` } func (me *PushNotification) ToJson() string { diff --git a/model/utils.go b/model/utils.go index 808c89e302..1ce41bb303 100644 --- a/model/utils.go +++ b/model/utils.go @@ -367,3 +367,15 @@ func IsValidHttpUrl(rawUrl string) bool { return true } + +func IsValidHttpsUrl(rawUrl string) bool { + if strings.Index(rawUrl, "https://") != 0 { + return false + } + + if _, err := url.ParseRequestURI(rawUrl); err != nil { + return false + } + + return true +} diff --git a/store/sql_user_store.go b/store/sql_user_store.go index cc6829b94c..6062b8a6af 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -652,3 +652,22 @@ func (us SqlUserStore) AnalyticsUniqueUserCount(teamId string) StoreChannel { return storeChannel } + +func (us SqlUserStore) GetUnreadCount(userId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + if count, err := us.GetReplica().SelectInt("SELECT SUM(CASE WHEN c.Type = 'D' THEN (c.TotalMsgCount - cm.MsgCount) ELSE 0 END + cm.MentionCount) FROM Channels c INNER JOIN ChannelMembers cm ON cm.ChannelId = c.Id AND cm.UserId = :UserId", map[string]interface{}{"UserId": userId}); err != nil { + result.Err = model.NewLocAppError("SqlUserStore.GetMentionCount", "store.sql_user.get_unread_count.app_error", nil, err.Error()) + } else { + result.Data = count + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_user_store_test.go b/store/sql_user_store_test.go index 2350bad309..8f23661364 100644 --- a/store/sql_user_store_test.go +++ b/store/sql_user_store_test.go @@ -421,3 +421,84 @@ func TestUserStoreUpdateAuthData(t *testing.T) { } } } + +func TestUserUnreadCount(t *testing.T) { + Setup() + + teamId := model.NewId() + + c1 := model.Channel{} + c1.TeamId = teamId + c1.DisplayName = "Unread Messages" + c1.Name = "unread-messages" + c1.Type = model.CHANNEL_OPEN + + c2 := model.Channel{} + c2.TeamId = teamId + c2.DisplayName = "Unread Direct" + c2.Name = "unread-direct" + c2.Type = model.CHANNEL_DIRECT + + u1 := model.User{} + u1.Email = model.NewId() + u1.Username = "user1" + u1.TeamId = teamId + Must(store.User().Save(&u1)) + + u2 := model.User{} + u2.Email = model.NewId() + u2.Username = "user2" + u2.TeamId = teamId + Must(store.User().Save(&u2)) + + if err := (<-store.Channel().Save(&c1)).Err; err != nil { + t.Fatal("couldn't save item", err) + } + + m1 := model.ChannelMember{} + m1.ChannelId = c1.Id + m1.UserId = u1.Id + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + + m2 := model.ChannelMember{} + m2.ChannelId = c1.Id + m2.UserId = u2.Id + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + + Must(store.Channel().SaveMember(&m1)) + Must(store.Channel().SaveMember(&m2)) + + m1.ChannelId = c2.Id + m2.ChannelId = c2.Id + + if err := (<-store.Channel().SaveDirectChannel(&c2, &m1, &m2)).Err; err != nil { + t.Fatal("couldn't save direct channel", err) + } + + p1 := model.Post{} + p1.ChannelId = c1.Id + p1.UserId = u1.Id + p1.Message = "this is a message for @" + u2.Username + + // Post one message with mention to open channel + Must(store.Post().Save(&p1)) + Must(store.Channel().IncrementMentionCount(c1.Id, u2.Id)) + + // Post 2 messages without mention to direct channel + p2 := model.Post{} + p2.ChannelId = c2.Id + p2.UserId = u1.Id + p2.Message = "first message" + Must(store.Post().Save(&p2)) + + p3 := model.Post{} + p3.ChannelId = c2.Id + p3.UserId = u1.Id + p3.Message = "second message" + Must(store.Post().Save(&p3)) + + badge := (<-store.User().GetUnreadCount(u2.Id)).Data.(int64) + if badge != 3 { + t.Fatal("should have 3 unread messages") + } +} diff --git a/store/store.go b/store/store.go index b041cfa254..1738ba84ec 100644 --- a/store/store.go +++ b/store/store.go @@ -130,6 +130,8 @@ type UserStore interface { GetSystemAdminProfiles() StoreChannel PermanentDelete(userId string) StoreChannel AnalyticsUniqueUserCount(teamId string) StoreChannel + + GetUnreadCount(userId string) StoreChannel } type SessionStore interface { diff --git a/webapp/components/admin_console/email_settings.jsx b/webapp/components/admin_console/email_settings.jsx index 1decdae91c..e591b636df 100644 --- a/webapp/components/admin_console/email_settings.jsx +++ b/webapp/components/admin_console/email_settings.jsx @@ -58,6 +58,14 @@ var holders = defineMessages({ id: 'admin.email.pushServerEx', defaultMessage: 'E.g.: "http://push-test.mattermost.com"' }, + genericPush: { + id: 'admin.email.genericPushNotification', + defaultMessage: 'Send generic description with user and channel names' + }, + fullPush: { + id: 'admin.email.fullPushNotification', + defaultMessage: 'Send full message snippet' + }, testing: { id: 'admin.email.testing', defaultMessage: 'Testing...' @@ -87,7 +95,8 @@ class EmailSettings extends React.Component { saveNeeded: false, serverError: null, emailSuccess: null, - emailFail: null + emailFail: null, + pushNotificationContents: this.props.config.EmailSettings.PushNotificationContents }; } @@ -125,6 +134,7 @@ class EmailSettings extends React.Component { config.EmailSettings.FeedbackEmail = ReactDOM.findDOMNode(this.refs.feedbackEmail).value.trim(); config.EmailSettings.SMTPServer = ReactDOM.findDOMNode(this.refs.SMTPServer).value.trim(); config.EmailSettings.PushNotificationServer = ReactDOM.findDOMNode(this.refs.PushNotificationServer).value.trim(); + config.EmailSettings.PushNotificationContents = ReactDOM.findDOMNode(this.refs.PushNotificationContents).value; config.EmailSettings.SMTPPort = ReactDOM.findDOMNode(this.refs.SMTPPort).value.trim(); config.EmailSettings.SMTPUsername = ReactDOM.findDOMNode(this.refs.SMTPUsername).value.trim(); config.EmailSettings.SMTPPassword = ReactDOM.findDOMNode(this.refs.SMTPPassword).value.trim(); @@ -928,6 +938,38 @@ class EmailSettings extends React.Component { +
+