summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/post.go91
-rw-r--r--model/license.go34
-rw-r--r--model/push_notification.go2
-rw-r--r--utils/license.go1
-rw-r--r--webapp/components/admin_console/admin_sidebar.jsx60
-rw-r--r--webapp/components/admin_console/email_settings.jsx361
-rw-r--r--webapp/i18n/en.json9
-rw-r--r--webapp/utils/constants.jsx4
8 files changed, 352 insertions, 210 deletions
diff --git a/api/post.go b/api/post.go
index c533ad656..a33b6ebf0 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
-
- 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)
+ 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.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 0cea67c3d..bc72ff9ad 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 9196a44dd..666dd8f7d 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 d3654932f..6905bee4f 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 9f9e85de1..da406e647 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 = (
- <li>
- <a
- href='#'
- className={this.isSelected('ldap_settings')}
- onClick={this.handleClick.bind(this, 'ldap_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.ldap'
- defaultMessage='LDAP Settings'
- />
- </a>
- </li>
- );
+ if (global.window.mm_license.LDAP === 'true') {
+ ldapSettings = (
+ <li>
+ <a
+ href='#'
+ className={this.isSelected('ldap_settings')}
+ onClick={this.handleClick.bind(this, 'ldap_settings', null)}
+ >
+ <FormattedMessage
+ id='admin.sidebar.ldap'
+ defaultMessage='LDAP Settings'
+ />
+ </a>
+ </li>
+ );
+ }
- complianceSettings = (
- <li>
- <a
- href='#'
- className={this.isSelected('compliance_settings')}
- onClick={this.handleClick.bind(this, 'compliance_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.compliance'
- defaultMessage='Compliance Settings'
- />
- </a>
- </li>
- );
+ if (global.window.mm_license.Compliance === 'true') {
+ complianceSettings = (
+ <li>
+ <a
+ href='#'
+ className={this.isSelected('compliance_settings')}
+ onClick={this.handleClick.bind(this, 'compliance_settings', null)}
+ >
+ <FormattedMessage
+ id='admin.sidebar.compliance'
+ defaultMessage='Compliance Settings'
+ />
+ </a>
+ </li>
+ );
+ }
}
licenseSettings = (
diff --git a/webapp/components/admin_console/email_settings.jsx b/webapp/components/admin_console/email_settings.jsx
index 1fa75ead9..7e8ad616f 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 = <option value='mhpns'>{Utils.localizeMessage('admin.email.mhpns', 'Use encrypted, production-quality HPNS connection to iOS and Android apps')}</option>;
+ }
+
+ let disableSave = !this.state.saveNeeded;
+
+ let tosCheckbox;
+ if (this.state.sendNotificationValue === 'mhpns') {
+ tosCheckbox = (
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ >
+ {''}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='checkbox'
+ ref='agree'
+ checked={this.state.agree}
+ onChange={this.handleAgreeChange}
+ />
+ <FormattedHTMLMessage
+ id='admin.email.agreeHPNS'
+ defaultMessage=' I understand and accept the Mattermost Hosted Push Notification Service <a href="https://about.mattermost.com/hpns-terms/" target="_blank">Terms of Service</a> and <a href="https://about.mattermost.com/hpns-privacy/" target="_blank">Privacy Policy</a>.'
+ />
+ </div>
+ </div>
+ );
+
+ disableSave = disableSave || !this.state.agree;
+ }
+
+ let sendHelpText;
+ let pushServerHelpText;
+ if (this.state.sendNotificationValue === 'off') {
+ sendHelpText = (
+ <FormattedHTMLMessage
+ id='admin.email.pushOffHelp'
+ defaultMessage='Please see <a href="http://docs.mattermost.com/deployment/push.html#push-notifications-and-mobile-devices" target="_blank">documentation on push notifications</a> to learn more about setup options.'
+ />
+ );
+ } else if (this.state.sendNotificationValue === 'mhpns') {
+ pushServerHelpText = (
+ <FormattedHTMLMessage
+ id='admin.email.mhpnsHelp'
+ defaultMessage='Download <a href="https://itunes.apple.com/us/app/mattermost/id984966508?mt=8" target="_blank">Mattermost iOS app</a> from iTunes. Download <a href="https://play.google.com/store/apps/details?id=com.mattermost.mattermost&hl=en" target="_blank">Mattermost Android app</a> from Google Play. Learn more about the <a href="http://docs.mattermost.com/deployment/push.html#hosted-push-notifications-service-hpns" target="_blank">Mattermost Hosted Push Notification Service</a>.'
+ />
+ );
+ } else if (this.state.sendNotificationValue === 'mtpns') {
+ pushServerHelpText = (
+ <FormattedHTMLMessage
+ id='admin.email.mtpnsHelp'
+ defaultMessage='Download <a href="https://itunes.apple.com/us/app/mattermost/id984966508?mt=8" target="_blank">Mattermost iOS app</a> from iTunes. Download <a href="https://play.google.com/store/apps/details?id=com.mattermost.mattermost&hl=en" target="_blank">Mattermost Android app</a> from Google Play. Learn more about the <a href="http://docs.mattermost.com/deployment/push.html#test-push-notifications-service-tpns" target="_blank">Mattermost Test Push Notification Service</a>.'
+ />
+ );
+ } else {
+ pushServerHelpText = (
+ <FormattedHTMLMessage
+ id='admin.email.easHelp'
+ defaultMessage='Learn more about compiling and deploying your own mobile apps from an <a href="http://docs.mattermost.com/deployment/push.html#enterprise-app-store-eas" target="_blank">Enterprise App Store</a>.'
+ />
+ );
+ }
+
+ const sendPushNotifications = (
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='sendPushNotifications'
+ >
+ <FormattedMessage
+ id='admin.email.pushTitle'
+ defaultMessage='Send Push Notifications: '
+ />
+ </label>
+ <div className='col-sm-8'>
+ <select
+ className='form-control'
+ id='sendPushNotifications'
+ ref='sendPushNotifications'
+ value={this.state.sendNotificationValue}
+ onChange={this.handleSendPushNotificationsChange}
+ >
+ <option value='off'>{Utils.localizeMessage('admin.email.pushOff', 'Do not send push notifications')}</option>
+ {mhpnsOption}
+ <option value='mtpns'>{Utils.localizeMessage('admin.email.mtpns', 'Use iOS and Android apps on iTunes and Google Play with TPNS')}</option>
+ <option value='self'>{Utils.localizeMessage('admin.email.selfPush', 'Manually enter Push Notification Service location')}</option>
+ </select>
+ <p className='help-text'>
+ {sendHelpText}
+ </p>
+ </div>
+ </div>
+ );
+
+ let pushNotificationServer;
+ let pushNotificationContent;
+ if (this.state.sendNotificationValue !== 'off') {
+ pushNotificationServer = (
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='PushNotificationServer'
+ >
+ <FormattedMessage
+ id='admin.email.pushServerTitle'
+ defaultMessage='Push Notification Server:'
+ />
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='PushNotificationServer'
+ ref='PushNotificationServer'
+ placeholder={Utils.localizeMessage('admin.email.pushServerEx', 'E.g.: "http://push-test.mattermost.com"')}
+ value={this.state.pushNotificationServer}
+ onChange={this.handlePushServerChange}
+ disabled={this.state.sendNotificationValue !== 'self'}
+ />
+ <p className='help-text'>
+ {pushServerHelpText}
+ </p>
+ </div>
+ </div>
+ );
+
+ pushNotificationContent = (
+ <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')}
+ >
+ <option value='generic'>{Utils.localizeMessage('admin.email.genericPushNotification', 'Send generic description with user and channel names')}</option>
+ <option value='full'>{Utils.localizeMessage('admin.email.fullPushNotification', 'Send full message snippet')}</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>
+ );
+ }
+
return (
<div className='wrapper--fixed'>
<h3>
@@ -803,120 +1014,16 @@ class EmailSettings extends React.Component {
</div>
</div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='sendPushNotifications'
- >
- <FormattedMessage
- id='admin.email.pushTitle'
- defaultMessage='Send Push Notifications: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='sendPushNotifications'
- value='true'
- ref='sendPushNotifications'
- defaultChecked={this.props.config.EmailSettings.SendPushNotifications}
- onChange={this.handleChange.bind(this, 'sendPushNotifications_true')}
- />
- <FormattedMessage
- id='admin.email.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='sendPushNotifications'
- value='false'
- defaultChecked={!this.props.config.EmailSettings.SendPushNotifications}
- onChange={this.handleChange.bind(this, 'sendPushNotifications_false')}
- />
- <FormattedMessage
- id='admin.email.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.pushDesc'
- defaultMessage='Typically set to true in production. When true, Mattermost attempts to send iOS and Android push notifications through the push notification server.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PushNotificationServer'
- >
- <FormattedMessage
- id='admin.email.pushServerTitle'
- defaultMessage='Push Notification Server:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PushNotificationServer'
- ref='PushNotificationServer'
- placeholder={formatMessage(holders.pushServerEx)}
- defaultValue={this.props.config.EmailSettings.PushNotificationServer}
- onChange={this.handleChange}
- disabled={!this.state.sendPushNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.pushServerDesc'
- defaultMessage='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.'
- />
- </p>
- </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>
+ {sendPushNotifications}
+ {tosCheckbox}
+ {pushNotificationServer}
+ {pushNotificationContent}
<div className='form-group'>
<div className='col-sm-12'>
{serverError}
<button
- disabled={!this.state.saveNeeded}
+ disabled={disableSave}
type='submit'
className={saveClass}
onClick={this.handleSubmit}
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index 3505c1d3c..790c798b2 100644
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -171,6 +171,15 @@
"admin.email.smtpUsernameTitle": "SMTP Username:",
"admin.email.testing": "Testing...",
"admin.email.true": "true",
+ "admin.email.agreeHPNS": " I understand and accept the Mattermost Hosted Push Notification Service <a href=\"https://about.mattermost.com/hpns-terms/\" target=\"_blank\">Terms of Service</a> and <a href=\"https://about.mattermost.com/hpns-privacy/\" target=\"_blank\">Privacy Policy</a>.",
+ "admin.email.pushOffHelp": "Please see <a href=\"http://docs.mattermost.com/deployment/push.html#push-notifications-and-mobile-devices\" target=\"_blank\">documentation on push notifications</a> to learn more about setup options.",
+ "admin.email.mhpnsHelp": "Download <a href=\"https://itunes.apple.com/us/app/mattermost/id984966508?mt=8\" target=\"_blank\">Mattermost iOS app</a> from iTunes. Download <a href=\"https://play.google.com/store/apps/details?id=com.mattermost.mattermost&hl=en\" target=\"_blank\">Mattermost Android app</a> from Google Play. Learn more about <a href=\"http://docs.mattermost.com/deployment/push.html#hosted-push-notifications-service-hpns\" target=\"_blank\">HPNS</a>.",
+ "admin.email.mtpnsHelp": "Download <a href=\"https://itunes.apple.com/us/app/mattermost/id984966508?mt=8\" target=\"_blank\">Mattermost iOS app</a> from iTunes. Download <a href=\"https://play.google.com/store/apps/details?id=com.mattermost.mattermost&hl=en\" target=\"_blank\">Mattermost Android app</a> from Google Play. Learn more about <a href=\"http://docs.mattermost.com/deployment/push.html#test-push-notifications-service-tpns\" target=\"_blank\">TPNS</a>.",
+ "admin.email.easHelp": "Learn more about compiling and deploying your own mobile apps from an <a href=\"http://docs.mattermost.com/deployment/push.html#enterprise-app-store-eas\" target=\"_blank\">Enterprise App Store</a>.",
+ "admin.email.mtpns": "Use iOS and Android apps on iTunes and Google Play with TPNS",
+ "admin.email.mhpns": "Use encrypted, production-quality HPNS connection to iOS and Android apps",
+ "admin.email.selfPush": "Manually enter Push Notification Service location",
+ "admin.email.pushOff": "Do not send push notifications",
"admin.gitab.clientSecretDescription": "Obtain this value via the instructions above for logging into GitLab.",
"admin.gitlab.EnableHtmlDesc": "<ol><li>Log in to your GitLab account and go to Profile Settings -> Applications.</li><li>Enter Redirect URIs \"<your-mattermost-url>/login/gitlab/complete\" (example: http://localhost:8065/login/gitlab/complete) and \"<your-mattermost-url>/signup/gitlab/complete\". </li><li>Then use \"Secret\" and \"Id\" fields from GitLab to complete the options below.</li><li>Complete the Endpoint URLs below. </li></ol>",
"admin.gitlab.authDescription": "Enter https://<your-gitlab-url>/oauth/authorize (example https://example.com:3000/oauth/authorize). Make sure you use HTTP or HTTPS in your URL depending on your server configuration.",
diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx
index d08ecfef7..88bf56706 100644
--- a/webapp/utils/constants.jsx
+++ b/webapp/utils/constants.jsx
@@ -637,5 +637,7 @@ export default {
TIME_SINCE_UPDATE_INTERVAL: 30000,
MIN_HASHTAG_LINK_LENGTH: 3,
EMOJI_PATH: '/static/emoji',
- DEFAULT_WEBHOOK_LOGO: logoWebhook
+ DEFAULT_WEBHOOK_LOGO: logoWebhook,
+ MHPNS: 'https://push.mattermost.com',
+ MTPNS: 'http://push-test.mattermost.com'
};