summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/post.go25
-rw-r--r--config/config.json1
-rw-r--r--i18n/en.json4
-rw-r--r--i18n/es.json4
-rw-r--r--model/config.go9
-rw-r--r--model/push_notification.go4
-rw-r--r--model/utils.go12
-rw-r--r--store/sql_user_store.go19
-rw-r--r--store/sql_user_store_test.go81
-rw-r--r--store/store.go2
-rw-r--r--webapp/components/admin_console/email_settings.jsx44
-rw-r--r--webapp/i18n/en.json6
-rw-r--r--webapp/i18n/es.json6
13 files changed, 210 insertions, 7 deletions
diff --git a/api/post.go b/api/post.go
index d0ec5826a..36fd4ee79 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 449ed74ec..a0b50cbf2 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 5e7cd2957..25a435580 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -3108,6 +3108,10 @@
"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 894bf0c18..231ebd9fb 100644
--- a/i18n/es.json
+++ b/i18n/es.json
@@ -3108,6 +3108,10 @@
"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 11ce260ee..d684c72b2 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 76f5bd125..9196a44dd 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 808c89e30..1ce41bb30 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 cc6829b94..6062b8a6a 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 2350bad30..8f2366136 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 b041cfa25..1738ba84e 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 1decdae91..e591b636d 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();
@@ -929,6 +939,38 @@ class EmailSettings extends React.Component {
</div>
<div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='pushNotificationContents'
+ >
+ <FormattedMessage
+ id='admin.email.pushContentTitle'
+ defaultMessage='Push Notification Contents:'
+ />
+ </label>
+ <div className='col-sm-8'>
+ <select
+ className='form-control'
+ id='pushNotificationContents'
+ ref='PushNotificationContents'
+ defaultValue={this.props.config.EmailSettings.PushNotificationContents}
+ onChange={this.handleChange.bind(this, 'pushNotificationContents')}
+ disabled={!this.state.sendPushNotifications}
+ >
+ <option value='generic'>{formatMessage(holders.genericPush)}</option>
+ <option value='full'>{formatMessage(holders.fullPush)}</option>
+ </select>
+ <p className='help-text'>
+ <FormattedHTMLMessage
+ id='admin.email.pushContentDesc'
+ defaultMessage='Selecting "Send generic description with user and channel names" provides push notifications with generic messages, including names of users and channels but no specific details from the message text.<br /><br />
+ Selecting "Send full message snippet" sends excerpts from messages triggering notifications with specifics and may include confidential information sent in messages. If your Push Notification Service is outside your firewall, it is HIGHLY RECOMMENDED this option only be used with an "https" protocol to encrypt the connection.'
+ />
+ </p>
+ </div>
+ </div>
+
+ <div className='form-group'>
<div className='col-sm-12'>
{serverError}
<button
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index cdce75760..dc43cc019 100644
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -42,6 +42,8 @@
"admin.email.emailSettings": "Email Settings",
"admin.email.emailSuccess": "No errors were reported while sending an email. Please check your inbox to make sure.",
"admin.email.false": "false",
+ "admin.email.fullPushNotification": "Send full message snippet",
+ "admin.email.genericPushNotification": "Send generic description with user and channel names",
"admin.email.inviteSaltDescription": "32-character salt added to signing of email invites. Randomly generated on install. Click \"Re-Generate\" to create new salt.",
"admin.email.inviteSaltExample": "Ex \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"",
"admin.email.inviteSaltTitle": "Invite Salt:",
@@ -56,6 +58,8 @@
"admin.email.passwordSaltDescription": "32-character salt added to signing of password reset emails. Randomly generated on install. Click \"Re-Generate\" to create new salt.",
"admin.email.passwordSaltExample": "Ex \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"",
"admin.email.passwordSaltTitle": "Password Reset Salt:",
+ "admin.email.pushContentDesc": "Selecting \"Send generic description with user and channel names\" provides push notifications with generic messages, including names of users and channels but no specific details from the message text.<br /><br />Selecting \"Send full message snippet\" sends excerpts from messages triggering notifications with specifics and may include confidential information sent in messages. If your Push Notification Service is outside your firewall, it is HIGHLY RECOMMENDED this option only be used with an \"https\" protocol to encrypt the connection.",
+ "admin.email.pushContentTitle": "Push Notification Contents:",
"admin.email.pushDesc": "Typically set to true in production. When true, Mattermost attempts to send iOS and Android push notifications through the push notification server.",
"admin.email.pushServerDesc": "Location of Mattermost push notification service you can set up behind your firewall using https://github.com/mattermost/push-proxy. For testing you can use http://push-test.mattermost.com, which connects to the sample Mattermost iOS app in the public Apple AppStore. Please do not use test service for production deployments.",
"admin.email.pushServerEx": "E.g.: \"http://push-test.mattermost.com\"",
@@ -1315,4 +1319,4 @@
"web.footer.terms": "Terms",
"web.header.back": "Back",
"web.root.singup_info": "All team communication in one place, searchable and accessible anywhere"
-}
+} \ No newline at end of file
diff --git a/webapp/i18n/es.json b/webapp/i18n/es.json
index 0f927101d..457752b64 100644
--- a/webapp/i18n/es.json
+++ b/webapp/i18n/es.json
@@ -42,6 +42,8 @@
"admin.email.emailSettings": "Configuraciones de correo",
"admin.email.emailSuccess": "No fueron reportados errores mientras se enviada el correo. Favor validar en tu bandeja de entrada.",
"admin.email.false": "falso",
+ "admin.email.fullPushNotification": "Enviar el mensaje completo",
+ "admin.email.genericPushNotification": "Enviar descripción generica con nombres de usuario y canal",
"admin.email.inviteSaltDescription": "32-caracter salt añadido a la firma de invitación de correos. Aleatoriamente generado en la instalación. Click \"Re-Generar\" para crear nuevo salt.",
"admin.email.inviteSaltExample": "Ej \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"",
"admin.email.inviteSaltTitle": "Salt de las invitaciones:",
@@ -56,6 +58,8 @@
"admin.email.passwordSaltDescription": "Un salt de 32-caracteres es añadido a la firma de correos para restablecer la contraseña. Aleatoriamente generado en la instalación. Pincha \"Regenerar\" para crear un nuevo salt.",
"admin.email.passwordSaltExample": "Ej \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"",
"admin.email.passwordSaltTitle": "Resetear el Salt para las contraseñas:",
+ "admin.email.pushContentDesc": "Al seleccionar \"Enviar descripción generica con nombres de usuario y canal\" las notificaciones se enviarán con mensajes genericos, incluyendo el nombre del usuario y el canal pero obviando el texto del mensaje.<br /><br /> Al seleccionar \"Enviar el mensaje completo\" envía los mensajes desencadenados por las notificaciones con características específicas y puede incluir información confidencial enviada en los mensajes. En el caso de que tu servidor de Envío de Notificaciones se encuentre fuera del alcance de tu cortafuego,, es ALTAMENTE RECOMENDABLE utilizar está opción junto con el protocolo \"https\" para cifrar las notificaciones enviadas por el servidor.",
+ "admin.email.pushContentTitle": "Contenido de las Notificaciones:",
"admin.email.pushDesc": "Normalmente se asigna como verdadero en producción. Cuando está en verdadero, Mattermost intenta enviar notificaciones a dispositivos iOS y Android a través del servidor de notificaciones.",
"admin.email.pushServerDesc": "Ubicación del servicio de notificaciones push de Mattermost, puedes ubicarlo detras de un cortafuego utilizando https://github.com/mattermost/push-proxy. Para realizar pruebas puedes utilizar https://push-test.mattermost.com, el cual conecta con la ap de ejemplo de Mattermost en iOS publicada en el Apple AppStore. Por favor no utilices este servicio en producción.",
"admin.email.pushServerEx": "Ej.: \"https://push-test.mattermost.com\"",
@@ -1313,4 +1317,4 @@
"web.footer.terms": "Términos",
"web.header.back": "Atrás",
"web.root.singup_info": "Todas las comunicaciones del equipo en un sólo lugar, con búsquedas y accesible desde cualquier parte"
-}
+} \ No newline at end of file