summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--README.md67
-rw-r--r--api/context.go8
-rw-r--r--model/client.go2
-rw-r--r--requirements.md23
-rw-r--r--store/sql_channel_store.go11
-rw-r--r--store/store.go4
-rw-r--r--utils/config.go2
-rw-r--r--web/react/components/channel_invite_modal.jsx2
-rw-r--r--web/react/components/edit_channel_modal.jsx7
-rw-r--r--web/react/components/new_channel.jsx4
-rw-r--r--web/react/components/post.jsx2
-rw-r--r--web/react/components/search_bar.jsx7
-rw-r--r--web/react/components/search_results.jsx7
-rw-r--r--web/react/components/setting_picture.jsx4
-rw-r--r--web/react/components/setting_upload.jsx28
-rw-r--r--web/react/components/team_feature_tab.jsx4
-rw-r--r--web/react/components/team_import_tab.jsx8
-rw-r--r--web/react/components/team_settings_modal.jsx2
-rw-r--r--web/react/utils/client.jsx17
-rw-r--r--web/react/utils/utils.jsx3
-rw-r--r--web/sass-files/sass/partials/_headers.scss3
-rw-r--r--web/sass-files/sass/partials/_settings.scss10
-rw-r--r--web/templates/head.html2
-rw-r--r--web/web.go15
25 files changed, 198 insertions, 48 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..130c84890 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,7 @@ Local Machine Setup (Docker)
3. When docker is done fetching the image, open http://localhost:8065/ in your browser.
### Arch ###
-1. Install docker using the following commands:
+1. Install Docker using the following commands:
``` bash
pacman -S docker
@@ -70,13 +70,13 @@ Local Machine Setup (Docker)
newgrp docker
```
-2. Start docker container:
+2. Start Docker container:
``` bash
docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform
```
-3. When docker is done fetching the image, open http://localhost:8065/ in your browser.
+3. When Docker is done fetching the image, open http://localhost:8065/ in your browser.
### Additional Notes ###
- If you want to work with the latest master from the repository (i.e. not a stable release) you can run the cmd:
@@ -85,7 +85,7 @@ Local Machine Setup (Docker)
docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform:dev
```
-- Instructions on how to update your docker image are found below.
+- Instructions on how to update your Docker image are found below.
- If you wish to remove mattermost-dev use:
@@ -103,7 +103,7 @@ Local Machine Setup (Docker)
AWS Elastic Beanstalk Setup (Docker)
------------------------------------
-1. Create a new elastic beanstalk docker application using the [Dockerrun.aws.json](docker/0.6/Dockerrun.aws.json) file provided.
+1. Create a new Elastic Beanstalk Docker application using the [Dockerrun.aws.json](docker/0.6/Dockerrun.aws.json) file provided.
1. From the AWS console select Elastic Beanstalk.
2. Select "Create New Application" from the top right.
3. Name the application and press next.
@@ -125,41 +125,80 @@ AWS Elastic Beanstalk Setup (Docker)
Configuration Settings
----------------------
-There are a few configuration settings you might want to adjust when setting up your instance of Mattermost. You can edit them in [config/config.json](config/config.json) or [docker/0.6/config_docker.json](docker/0.6/config_docker.json) if you're running a docker instance.
+There are a few configuration settings you might want to adjust when setting up your instance of Mattermost. You can edit them in [config/config.json](config/config.json) or [docker/0.6/config_docker.json](docker/0.6/config_docker.json) if you're running a Docker instance.
* *EmailSettings*:*ByPassEmail* - If this is set to true, then users on the system will not need to verify their email addresses when signing up. In addition, no emails will ever be sent.
* *ServiceSettings*:*UseLocalStorage* - If this is set to true, then your Mattermost server will store uploaded files in the storage directory specified by *StorageDirectory*. *StorageDirectory* must be set if *UseLocalStorage* is set to true.
* *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.
-Upgrading Mattermost
----------------------
+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 Preview
+----------------------------
### Docker ###
-To upgrade your docker image to the latest release (NOTE: this will destroy all data in the docker container):
+To upgrade your Docker image to a preview of the latest stable release (NOTE: this will erase all data in the Docker container, including the database):
-1. Stop your docker container by running:
+1. Stop your Docker container by running:
``` bash
docker stop mattermost-dev
```
-2. Delete your docker container by running:
+2. Delete your Docker container by running:
``` bash
docker rm mattermost-dev
```
-3. Update your docker image by running:
+3. Update your Docker image by running:
``` bash
docker pull mattermost/platform
```
-4. Start your docker container by running:
+4. Start your Docker container by running:
``` bash
docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform
```
-To upgrade to the latest master from the repository replace `mattermost/platform` with `mattermost/platform:dev` in the above instructions.
+To upgrade to the latest development build on master from the repository replace `mattermost/platform` with `mattermost/platform:dev` in the instructions 3) and 4) above.
Contributing
------------
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/requirements.md b/requirements.md
new file mode 100644
index 000000000..cc0d1833d
--- /dev/null
+++ b/requirements.md
@@ -0,0 +1,23 @@
+# Requirements
+
+## Web Client
+
+Supported Operating Systems and Browsers:
+
+- PC: Windows 7, Windows 8 (Chrome 43+, Firefox 38+, Internet Explorer 10+)
+- Mac: OS 10 (Safari 7, Chrome 43+)
+- Linux: Arch 4.0.0 (Chrome 43+)
+- iPhone 4s and higher (Safari on iOS 8.3+, Chrome 43+)
+- Android 5 and higher (Chrome 43+)
+
+## Server
+
+While the pre-released version of Mattermost is not currently supported in production, the intention from the product team is to support:
+
+- Ubuntu
+- Debian
+- CentOS
+- RedHat Enterprise Linux
+- Oracle Linux
+
+The Mattermost roadmap does not currently include production support for Fedora, FreeBSD or Arch Linux.
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/utils/config.go b/utils/config.go
index 536d0d802..46daf203c 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -234,8 +234,8 @@ func LoadConfig(fileName string) {
// Check for a valid email for feedback, if not then do feedback@domain
if _, err := mail.ParseAddress(config.EmailSettings.FeedbackEmail); err != nil {
- config.EmailSettings.FeedbackEmail = "feedback@localhost"
l4g.Error("Misconfigured feedback email setting: %s", config.EmailSettings.FeedbackEmail)
+ config.EmailSettings.FeedbackEmail = "feedback@localhost"
}
configureLog(config.LogSettings)
diff --git a/web/react/components/channel_invite_modal.jsx b/web/react/components/channel_invite_modal.jsx
index e446167ec..b70811db1 100644
--- a/web/react/components/channel_invite_modal.jsx
+++ b/web/react/components/channel_invite_modal.jsx
@@ -138,7 +138,7 @@ export default class ChannelInviteModal extends React.Component {
<div className='modal-content'>
<div className='modal-header'>
<button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>&times;</span></button>
- <h4 className='modal-title'>Add New Members to <span className='name'>{this.state.channel_name}</span></h4>
+ <h4 className='modal-title'>Add New Members to <span className='name'>{this.state.channelName}</span></h4>
</div>
<div className='modal-body'>
{inviteError}
diff --git a/web/react/components/edit_channel_modal.jsx b/web/react/components/edit_channel_modal.jsx
index 1a633b193..76f0c2c4d 100644
--- a/web/react/components/edit_channel_modal.jsx
+++ b/web/react/components/edit_channel_modal.jsx
@@ -50,13 +50,18 @@ module.exports = React.createClass({
render: function() {
var server_error = this.state.server_error ? <div className='form-group has-error'><br/><label className='control-label'>{ this.state.server_error }</label></div> : null;
+ var editTitle = <h4 className='modal-title' ref='title'>Edit Description</h4>;
+ if (this.state.title) {
+ editTitle = <h4 className='modal-title' ref='title'>Edit Description for <span className='name'>{this.state.title}</span></h4>;
+ }
+
return (
<div className="modal fade" ref="modal" id="edit_channel" role="dialog" tabIndex="-1" aria-hidden="true">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
- <h4 className="modal-title" ref="title">Edit Description for {this.state.title}</h4>
+ {editTitle}
</div>
<div className="modal-body">
<textarea className="form-control no-resize" rows="6" ref="channelDesc" maxLength="1024" value={this.state.description} onChange={this.handleUserInput}></textarea>
diff --git a/web/react/components/new_channel.jsx b/web/react/components/new_channel.jsx
index b00376758..38c9ea76d 100644
--- a/web/react/components/new_channel.jsx
+++ b/web/react/components/new_channel.jsx
@@ -127,12 +127,12 @@ module.exports = React.createClass({
<div className='modal-body'>
<div className={displayNameClass}>
<label className='control-label'>Display Name</label>
- <input onKeyUp={this.displayNameKeyUp} type='text' ref='display_name' className='form-control' placeholder='Enter display name' maxLength='64' />
+ <input onKeyUp={this.displayNameKeyUp} type='text' ref='display_name' className='form-control' placeholder='Enter display name' maxLength='22' />
{displayNameError}
</div>
<div className={nameClass}>
<label className='control-label'>Handle</label>
- <input type='text' className='form-control' ref='channel_name' placeholder="lowercase alphanumeric's only" maxLength='64' />
+ <input type='text' className='form-control' ref='channel_name' placeholder="lowercase alphanumeric's only" maxLength='22' />
{nameError}
</div>
<div className='form-group'>
diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx
index 7bc6a8c01..53ffeb400 100644
--- a/web/react/components/post.jsx
+++ b/web/react/components/post.jsx
@@ -102,7 +102,7 @@ module.exports = React.createClass({
currentUserCss = "current--user";
}
- var timestamp = UserStore.getCurrentUser().update_at;
+ var timestamp = UserStore.getProfile(post.user_id).update_at;
return (
<div>
diff --git a/web/react/components/search_bar.jsx b/web/react/components/search_bar.jsx
index e39cf5d46..b11b39e9e 100644
--- a/web/react/components/search_bar.jsx
+++ b/web/react/components/search_bar.jsx
@@ -48,6 +48,13 @@ module.exports = React.createClass({
});
AppDispatcher.handleServerAction({
+ type: ActionTypes.RECIEVED_SEARCH_TERM,
+ term: null,
+ do_search: false,
+ is_mention_search: false
+ });
+
+ AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_POST_SELECTED,
results: null
});
diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx
index 643ad112b..b1efd7685 100644
--- a/web/react/components/search_results.jsx
+++ b/web/react/components/search_results.jsx
@@ -24,6 +24,13 @@ var RhsHeaderSearch = React.createClass({
});
AppDispatcher.handleServerAction({
+ type: ActionTypes.RECIEVED_SEARCH_TERM,
+ term: null,
+ do_search: false,
+ is_mention_search: false
+ });
+
+ AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_POST_SELECTED,
results: null
});
diff --git a/web/react/components/setting_picture.jsx b/web/react/components/setting_picture.jsx
index e97b67706..5b12ad7e9 100644
--- a/web/react/components/setting_picture.jsx
+++ b/web/react/components/setting_picture.jsx
@@ -48,6 +48,7 @@ module.exports = React.createClass({
}
confirmButton = <a className={confirmButtonClass} onClick={this.props.submit}>Save</a>;
}
+ var helpText = 'Upload a profile picture in either JPG or PNG format, at least ' + config.ProfileWidth + 'px in width and ' + config.ProfileHeight + 'px height.'
var self = this;
return (
@@ -59,6 +60,9 @@ module.exports = React.createClass({
{img}
</li>
<li className='setting-list-item'>
+ {helpText}
+ </li>
+ <li className='setting-list-item'>
{serverError}
{clientError}
<span className='btn btn-sm btn-primary btn-file sel-btn'>Select<input ref='input' accept='.jpg,.png,.bmp' type='file' onChange={this.props.pictureChange}/></span>
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_feature_tab.jsx b/web/react/components/team_feature_tab.jsx
index ee0bfa874..4f28d84c6 100644
--- a/web/react/components/team_feature_tab.jsx
+++ b/web/react/components/team_feature_tab.jsx
@@ -133,10 +133,10 @@ module.exports = React.createClass({
<div>
<div className='modal-header'>
<button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>&times;</span></button>
- <h4 className='modal-title' ref='title'><i className='modal-back'></i>Feature Settings</h4>
+ <h4 className='modal-title' ref='title'><i className='modal-back'></i>Advanced Features</h4>
</div>
<div ref='wrapper' className='user-settings'>
- <h3 className='tab-header'>Feature Settings</h3>
+ <h3 className='tab-header'>Advanced Features</h3>
<div className='divider-dark first'/>
{valetSection}
<div className='divider-dark'/>
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/react/components/team_settings_modal.jsx b/web/react/components/team_settings_modal.jsx
index c9f479a22..ef2564de0 100644
--- a/web/react/components/team_settings_modal.jsx
+++ b/web/react/components/team_settings_modal.jsx
@@ -27,8 +27,8 @@ module.exports = React.createClass({
},
render: function() {
var tabs = [];
- tabs.push({name: 'feature', uiName: 'Features', icon: 'glyphicon glyphicon-wrench'});
tabs.push({name: 'import', uiName: 'Import', icon: 'glyphicon glyphicon-upload'});
+ tabs.push({name: 'feature', uiName: 'Advanced', icon: 'glyphicon glyphicon-wrench'});
return (
<div className='modal fade' ref='modal' id='team_settings' role='dialog' tabIndex='-1' aria-hidden='true'>
diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx
index 103292abf..754843697 100644
--- a/web/react/utils/client.jsx
+++ b/web/react/utils/client.jsx
@@ -252,6 +252,7 @@ module.exports.revokeSession = function(altId, success, error) {
module.exports.getSessions = function(userId, success, error) {
$.ajax({
+ cache: false,
url: '/api/v1/users/' + userId + '/sessions',
dataType: 'json',
contentType: 'application/json',
@@ -282,6 +283,7 @@ module.exports.getMeSynchronous = function(success, error) {
var currentUser = null;
$.ajax({
async: false,
+ cache: false,
url: '/api/v1/users/me',
dataType: 'json',
contentType: 'application/json',
@@ -293,12 +295,9 @@ module.exports.getMeSynchronous = function(success, error) {
}
},
error: function onError(xhr, status, err) {
- var ieChecker = window.navigator.userAgent; // This and the condition below is used to check specifically for browsers IE10 & 11 to suppress a 200 'OK' error from appearing on login
- if (xhr.status !== 200 || !(ieChecker.indexOf('Trident/7.0') > 0 || ieChecker.indexOf('Trident/6.0') > 0)) {
- if (error) {
- var e = handleError('getMeSynchronous', xhr, status, err);
- error(e);
- }
+ if (error) {
+ var e = handleError('getMeSynchronous', xhr, status, err);
+ error(e);
}
}
});
@@ -566,6 +565,7 @@ module.exports.updateLastViewedAt = function(channelId, success, error) {
function getChannels(success, error) {
$.ajax({
+ cache: false,
url: '/api/v1/channels/',
dataType: 'json',
type: 'GET',
@@ -581,6 +581,7 @@ module.exports.getChannels = getChannels;
module.exports.getChannel = function(id, success, error) {
$.ajax({
+ cache: false,
url: '/api/v1/channels/' + id + '/',
dataType: 'json',
type: 'GET',
@@ -610,6 +611,7 @@ module.exports.getMoreChannels = function(success, error) {
function getChannelCounts(success, error) {
$.ajax({
+ cache: false,
url: '/api/v1/channels/counts',
dataType: 'json',
type: 'GET',
@@ -653,6 +655,7 @@ module.exports.executeCommand = function(channelId, command, suggest, success, e
module.exports.getPosts = function(channelId, offset, limit, success, error, complete) {
$.ajax({
+ cache: false,
url: '/api/v1/channels/' + channelId + '/posts/' + offset + '/' + limit,
dataType: 'json',
type: 'GET',
@@ -668,6 +671,7 @@ module.exports.getPosts = function(channelId, offset, limit, success, error, com
module.exports.getPost = function(channelId, postId, success, error) {
$.ajax({
+ cache: false,
url: '/api/v1/channels/' + channelId + '/post/' + postId,
dataType: 'json',
type: 'GET',
@@ -791,6 +795,7 @@ module.exports.removeChannelMember = function(id, data, success, error) {
module.exports.getProfiles = function(success, error) {
$.ajax({
+ cache: false,
url: '/api/v1/users/profiles',
dataType: 'json',
contentType: 'application/json',
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 09cd299df..4571312bb 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -271,11 +271,12 @@ function getYoutubeEmbed(link) {
iframe.setAttribute('src',
'https://www.youtube.com/embed/' +
div.id +
- '?autoplay=1&autohide=1&border=0&wmode=opaque&enablejsapi=1');
+ '?autoplay=1&autohide=1&border=0&wmode=opaque&fs=1&enablejsapi=1');
iframe.setAttribute('width', '480px');
iframe.setAttribute('height', '360px');
iframe.setAttribute('type', 'text/html');
iframe.setAttribute('frameborder', '0');
+ iframe.setAttribute('allowfullscreen', 'allowfullscreen');
div.parentNode.replaceChild(iframe, 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;
+ }
}
}
diff --git a/web/templates/head.html b/web/templates/head.html
index 5448b09ed..02904e764 100644
--- a/web/templates/head.html
+++ b/web/templates/head.html
@@ -50,6 +50,8 @@
config = {};
}
config.SiteName = '{{ .SiteName }}';
+ config.ProfileWidth = '{{ .Props.ProfileWidth }}'
+ config.ProfileHeight = '{{ .Props.ProfileHeight }}'
</script>
<script src="/static/js/bundle.js"></script>
diff --git a/web/web.go b/web/web.go
index d6f8d553b..dc2b5dced 100644
--- a/web/web.go
+++ b/web/web.go
@@ -4,18 +4,19 @@
package web
import (
- l4g "code.google.com/p/log4go"
"fmt"
+ "html/template"
+ "net/http"
+ "strconv"
+ "strings"
+
+ l4g "code.google.com/p/log4go"
"github.com/gorilla/mux"
"github.com/mattermost/platform/api"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
"github.com/mssola/user_agent"
"gopkg.in/fsnotify.v1"
- "html/template"
- "net/http"
- "strconv"
- "strings"
)
var Templates *template.Template
@@ -30,6 +31,8 @@ func NewHtmlTemplatePage(templateName string, title string) *HtmlTemplatePage {
props := make(map[string]string)
props["AnalyticsUrl"] = utils.Cfg.ServiceSettings.AnalyticsUrl
+ props["ProfileHeight"] = fmt.Sprintf("%v", utils.Cfg.ImageSettings.ProfileHeight)
+ props["ProfileWidth"] = fmt.Sprintf("%v", utils.Cfg.ImageSettings.ProfileWidth)
return &HtmlTemplatePage{TemplateName: templateName, Title: title, SiteName: utils.Cfg.ServiceSettings.SiteName, Props: props}
}
@@ -212,7 +215,7 @@ func signupTeamComplete(c *api.Context, w http.ResponseWriter, r *http.Request)
props := model.MapFromJson(strings.NewReader(data))
t, err := strconv.ParseInt(props["time"], 10, 64)
- if err != nil || model.GetMillis()-t > 1000*60*60 { // one hour
+ if err != nil || model.GetMillis()-t > 1000*60*60*24*30 { // 30 days
c.Err = model.NewAppError("signupTeamComplete", "The signup link has expired", "")
return
}