summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/post.go16
-rw-r--r--config/config.json5
-rw-r--r--doc/process/overview.md2
-rw-r--r--docker/dev/config_docker.json5
-rw-r--r--docker/local/config_docker.json5
-rw-r--r--mattermost.go26
-rw-r--r--model/config.go18
-rw-r--r--model/push_notification.go45
-rw-r--r--model/push_notification_test.go19
-rw-r--r--utils/apns.go37
-rw-r--r--utils/config.go1
-rw-r--r--web/react/components/admin_console/email_settings.jsx67
12 files changed, 183 insertions, 63 deletions
diff --git a/api/post.go b/api/post.go
index 88d0127d3..81cc9a1c6 100644
--- a/api/post.go
+++ b/api/post.go
@@ -536,7 +536,7 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err)
}
- if len(utils.Cfg.EmailSettings.ApplePushServer) > 0 {
+ if *utils.Cfg.EmailSettings.SendPushNotifications {
sessionChan := Srv.Store.Session().GetSessions(id)
if result := <-sessionChan; result.Err != nil {
l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err)
@@ -548,7 +548,19 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" && strings.HasPrefix(session.DeviceId, "apple:") {
alreadySeen[session.DeviceId] = session.DeviceId
- utils.SendAppleNotifyAndForget(strings.TrimPrefix(session.DeviceId, "apple:"), subjectPage.Render(), 1)
+ msg := model.PushNotification{}
+ msg.Platform = model.PUSH_NOTIFY_APPLE
+ msg.Message = subjectPage.Render()
+ msg.Badge = 1
+ msg.DeviceId = strings.TrimPrefix(session.DeviceId, "apple:")
+ msg.ServerId = utils.CfgDiagnosticId
+
+ httpClient := http.Client{}
+ request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+"/api/v1/send_push", strings.NewReader(msg.ToJson()))
+
+ if _, err := httpClient.Do(request); err != nil {
+ l4g.Error("Failed to send push notificationid=%v, err=%v", id, err)
+ }
}
}
}
diff --git a/config/config.json b/config/config.json
index 932bed8a2..999ea8a83 100644
--- a/config/config.json
+++ b/config/config.json
@@ -68,9 +68,8 @@
"ConnectionSecurity": "",
"InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9YoS",
"PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL",
- "ApplePushServer": "",
- "ApplePushCertPublic": "",
- "ApplePushCertPrivate": ""
+ "SendPushNotifications": true,
+ "PushNotificationServer": "https://push.mattermost.com"
},
"RateLimitSettings": {
"EnableRateLimiter": true,
diff --git a/doc/process/overview.md b/doc/process/overview.md
index af632e393..b34908782 100644
--- a/doc/process/overview.md
+++ b/doc/process/overview.md
@@ -64,7 +64,7 @@ Mattermost priorities are managed in Jira tickets, which are created by the core
On non-holiday weekdays new tickets are reviewed in a process called "triage", and assigned a Fix Version of "backlog", indicating the ticket has enough specificity that it can be assigned to a developer to be completed.
-By default, all tickets are created as internal-only, and the triage process reviews them for sufficient specifity and abscense of sensitive information before switching their visibility to public as part of the triage process.
+By default, all tickets are created as public unless they contain sensitive information. The triage process reviews them for sufficient specifity. If the ticket is unclear, triage may reassign the ticket back to the original reporter to add more details.
View [current issues scheduled for the next triage meeting](https://mattermost.atlassian.net/browse/PLT-1203?filter=10105).
diff --git a/docker/dev/config_docker.json b/docker/dev/config_docker.json
index c23a72cd1..4c5502ddd 100644
--- a/docker/dev/config_docker.json
+++ b/docker/dev/config_docker.json
@@ -68,9 +68,8 @@
"ConnectionSecurity": "",
"InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9YoS",
"PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL",
- "ApplePushServer": "",
- "ApplePushCertPublic": "",
- "ApplePushCertPrivate": ""
+ "SendPushNotifications": true,
+ "PushNotificationServer": "https://push.mattermost.com"
},
"RateLimitSettings": {
"EnableRateLimiter": true,
diff --git a/docker/local/config_docker.json b/docker/local/config_docker.json
index c23a72cd1..4c5502ddd 100644
--- a/docker/local/config_docker.json
+++ b/docker/local/config_docker.json
@@ -68,9 +68,8 @@
"ConnectionSecurity": "",
"InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9YoS",
"PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL",
- "ApplePushServer": "",
- "ApplePushCertPublic": "",
- "ApplePushCertPrivate": ""
+ "SendPushNotifications": true,
+ "PushNotificationServer": "https://push.mattermost.com"
},
"RateLimitSettings": {
"EnableRateLimiter": true,
diff --git a/mattermost.go b/mattermost.go
index 2d5727400..eaab1de88 100644
--- a/mattermost.go
+++ b/mattermost.go
@@ -68,6 +68,7 @@ func main() {
manualtesting.InitManualTesting()
}
+ setDiagnosticId()
runSecurityAndDiagnosticsJobAndForget()
// wait for kill signal before attempting to gracefully shutdown
@@ -80,6 +81,21 @@ func main() {
}
}
+func setDiagnosticId() {
+ if result := <-api.Srv.Store.System().Get(); result.Err == nil {
+ props := result.Data.(model.StringMap)
+
+ id := props[model.SYSTEM_DIAGNOSTIC_ID]
+ if len(id) == 0 {
+ id = model.NewId()
+ systemId := &model.System{Name: model.SYSTEM_DIAGNOSTIC_ID, Value: id}
+ <-api.Srv.Store.System().Save(systemId)
+ }
+
+ utils.CfgDiagnosticId = id
+ }
+}
+
func runSecurityAndDiagnosticsJobAndForget() {
go func() {
for {
@@ -92,15 +108,9 @@ func runSecurityAndDiagnosticsJobAndForget() {
if (currentTime - lastSecurityTime) > 1000*60*60*24*1 {
l4g.Debug("Checking for security update from Mattermost")
- id := props[model.SYSTEM_DIAGNOSTIC_ID]
- if len(id) == 0 {
- id = model.NewId()
- systemId := &model.System{Name: model.SYSTEM_DIAGNOSTIC_ID, Value: id}
- <-api.Srv.Store.System().Save(systemId)
- }
-
v := url.Values{}
- v.Set(utils.PROP_DIAGNOSTIC_ID, id)
+
+ v.Set(utils.PROP_DIAGNOSTIC_ID, utils.CfgDiagnosticId)
v.Set(utils.PROP_DIAGNOSTIC_BUILD, model.CurrentVersion+"."+model.BuildNumber)
v.Set(utils.PROP_DIAGNOSTIC_DATABASE, utils.Cfg.SqlSettings.DriverName)
v.Set(utils.PROP_DIAGNOSTIC_OS, runtime.GOOS)
diff --git a/model/config.go b/model/config.go
index 50a8dc133..195cefae8 100644
--- a/model/config.go
+++ b/model/config.go
@@ -96,11 +96,8 @@ type EmailSettings struct {
ConnectionSecurity string
InviteSalt string
PasswordResetSalt string
-
- // For Future Use
- ApplePushServer string
- ApplePushCertPublic string
- ApplePushCertPrivate string
+ SendPushNotifications *bool
+ PushNotificationServer *string
}
type RateLimitSettings struct {
@@ -181,6 +178,17 @@ func (o *Config) SetDefaults() {
o.TeamSettings.EnableTeamListing = new(bool)
*o.TeamSettings.EnableTeamListing = false
}
+
+ if o.EmailSettings.SendPushNotifications == nil {
+ o.EmailSettings.SendPushNotifications = new(bool)
+ *o.EmailSettings.SendPushNotifications = true
+ }
+
+ if o.EmailSettings.PushNotificationServer == nil {
+ o.EmailSettings.PushNotificationServer = new(string)
+ *o.EmailSettings.PushNotificationServer = "https://push.mattermost.com"
+ }
+
}
func (o *Config) IsValid() *AppError {
diff --git a/model/push_notification.go b/model/push_notification.go
new file mode 100644
index 000000000..76f5bd125
--- /dev/null
+++ b/model/push_notification.go
@@ -0,0 +1,45 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+)
+
+const (
+ PUSH_NOTIFY_APPLE = "apple"
+ PUSH_NOTIFY_ANDROID = "android"
+)
+
+type PushNotification struct {
+ Platform string `json:"platform"`
+ ServerId string `json:"server_id"`
+ DeviceId string `json:"device_id"`
+ Category string `json:"category"`
+ Sound string `json:"sound"`
+ Message string `json:"message"`
+ Badge int `json:"badge"`
+ ContentAvailable int `json:"cont_ava"`
+}
+
+func (me *PushNotification) ToJson() string {
+ b, err := json.Marshal(me)
+ if err != nil {
+ return ""
+ } else {
+ return string(b)
+ }
+}
+
+func PushNotificationFromJson(data io.Reader) *PushNotification {
+ decoder := json.NewDecoder(data)
+ var me PushNotification
+ err := decoder.Decode(&me)
+ if err == nil {
+ return &me
+ } else {
+ return nil
+ }
+}
diff --git a/model/push_notification_test.go b/model/push_notification_test.go
new file mode 100644
index 000000000..94329f389
--- /dev/null
+++ b/model/push_notification_test.go
@@ -0,0 +1,19 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestPushNotification(t *testing.T) {
+ msg := PushNotification{Platform: "test"}
+ json := msg.ToJson()
+ result := PushNotificationFromJson(strings.NewReader(json))
+
+ if msg.Platform != result.Platform {
+ t.Fatal("Ids do not match")
+ }
+}
diff --git a/utils/apns.go b/utils/apns.go
deleted file mode 100644
index 06e8ce6ef..000000000
--- a/utils/apns.go
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package utils
-
-import (
- l4g "code.google.com/p/log4go"
- "fmt"
- "github.com/anachronistic/apns"
- "github.com/mattermost/platform/model"
-)
-
-func SendAppleNotifyAndForget(deviceId string, message string, badge int) {
- go func() {
- if err := SendAppleNotify(deviceId, message, badge); err != nil {
- l4g.Error(fmt.Sprintf("%v %v", err.Message, err.DetailedError))
- }
- }()
-}
-
-func SendAppleNotify(deviceId string, message string, badge int) *model.AppError {
- payload := apns.NewPayload()
- payload.Alert = message
- payload.Badge = 1
-
- pn := apns.NewPushNotification()
- pn.DeviceToken = deviceId
- pn.AddPayload(payload)
- client := apns.BareClient(Cfg.EmailSettings.ApplePushServer, Cfg.EmailSettings.ApplePushCertPublic, Cfg.EmailSettings.ApplePushCertPrivate)
- resp := client.Send(pn)
-
- if resp.Error != nil {
- return model.NewAppError("", "Could not send apple push notification", fmt.Sprintf("id=%v err=%v", deviceId, resp.Error))
- } else {
- return nil
- }
-}
diff --git a/utils/config.go b/utils/config.go
index 2fd799cd1..0b292a2ca 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -24,6 +24,7 @@ const (
)
var Cfg *model.Config = &model.Config{}
+var CfgDiagnosticId = ""
var CfgLastModified int64 = 0
var CfgFileName string = ""
var ClientCfg map[string]string = map[string]string{}
diff --git a/web/react/components/admin_console/email_settings.jsx b/web/react/components/admin_console/email_settings.jsx
index d0565a0e0..238ace3da 100644
--- a/web/react/components/admin_console/email_settings.jsx
+++ b/web/react/components/admin_console/email_settings.jsx
@@ -18,6 +18,7 @@ export default class EmailSettings extends React.Component {
this.state = {
sendEmailNotifications: this.props.config.EmailSettings.SendEmailNotifications,
+ sendPushNotifications: this.props.config.EmailSettings.SendPushNotifications,
saveNeeded: false,
serverError: null,
emailSuccess: null,
@@ -36,6 +37,14 @@ export default class EmailSettings extends React.Component {
s.sendEmailNotifications = false;
}
+ if (action === 'sendPushNotifications_true') {
+ s.sendPushNotifications = true;
+ }
+
+ if (action === 'sendPushNotifications_false') {
+ s.sendPushNotifications = false;
+ }
+
this.setState(s);
}
@@ -43,11 +52,12 @@ export default class EmailSettings extends React.Component {
var config = this.props.config;
config.EmailSettings.EnableSignUpWithEmail = ReactDOM.findDOMNode(this.refs.allowSignUpWithEmail).checked;
config.EmailSettings.SendEmailNotifications = ReactDOM.findDOMNode(this.refs.sendEmailNotifications).checked;
+ config.EmailSettings.SendPushlNotifications = ReactDOM.findDOMNode(this.refs.sendPushNotifications).checked;
config.EmailSettings.RequireEmailVerification = ReactDOM.findDOMNode(this.refs.requireEmailVerification).checked;
- config.EmailSettings.SendEmailNotifications = ReactDOM.findDOMNode(this.refs.sendEmailNotifications).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.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();
@@ -526,6 +536,61 @@ export default class EmailSettings extends React.Component {
</div>
<div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='sendPushNotifications'
+ >
+ {'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')}
+ />
+ {'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')}
+ />
+ {'false'}
+ </label>
+ <p className='help-text'>{'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'
+ >
+ {'Push Notification Server:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='PushNotificationServer'
+ ref='PushNotificationServer'
+ placeholder='E.g.: "https://push.mattermost.com"'
+ defaultValue={this.props.config.EmailSettings.PushNotificationServer}
+ onChange={this.handleChange}
+ disabled={!this.state.sendPushNotifications}
+ />
+ <p className='help-text'>{'Location of the push notification server.'}</p>
+ </div>
+ </div>
+
+ <div className='form-group'>
<div className='col-sm-12'>
{serverError}
<button