Option to enable full snippets in push notifications

Этот коммит содержится в:
Elias Nahum
2016-03-11 00:14:55 -03:00
родитель d383ed2f8d
Коммит 1f5c8c4e4e
13 изменённых файлов: 210 добавлений и 7 удалений

Просмотреть файл

@@ -670,8 +670,15 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
alreadySeen[session.DeviceId] = session.DeviceId alreadySeen[session.DeviceId] = session.DeviceId
msg := model.PushNotification{} 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.ServerId = utils.CfgDiagnosticId
msg.ChannelId = channel.Id
msg.ChannelName = channel.Name
if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") { if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") {
msg.Platform = 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+":") msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")
} }
if channel.Type == model.CHANNEL_DIRECT { if *utils.Cfg.EmailSettings.PushNotificationContents == model.FULL_NOTIFICATION {
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message") 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 { } 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{ tr := &http.Transport{

Просмотреть файл

@@ -87,6 +87,7 @@
"InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9YoS", "InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9YoS",
"PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL", "PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL",
"SendPushNotifications": false, "SendPushNotifications": false,
"PushNotificationContents": "generic",
"PushNotificationServer": "" "PushNotificationServer": ""
}, },
"RateLimitSettings": { "RateLimitSettings": {

Просмотреть файл

@@ -3107,6 +3107,10 @@
"id": "store.sql_user.get_total_users_count.app_error", "id": "store.sql_user.get_total_users_count.app_error",
"translation": "We could not count the users" "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", "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." "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."

Просмотреть файл

@@ -3107,6 +3107,10 @@
"id": "store.sql_user.get_total_users_count.app_error", "id": "store.sql_user.get_total_users_count.app_error",
"translation": "No pudimos contar los usuarios" "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", "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." "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."

Просмотреть файл

@@ -25,6 +25,9 @@ const (
WEBSERVER_MODE_REGULAR = "regular" WEBSERVER_MODE_REGULAR = "regular"
WEBSERVER_MODE_GZIP = "gzip" WEBSERVER_MODE_GZIP = "gzip"
WEBSERVER_MODE_DISABLED = "disabled" WEBSERVER_MODE_DISABLED = "disabled"
GENERIC_NOTIFICATION = "generic"
FULL_NOTIFICATION = "full"
) )
type ServiceSettings struct { type ServiceSettings struct {
@@ -121,6 +124,7 @@ type EmailSettings struct {
PasswordResetSalt string PasswordResetSalt string
SendPushNotifications *bool SendPushNotifications *bool
PushNotificationServer *string PushNotificationServer *string
PushNotificationContents *string
} }
type RateLimitSettings struct { type RateLimitSettings struct {
@@ -299,6 +303,11 @@ func (o *Config) SetDefaults() {
*o.EmailSettings.PushNotificationServer = "" *o.EmailSettings.PushNotificationServer = ""
} }
if o.EmailSettings.PushNotificationContents == nil {
o.EmailSettings.PushNotificationContents = new(string)
*o.EmailSettings.PushNotificationContents = GENERIC_NOTIFICATION
}
if o.SupportSettings.TermsOfServiceLink == nil { if o.SupportSettings.TermsOfServiceLink == nil {
o.SupportSettings.TermsOfServiceLink = new(string) o.SupportSettings.TermsOfServiceLink = new(string)
*o.SupportSettings.TermsOfServiceLink = "/static/help/terms.html" *o.SupportSettings.TermsOfServiceLink = "/static/help/terms.html"

Просмотреть файл

@@ -11,6 +11,8 @@ import (
const ( const (
PUSH_NOTIFY_APPLE = "apple" PUSH_NOTIFY_APPLE = "apple"
PUSH_NOTIFY_ANDROID = "android" PUSH_NOTIFY_ANDROID = "android"
CATEGORY_DM = "DIRECT_MESSAGE"
) )
type PushNotification struct { type PushNotification struct {
@@ -22,6 +24,8 @@ type PushNotification struct {
Message string `json:"message"` Message string `json:"message"`
Badge int `json:"badge"` Badge int `json:"badge"`
ContentAvailable int `json:"cont_ava"` ContentAvailable int `json:"cont_ava"`
ChannelId string `json:"channel_id"`
ChannelName string `json:"channel_name"`
} }
func (me *PushNotification) ToJson() string { func (me *PushNotification) ToJson() string {

Просмотреть файл

@@ -367,3 +367,15 @@ func IsValidHttpUrl(rawUrl string) bool {
return true 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
}

Просмотреть файл

@@ -652,3 +652,22 @@ func (us SqlUserStore) AnalyticsUniqueUserCount(teamId string) StoreChannel {
return 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
}

Просмотреть файл

@@ -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")
}
}

Просмотреть файл

@@ -130,6 +130,8 @@ type UserStore interface {
GetSystemAdminProfiles() StoreChannel GetSystemAdminProfiles() StoreChannel
PermanentDelete(userId string) StoreChannel PermanentDelete(userId string) StoreChannel
AnalyticsUniqueUserCount(teamId string) StoreChannel AnalyticsUniqueUserCount(teamId string) StoreChannel
GetUnreadCount(userId string) StoreChannel
} }
type SessionStore interface { type SessionStore interface {

Просмотреть файл

@@ -58,6 +58,14 @@ var holders = defineMessages({
id: 'admin.email.pushServerEx', id: 'admin.email.pushServerEx',
defaultMessage: 'E.g.: "http://push-test.mattermost.com"' 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: { testing: {
id: 'admin.email.testing', id: 'admin.email.testing',
defaultMessage: 'Testing...' defaultMessage: 'Testing...'
@@ -87,7 +95,8 @@ class EmailSettings extends React.Component {
saveNeeded: false, saveNeeded: false,
serverError: null, serverError: null,
emailSuccess: 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.FeedbackEmail = ReactDOM.findDOMNode(this.refs.feedbackEmail).value.trim();
config.EmailSettings.SMTPServer = ReactDOM.findDOMNode(this.refs.SMTPServer).value.trim(); config.EmailSettings.SMTPServer = ReactDOM.findDOMNode(this.refs.SMTPServer).value.trim();
config.EmailSettings.PushNotificationServer = ReactDOM.findDOMNode(this.refs.PushNotificationServer).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.SMTPPort = ReactDOM.findDOMNode(this.refs.SMTPPort).value.trim();
config.EmailSettings.SMTPUsername = ReactDOM.findDOMNode(this.refs.SMTPUsername).value.trim(); config.EmailSettings.SMTPUsername = ReactDOM.findDOMNode(this.refs.SMTPUsername).value.trim();
config.EmailSettings.SMTPPassword = ReactDOM.findDOMNode(this.refs.SMTPPassword).value.trim(); config.EmailSettings.SMTPPassword = ReactDOM.findDOMNode(this.refs.SMTPPassword).value.trim();
@@ -928,6 +938,38 @@ class EmailSettings extends React.Component {
</div> </div>
</div> </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='form-group'>
<div className='col-sm-12'> <div className='col-sm-12'>
{serverError} {serverError}

Просмотреть файл

@@ -42,6 +42,8 @@
"admin.email.emailSettings": "Email Settings", "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.emailSuccess": "No errors were reported while sending an email. Please check your inbox to make sure.",
"admin.email.false": "false", "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.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.inviteSaltExample": "Ex \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"",
"admin.email.inviteSaltTitle": "Invite Salt:", "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.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.passwordSaltExample": "Ex \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"",
"admin.email.passwordSaltTitle": "Password Reset Salt:", "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.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.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\"", "admin.email.pushServerEx": "E.g.: \"http://push-test.mattermost.com\"",

Просмотреть файл

@@ -42,6 +42,8 @@
"admin.email.emailSettings": "Configuraciones de correo", "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.emailSuccess": "No fueron reportados errores mientras se enviada el correo. Favor validar en tu bandeja de entrada.",
"admin.email.false": "falso", "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.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.inviteSaltExample": "Ej \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"",
"admin.email.inviteSaltTitle": "Salt de las invitaciones:", "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.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.passwordSaltExample": "Ej \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"",
"admin.email.passwordSaltTitle": "Resetear el Salt para las contraseñas:", "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.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.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\"", "admin.email.pushServerEx": "Ej.: \"https://push-test.mattermost.com\"",