From 08a3cf6b38fd8fdac3b5f7176133bc1a248bc8fc Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Tue, 3 May 2016 11:54:49 -0400 Subject: [PATCH] Port HPNS from 2.2 to master (#2863) --- api/post.go | 89 +++-- model/license.go | 34 +- model/push_notification.go | 2 + utils/license.go | 1 + .../admin_console/admin_sidebar.jsx | 60 +-- .../admin_console/email_settings.jsx | 361 ++++++++++++------ webapp/i18n/en.json | 9 + webapp/utils/constants.jsx | 4 +- 8 files changed, 351 insertions(+), 209 deletions(-) diff --git a/api/post.go b/api/post.go index c533ad656c..a33b6ebf08 100644 --- a/api/post.go +++ b/api/post.go @@ -697,55 +697,60 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel * sessions := result.Data.([]*model.Session) alreadySeen := make(map[string]string) - for _, session := range sessions { - if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" && - (strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")) { - alreadySeen[session.DeviceId] = session.DeviceId + pushServer := *utils.Cfg.EmailSettings.PushNotificationServer + if pushServer == model.MHPNS && (!utils.IsLicensed || !*utils.License.Features.MHPNS) { + l4g.Warn(utils.T("api.post.send_notifications_and_forget.push_notification.mhpnsWarn")) + } else { + for _, session := range sessions { + if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" && + (strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")) { + alreadySeen[session.DeviceId] = session.DeviceId - msg := model.PushNotification{} - 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 - msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") - } else if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":") { - msg.Platform = model.PUSH_NOTIFY_ANDROID - msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":") - } - - 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) + msg := model.PushNotification{} + 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.Message = "@" + senderName + " @ " + channelName + ": " + model.ClearMentionTags(post.Message) + msg.Badge = int(badge.Data.(int64)) } - } else { - if channel.Type == model.CHANNEL_DIRECT { - msg.Category = model.CATEGORY_DM - msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message") + 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 + msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") + } else if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":") { + msg.Platform = model.PUSH_NOTIFY_ANDROID + msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":") + } + + 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{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections}, - } - httpClient := &http.Client{Transport: tr} - request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+model.API_URL_SUFFIX_V1+"/send_push", strings.NewReader(msg.ToJson())) + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections}, + } + httpClient := &http.Client{Transport: tr} + request, _ := http.NewRequest("POST", pushServer+model.API_URL_SUFFIX_V1+"/send_push", strings.NewReader(msg.ToJson())) - l4g.Debug(utils.T("api.post.send_notifications_and_forget.push_notification.debug"), msg.DeviceId, msg.Message) - if _, err := httpClient.Do(request); err != nil { - l4g.Error(utils.T("api.post.send_notifications_and_forget.push_notification.error"), id, err) + l4g.Debug(utils.T("api.post.send_notifications_and_forget.push_notification.debug"), msg.DeviceId, msg.Message) + if _, err := httpClient.Do(request); err != nil { + l4g.Error(utils.T("api.post.send_notifications_and_forget.push_notification.error"), id, err) + } } } } diff --git a/model/license.go b/model/license.go index 0cea67c3d1..bc72ff9adc 100644 --- a/model/license.go +++ b/model/license.go @@ -32,15 +32,22 @@ type Customer struct { } type Features struct { - Users *int `json:"users"` - LDAP *bool `json:"ldap"` - MFA *bool `json:"mfa"` - GoogleSSO *bool `json:"google_sso"` - Compliance *bool `json:"compliance"` - CustomBrand *bool `json:"custom_brand"` + Users *int `json:"users"` + LDAP *bool `json:"ldap"` + MFA *bool `json:"mfa"` + GoogleSSO *bool `json:"google_sso"` + Compliance *bool `json:"compliance"` + CustomBrand *bool `json:"custom_brand"` + MHPNS *bool `json:"mhpns"` + FutureFeatures *bool `json:"future_features"` } func (f *Features) SetDefaults() { + if f.FutureFeatures == nil { + f.FutureFeatures = new(bool) + *f.FutureFeatures = true + } + if f.Users == nil { f.Users = new(int) *f.Users = 0 @@ -48,27 +55,32 @@ func (f *Features) SetDefaults() { if f.LDAP == nil { f.LDAP = new(bool) - *f.LDAP = true + *f.LDAP = *f.FutureFeatures } if f.MFA == nil { f.MFA = new(bool) - *f.MFA = true + *f.MFA = *f.FutureFeatures } if f.GoogleSSO == nil { f.GoogleSSO = new(bool) - *f.GoogleSSO = true + *f.GoogleSSO = *f.FutureFeatures } if f.Compliance == nil { f.Compliance = new(bool) - *f.Compliance = true + *f.Compliance = *f.FutureFeatures } if f.CustomBrand == nil { f.CustomBrand = new(bool) - *f.CustomBrand = true + *f.CustomBrand = *f.FutureFeatures + } + + if f.MHPNS == nil { + f.MHPNS = new(bool) + *f.MHPNS = *f.FutureFeatures } } diff --git a/model/push_notification.go b/model/push_notification.go index 9196a44dd8..666dd8f7d4 100644 --- a/model/push_notification.go +++ b/model/push_notification.go @@ -13,6 +13,8 @@ const ( PUSH_NOTIFY_ANDROID = "android" CATEGORY_DM = "DIRECT_MESSAGE" + + MHPNS = "https://push.mattermost.com" ) type PushNotification struct { diff --git a/utils/license.go b/utils/license.go index d3654932f8..6905bee4fc 100644 --- a/utils/license.go +++ b/utils/license.go @@ -124,6 +124,7 @@ func getClientLicense(l *model.License) map[string]string { props["GoogleSSO"] = strconv.FormatBool(*l.Features.GoogleSSO) props["Compliance"] = strconv.FormatBool(*l.Features.Compliance) props["CustomBrand"] = strconv.FormatBool(*l.Features.CustomBrand) + props["MHPNS"] = strconv.FormatBool(*l.Features.MHPNS) props["IssuedAt"] = strconv.FormatInt(l.IssuedAt, 10) props["StartsAt"] = strconv.FormatInt(l.StartsAt, 10) props["ExpiresAt"] = strconv.FormatInt(l.ExpiresAt, 10) diff --git a/webapp/components/admin_console/admin_sidebar.jsx b/webapp/components/admin_console/admin_sidebar.jsx index 9f9e85de11..da406e6476 100644 --- a/webapp/components/admin_console/admin_sidebar.jsx +++ b/webapp/components/admin_console/admin_sidebar.jsx @@ -184,35 +184,39 @@ export default class AdminSidebar extends React.Component { let licenseSettings; if (global.window.mm_config.BuildEnterpriseReady === 'true') { if (global.window.mm_license.IsLicensed === 'true') { - ldapSettings = ( -
  • - - - -
  • - ); + if (global.window.mm_license.LDAP === 'true') { + ldapSettings = ( +
  • + + + +
  • + ); + } - complianceSettings = ( -
  • - - - -
  • - ); + if (global.window.mm_license.Compliance === 'true') { + complianceSettings = ( +
  • + + + +
  • + ); + } } licenseSettings = ( diff --git a/webapp/components/admin_console/email_settings.jsx b/webapp/components/admin_console/email_settings.jsx index 1fa75ead9b..7e8ad616f4 100644 --- a/webapp/components/admin_console/email_settings.jsx +++ b/webapp/components/admin_console/email_settings.jsx @@ -10,6 +10,9 @@ import ConnectionSecurityDropdownSetting from './connection_security_dropdown_se import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl'; +import * as Utils from 'utils/utils.jsx'; +import Constants from 'utils/constants.jsx'; + var holders = defineMessages({ notificationDisplayExample: { id: 'admin.email.notificationDisplayExample', @@ -43,18 +46,6 @@ var holders = defineMessages({ id: 'admin.email.passwordSaltExample', defaultMessage: 'Ex "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo"' }, - pushServerEx: { - 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...' @@ -77,6 +68,29 @@ class EmailSettings extends React.Component { this.buildConfig = this.buildConfig.bind(this); this.handleGenerateInvite = this.handleGenerateInvite.bind(this); this.handleGenerateReset = this.handleGenerateReset.bind(this); + this.handleSendPushNotificationsChange = this.handleSendPushNotificationsChange.bind(this); + this.handlePushServerChange = this.handlePushServerChange.bind(this); + this.handleAgreeChange = this.handleAgreeChange.bind(this); + + let sendNotificationValue; + let agree = false; + if (!props.config.EmailSettings.SendPushNotifications) { + sendNotificationValue = 'off'; + } else if (props.config.EmailSettings.PushNotificationServer === Constants.MHPNS && global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.MHPNS === 'true') { + sendNotificationValue = 'mhpns'; + agree = true; + } else if (props.config.EmailSettings.PushNotificationServer === Constants.MTPNS) { + sendNotificationValue = 'mtpns'; + } else { + sendNotificationValue = 'self'; + } + + let pushNotificationServer = this.props.config.EmailSettings.PushNotificationServer; + if (sendNotificationValue === 'mtpns') { + pushNotificationServer = Constants.MTPNS; + } else if (sendNotificationValue === 'mhpns') { + pushNotificationServer = Constants.MHPNS; + } this.state = { sendEmailNotifications: this.props.config.EmailSettings.SendEmailNotifications, @@ -86,12 +100,15 @@ class EmailSettings extends React.Component { emailSuccess: null, emailFail: null, pushNotificationContents: this.props.config.EmailSettings.PushNotificationContents, - connectionSecurity: this.props.config.EmailSettings.ConnectionSecurity + connectionSecurity: this.props.config.EmailSettings.ConnectionSecurity, + sendNotificationValue, + pushNotificationServer, + agree }; } handleChange(action) { - var s = {saveNeeded: true, serverError: this.state.serverError}; + const s = {saveNeeded: true}; if (action === 'sendEmailNotifications_true') { s.sendEmailNotifications = true; @@ -113,18 +130,15 @@ class EmailSettings extends React.Component { } buildConfig() { - var config = this.props.config; + const config = this.props.config; config.EmailSettings.EnableSignUpWithEmail = ReactDOM.findDOMNode(this.refs.allowSignUpWithEmail).checked; config.EmailSettings.EnableSignInWithEmail = ReactDOM.findDOMNode(this.refs.allowSignInWithEmail).checked; config.EmailSettings.EnableSignInWithUsername = ReactDOM.findDOMNode(this.refs.allowSignInWithUsername).checked; config.EmailSettings.SendEmailNotifications = ReactDOM.findDOMNode(this.refs.sendEmailNotifications).checked; - config.EmailSettings.SendPushNotifications = ReactDOM.findDOMNode(this.refs.sendPushNotifications).checked; config.EmailSettings.RequireEmailVerification = ReactDOM.findDOMNode(this.refs.requireEmailVerification).checked; config.EmailSettings.FeedbackName = ReactDOM.findDOMNode(this.refs.feedbackName).value.trim(); 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(); @@ -142,9 +156,43 @@ class EmailSettings extends React.Component { ReactDOM.findDOMNode(this.refs.PasswordResetSalt).value = config.EmailSettings.PasswordResetSalt; } + const sendPushNotifications = this.refs.sendPushNotifications.value; + if (sendPushNotifications === 'off') { + config.EmailSettings.SendPushNotifications = false; + } else { + config.EmailSettings.SendPushNotifications = true; + } + + if (this.refs.PushNotificationServer) { + config.EmailSettings.PushNotificationServer = this.refs.PushNotificationServer.value.trim(); + } + + if (this.refs.PushNotificationContents) { + config.EmailSettings.PushNotificationContents = this.refs.PushNotificationContents.value; + } + return config; } + handleSendPushNotificationsChange(e) { + const sendNotificationValue = e.target.value; + let pushNotificationServer = this.state.pushNotificationServer; + if (sendNotificationValue === 'mtpns') { + pushNotificationServer = Constants.MTPNS; + } else if (sendNotificationValue === 'mhpns') { + pushNotificationServer = Constants.MHPNS; + } + this.setState({saveNeeded: true, sendNotificationValue, pushNotificationServer, agree: false}); + } + + handlePushServerChange(e) { + this.setState({saveNeeded: true, pushNotificationServer: e.target.value}); + } + + handleAgreeChange(e) { + this.setState({agree: e.target.checked}); + } + handleGenerateInvite(e) { e.preventDefault(); ReactDOM.findDOMNode(this.refs.InviteSalt).value = crypto.randomBytes(256).toString('base64').substring(0, 32); @@ -263,6 +311,169 @@ class EmailSettings extends React.Component { ); } + let mhpnsOption; + if (global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.MHPNS === 'true') { + mhpnsOption = ; + } + + let disableSave = !this.state.saveNeeded; + + let tosCheckbox; + if (this.state.sendNotificationValue === 'mhpns') { + tosCheckbox = ( +
    + +
    + + +
    +
    + ); + + disableSave = disableSave || !this.state.agree; + } + + let sendHelpText; + let pushServerHelpText; + if (this.state.sendNotificationValue === 'off') { + sendHelpText = ( + + ); + } else if (this.state.sendNotificationValue === 'mhpns') { + pushServerHelpText = ( + + ); + } else if (this.state.sendNotificationValue === 'mtpns') { + pushServerHelpText = ( + + ); + } else { + pushServerHelpText = ( + + ); + } + + const sendPushNotifications = ( +
    + +
    + +

    + {sendHelpText} +

    +
    +
    + ); + + let pushNotificationServer; + let pushNotificationContent; + if (this.state.sendNotificationValue !== 'off') { + pushNotificationServer = ( +
    + +
    + +

    + {pushServerHelpText} +

    +
    +
    + ); + + pushNotificationContent = ( +
    + +
    + +

    + +

    +
    +
    + ); + } + return (

    @@ -803,120 +1014,16 @@ class EmailSettings extends React.Component {

    -
    - -
    - - -

    - -

    -
    -
    - -
    - -
    - -

    - -

    -
    -
    - -
    - -
    - -

    - -

    -
    -
    + {sendPushNotifications} + {tosCheckbox} + {pushNotificationServer} + {pushNotificationContent}
    {serverError}