Port HPNS from 2.2 to master (#2863)

This commit is contained in:
Joram Wilander
2016-05-03 11:54:49 -04:00
committed by Corey Hulen
parent ffb4cb5e10
commit 08a3cf6b38
8 changed files with 351 additions and 209 deletions

View File

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

View File

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

View File

@@ -13,6 +13,8 @@ const (
PUSH_NOTIFY_ANDROID = "android"
CATEGORY_DM = "DIRECT_MESSAGE"
MHPNS = "https://push.mattermost.com"
)
type PushNotification struct {

View File

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

View File

@@ -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 = (

View File

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

View File

@@ -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.",

View File

@@ -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'
};