summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--README.md39
-rw-r--r--api/context.go8
-rw-r--r--model/client.go2
-rw-r--r--store/sql_channel_store.go11
-rw-r--r--store/store.go4
-rw-r--r--web/react/components/setting_upload.jsx28
-rw-r--r--web/react/components/team_import_tab.jsx8
-rw-r--r--web/sass-files/sass/partials/_headers.scss3
-rw-r--r--web/sass-files/sass/partials/_settings.scss10
10 files changed, 105 insertions, 12 deletions
diff --git a/.gitignore b/.gitignore
index 97de7eea5..79761adac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,3 +50,7 @@ web/sass-files/sass/.sass-cache/
# Default local file storage
data/*
api/data/*
+
+.agignore
+.ctags
+tags
diff --git a/README.md b/README.md
index 9e49cb8b0..c4276ed78 100644
--- a/README.md
+++ b/README.md
@@ -132,6 +132,45 @@ There are a few configuration settings you might want to adjust when setting up
* *ServiceSettings*:*StorageDirectory* - The file path where files will be stored locally if *UseLocalStorage* is set to true. The operating system user that is running the Mattermost application must have read and write privileges to this directory.
* *AWSSettings*:*S3*\* - If *UseLocalStorage* is set to false, and the S3 settings are configured here, then Mattermost will store files in the provided S3 bucket.
+Email Setup (Optional)
+----------------------
+
+1. Setup an email sending service. If you already have credentials for a SMTP server you can skip this step.
+ 1. [Setup Amazon Simple Email Service](https://console.aws.amazon.com/ses)
+ 2. From the `SMTP Settings` menu click `Create My SMTP Credentials`
+ 3. Copy the `Server Name`, `Port`, `SMTP Username`, and `SMTP Password`
+ 4. From the `Domains` menu setup and verify a new domain. It it also a good practice to enable `Generate DKIM Settings` for this domain.
+ 5. Choose an email address like `feedback@example.com` for Mattermost to send emails from.
+ 6. Test sending an email from `feedback@example.com` by clicking the `Send a Test Email` button and verify everything appears to be working correctly.
+2. Modify the Mattermost configuration file config.json or config_docker.json with the SMTP information.
+ 1. If you're running Mattermost on Amazon Beanstalk you can shell into the instance with the following commands
+ 2. `ssh ec2-user@[domain for the docker instance]`
+ 3. `sudo gpasswd -a ec2-user docker`
+ 4. Retrieve the name of the container with `sudo docker ps`
+ 5. `sudo docker exec -ti container_name /bin/bash`
+2. Edit the config file `vi /config_docker.json` with the settings you captured from the step above. See an example below and notice `ByPassEmail` has been set to `false`
+
+``` bash
+"EmailSettings": {
+ "ByPassEmail" : false,
+ "SMTPUsername": "AKIADTOVBGERKLCBV",
+ "SMTPPassword": "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY",
+ "SMTPServer": "email-smtp.us-east-1.amazonaws.com:465",
+ "UseTLS": true,
+ "FeedbackEmail": "feedback@example.com",
+ "FeedbackName": "Feedback",
+ "ApplePushServer": "",
+ "ApplePushCertPublic": "",
+ "ApplePushCertPrivate": ""
+}
+```
+
+3. Restart Mattermost
+ 1. Find the process id with `ps -A` and look for the process named `platform`
+ 2. Kill the process `kill pid`
+ 3. The service should restart automatically. Verify the Mattermost service is running with `ps -A`
+ 4. Current logged in users will not be affected, but upon logging out or session expiration users will be required to verify their email address.
+
Upgrading Mattermost
---------------------
diff --git a/api/context.go b/api/context.go
index e3f279e90..8babf85f2 100644
--- a/api/context.go
+++ b/api/context.go
@@ -196,7 +196,9 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func (c *Context) LogAudit(extraInfo string) {
audit := &model.Audit{UserId: c.Session.UserId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.AltId}
- Srv.Store.Audit().Save(audit)
+ if r := <-Srv.Store.Audit().Save(audit); r.Err != nil {
+ c.LogError(r.Err)
+ }
}
func (c *Context) LogAuditWithUserId(userId, extraInfo string) {
@@ -206,7 +208,9 @@ func (c *Context) LogAuditWithUserId(userId, extraInfo string) {
}
audit := &model.Audit{UserId: userId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.AltId}
- Srv.Store.Audit().Save(audit)
+ if r := <-Srv.Store.Audit().Save(audit); r.Err != nil {
+ c.LogError(r.Err)
+ }
}
func (c *Context) LogError(err *model.AppError) {
diff --git a/model/client.go b/model/client.go
index 6fcfa5043..17e2466df 100644
--- a/model/client.go
+++ b/model/client.go
@@ -5,6 +5,7 @@ package model
import (
"bytes"
+ l4g "code.google.com/p/log4go"
"fmt"
"io/ioutil"
"net/http"
@@ -93,6 +94,7 @@ func getCookie(name string, resp *http.Response) *http.Cookie {
func (c *Client) Must(result *Result, err *AppError) *Result {
if err != nil {
+ l4g.Close()
time.Sleep(time.Second)
panic(err)
}
diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go
index cf34f2847..d503d2225 100644
--- a/store/sql_channel_store.go
+++ b/store/sql_channel_store.go
@@ -153,7 +153,16 @@ func (s SqlChannelStore) extraUpdated(channel *model.Channel) StoreChannel {
channel.ExtraUpdated()
- if count, err := s.GetMaster().Update(channel); err != nil || count != 1 {
+ _, err := s.GetMaster().Exec(
+ `UPDATE
+ Channels
+ SET
+ ExtraUpdateAt = :Time
+ WHERE
+ Id = :Id`,
+ map[string]interface{}{"Id": channel.Id, "Time": channel.ExtraUpdateAt})
+
+ if err != nil {
result.Err = model.NewAppError("SqlChannelStore.extraUpdated", "Problem updating members last updated time", "id="+channel.Id+", "+err.Error())
}
diff --git a/store/store.go b/store/store.go
index 617ea7f2b..8dbf12b55 100644
--- a/store/store.go
+++ b/store/store.go
@@ -4,7 +4,9 @@
package store
import (
+ l4g "code.google.com/p/log4go"
"github.com/mattermost/platform/model"
+ "time"
)
type StoreResult struct {
@@ -17,6 +19,8 @@ type StoreChannel chan StoreResult
func Must(sc StoreChannel) interface{} {
r := <-sc
if r.Err != nil {
+ l4g.Close()
+ time.Sleep(time.Second)
panic(r.Err)
}
diff --git a/web/react/components/setting_upload.jsx b/web/react/components/setting_upload.jsx
index 83b6d85fc..02789f5dd 100644
--- a/web/react/components/setting_upload.jsx
+++ b/web/react/components/setting_upload.jsx
@@ -46,17 +46,25 @@ module.exports = React.createClass({
serverError: ''
});
},
+ onFileSelect: function(e) {
+ var filename = $(e.target).val();
+ if (filename.substring(3, 11) === 'fakepath') {
+ filename = filename.substring(12);
+ }
+ $(e.target).closest('li').find('.file-status').addClass('hide');
+ $(e.target).closest('li').find('.file-name').removeClass('hide').html(filename);
+ },
render: function() {
var clientError = null;
if (this.state.clientError) {
clientError = (
- <div className='form-group has-error'><label className='control-label'>{this.state.clientError}</label></div>
+ <div className='file-status'>{this.state.clientError}</div>
);
}
var serverError = null;
if (this.state.serverError) {
serverError = (
- <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>
+ <div className='file-status'>{this.state.serverError}</div>
);
}
return (
@@ -65,11 +73,21 @@ module.exports = React.createClass({
<li className='col-xs-offset-3 col-xs-8'>
<ul className='setting-list'>
<li className='setting-list-item'>
+ <span className='btn btn-sm btn-primary btn-file sel-btn'>Select file<input ref='uploadinput' accept={this.props.fileTypesAccepted} type='file' onChange={this.onFileSelect}/></span>
+ <a
+ className={'btn btn-sm btn-primary'}
+ onClick={this.doSubmit}>
+ Import
+ </a>
+ <a
+ className='btn btn-sm btn-link theme'
+ href='#'
+ onClick={this.doCancel}>
+ Cancel
+ </a>
+ <div className='file-status file-name hide'></div>
{serverError}
{clientError}
- <span className='btn btn-sm btn-primary btn-file sel-btn'>Select File<input ref='uploadinput' accept={this.props.fileTypesAccepted} type='file' onChange={this.onFileSelect}/></span>
- <a className={'btn btn-sm btn-primary'} onClick={this.doSubmit}>Import</a>
- <a className='btn btn-sm theme' href='#' onClick={this.doCancel}>Cancel</a>
</li>
</ul>
</li>
diff --git a/web/react/components/team_import_tab.jsx b/web/react/components/team_import_tab.jsx
index ae7f875cb..c21701c0e 100644
--- a/web/react/components/team_import_tab.jsx
+++ b/web/react/components/team_import_tab.jsx
@@ -34,17 +34,17 @@ module.exports = React.createClass({
break;
case 'in-progress':
messageSection = (
- <p>Importing...</p>
+ <p className="confirm-import alert alert-warning"><i className="fa fa-spinner fa-pulse"></i> Importing...</p>
);
break;
case 'done':
messageSection = (
- <p>Import sucessfull: <a href={this.state.link} download='MattermostImportSummary.txt'>View Summary</a></p>
+ <p className="confirm-import alert alert-success"><i className="fa fa-check"></i> Import sucessfull: <a href={this.state.link} download='MattermostImportSummery.txt'>View Summery</a></p>
);
break;
case 'fail':
messageSection = (
- <p>Import failure: <a href={this.state.link} download='MattermostImportSummary.txt'>View Summary</a></p>
+ <p className="confirm-import alert alert-warning"><i className="fa fa-warning"></i> Import failure: <a href={this.state.link} download='MattermostImportSummery.txt'>View Summery</a></p>
);
break;
}
@@ -59,8 +59,8 @@ module.exports = React.createClass({
<h3 className='tab-header'>Import</h3>
<div className='divider-dark first'/>
{uploadSection}
- {messageSection}
<div className='divider-dark'/>
+ {messageSection}
</div>
</div>
);
diff --git a/web/sass-files/sass/partials/_headers.scss b/web/sass-files/sass/partials/_headers.scss
index 571c7ff1f..c311941b6 100644
--- a/web/sass-files/sass/partials/_headers.scss
+++ b/web/sass-files/sass/partials/_headers.scss
@@ -200,6 +200,9 @@
}
}
}
+ .search__clear {
+ display: none;
+ }
}
#navbar {
diff --git a/web/sass-files/sass/partials/_settings.scss b/web/sass-files/sass/partials/_settings.scss
index 0262ef60c..99a7eb7bc 100644
--- a/web/sass-files/sass/partials/_settings.scss
+++ b/web/sass-files/sass/partials/_settings.scss
@@ -111,6 +111,16 @@
}
}
+ .file-status {
+ font-size: 13px;
+ margin-top: 8px;
+ color: #555;
+ }
+
+ .confirm-import {
+ padding: 4px 10px;
+ margin: 10px 0;
+ }
}
}