summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/preference_test.go3
-rw-r--r--doc/install/Docker-Single-Container.md7
-rw-r--r--model/webhook.go5
-rw-r--r--web/react/components/admin_console/email_settings.jsx8
-rw-r--r--web/react/components/admin_console/log_settings.jsx18
-rw-r--r--web/react/components/edit_post_modal.jsx4
-rw-r--r--web/react/components/popover_list_members.jsx22
-rw-r--r--web/react/components/rhs_comment.jsx26
-rw-r--r--web/react/components/sidebar.jsx14
-rw-r--r--web/react/components/user_profile.jsx35
-rw-r--r--web/react/components/user_settings/user_settings_appearance.jsx10
-rw-r--r--web/react/components/user_settings/user_settings_display.jsx21
-rw-r--r--web/react/components/user_settings/user_settings_notifications.jsx65
-rw-r--r--web/static/images/webhook_icon.jpgbin0 -> 68190 bytes
-rw-r--r--web/web.go16
15 files changed, 152 insertions, 102 deletions
diff --git a/api/preference_test.go b/api/preference_test.go
index 9d3db9e2f..eaa92fe47 100644
--- a/api/preference_test.go
+++ b/api/preference_test.go
@@ -60,9 +60,10 @@ func TestGetAllPreferences(t *testing.T) {
Client.LoginByEmail(team.Name, user2.Email, "pwd")
+ // note that user2 will automatically have a preference set for them to show user1 for direct messages
if result, err := Client.GetAllPreferences(); err != nil {
t.Fatal(err)
- } else if data := result.Data.(model.Preferences); len(data) != 0 {
+ } else if data := result.Data.(model.Preferences); len(data) != 1 {
t.Fatal("received the wrong number of preferences")
}
}
diff --git a/doc/install/Docker-Single-Container.md b/doc/install/Docker-Single-Container.md
index 4b952cd71..7c0784ad0 100644
--- a/doc/install/Docker-Single-Container.md
+++ b/doc/install/Docker-Single-Container.md
@@ -2,6 +2,13 @@
The following install instructions are for single-container installs of Mattermost using Docker for exploring product functionality and upgrading to newer versions.
+### One-line Docker Install ###
+
+If you have Docker set up, Mattermost installs in one-line:
+`docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform`
+
+Otherwise, see step-by-step available:
+
### Mac OSX ###
1. Install Docker Toolbox using instructions at: http://docs.docker.com/installation/mac/
diff --git a/model/webhook.go b/model/webhook.go
index 3bf034908..9b9969b96 100644
--- a/model/webhook.go
+++ b/model/webhook.go
@@ -8,6 +8,11 @@ import (
"io"
)
+const (
+ DEFAULT_WEBHOOK_USERNAME = "webhook"
+ DEFAULT_WEBHOOK_ICON = "/static/images/webhook_icon.jpg"
+)
+
type IncomingWebhook struct {
Id string `json:"id"`
CreateAt int64 `json:"create_at"`
diff --git a/web/react/components/admin_console/email_settings.jsx b/web/react/components/admin_console/email_settings.jsx
index 01759b222..40e00ff04 100644
--- a/web/react/components/admin_console/email_settings.jsx
+++ b/web/react/components/admin_console/email_settings.jsx
@@ -440,9 +440,11 @@ export default class EmailSettings extends React.Component {
className='table table-bordered'
cellPadding='5'
>
- <tr><td className='help-text'>{'None'}</td><td className='help-text'>{'Mattermost will send email over an unsecure connection.'}</td></tr>
- <tr><td className='help-text'>{'TLS'}</td><td className='help-text'>{'Encrypts the communication between Mattermost and your email server.'}</td></tr>
- <tr><td className='help-text'>{'STARTTLS'}</td><td className='help-text'>{'Takes an existing insecure connection and attempts to upgrade it to a secure connection using TLS.'}</td></tr>
+ <tbody>
+ <tr><td className='help-text'>{'None'}</td><td className='help-text'>{'Mattermost will send email over an unsecure connection.'}</td></tr>
+ <tr><td className='help-text'>{'TLS'}</td><td className='help-text'>{'Encrypts the communication between Mattermost and your email server.'}</td></tr>
+ <tr><td className='help-text'>{'STARTTLS'}</td><td className='help-text'>{'Takes an existing insecure connection and attempts to upgrade it to a secure connection using TLS.'}</td></tr>
+ </tbody>
</table>
</div>
<div className='help-text'>
diff --git a/web/react/components/admin_console/log_settings.jsx b/web/react/components/admin_console/log_settings.jsx
index 931818bb8..7e9eda89b 100644
--- a/web/react/components/admin_console/log_settings.jsx
+++ b/web/react/components/admin_console/log_settings.jsx
@@ -249,22 +249,24 @@ export default class LogSettings extends React.Component {
onChange={this.handleChange}
disabled={!this.state.fileEnable}
/>
- <p className='help-text'>
+ <div className='help-text'>
{'Format of log message output. If blank will be set to "[%D %T] [%L] %M", where:'}
<div className='help-text'>
<table
className='table table-bordered'
cellPadding='5'
>
- <tr><td className='help-text'>{'%T'}</td><td className='help-text'>{'Time (15:04:05 MST)'}</td></tr>
- <tr><td className='help-text'>{'%D'}</td><td className='help-text'>{'Date (2006/01/02)'}</td></tr>
- <tr><td className='help-text'>{'%d'}</td><td className='help-text'>{'Date (01/02/06)'}</td></tr>
- <tr><td className='help-text'>{'%L'}</td><td className='help-text'>{'Level (DEBG, INFO, EROR)'}</td></tr>
- <tr><td className='help-text'>{'%S'}</td><td className='help-text'>{'Source'}</td></tr>
- <tr><td className='help-text'>{'%M'}</td><td className='help-text'>{'Message'}</td></tr>
+ <tbody>
+ <tr><td className='help-text'>{'%T'}</td><td className='help-text'>{'Time (15:04:05 MST)'}</td></tr>
+ <tr><td className='help-text'>{'%D'}</td><td className='help-text'>{'Date (2006/01/02)'}</td></tr>
+ <tr><td className='help-text'>{'%d'}</td><td className='help-text'>{'Date (01/02/06)'}</td></tr>
+ <tr><td className='help-text'>{'%L'}</td><td className='help-text'>{'Level (DEBG, INFO, EROR)'}</td></tr>
+ <tr><td className='help-text'>{'%S'}</td><td className='help-text'>{'Source'}</td></tr>
+ <tr><td className='help-text'>{'%M'}</td><td className='help-text'>{'Message'}</td></tr>
+ </tbody>
</table>
</div>
- </p>
+ </div>
</div>
</div>
diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx
index 90d9696e7..b259b3c18 100644
--- a/web/react/components/edit_post_modal.jsx
+++ b/web/react/components/edit_post_modal.jsx
@@ -70,7 +70,7 @@ export default class EditPostModal extends React.Component {
refocusId: options.refocusId || ''
});
- $(React.findDOMNode(this.refs.modal)).modal('show');
+ $(ReactDOM.findDOMNode(this.refs.modal)).modal('show');
}
componentDidMount() {
var self = this;
@@ -92,7 +92,7 @@ export default class EditPostModal extends React.Component {
$('#edit_textbox').get(0).focus();
});
- $(React.findDOMNode(this.refs.modal)).on('hide.bs.modal', function onShown() {
+ $(ReactDOM.findDOMNode(this.refs.modal)).on('hide.bs.modal', function onShown() {
if (self.state.refocusId !== '') {
setTimeout(() => {
$(self.state.refocusId).get(0).focus();
diff --git a/web/react/components/popover_list_members.jsx b/web/react/components/popover_list_members.jsx
index 5ea452830..155e88600 100644
--- a/web/react/components/popover_list_members.jsx
+++ b/web/react/components/popover_list_members.jsx
@@ -35,13 +35,20 @@ export default class PopoverListMembers extends React.Component {
const teamMembers = UserStore.getProfilesUsernameMap();
if (members && teamMembers) {
- members.sort(function compareByLocal(a, b) {
+ members.sort((a, b) => {
return a.username.localeCompare(b.username);
});
- members.forEach(function addMemberElement(m) {
+ members.forEach((m, i) => {
if (teamMembers[m.username] && teamMembers[m.username].delete_at <= 0) {
- popoverHtml.push(<div className='text--nowrap'>{m.username}</div>);
+ popoverHtml.push(
+ <div
+ className='text--nowrap'
+ key={'popover-member-' + i}
+ >
+ {m.username}
+ </div>
+ );
count++;
}
});
@@ -58,7 +65,14 @@ export default class PopoverListMembers extends React.Component {
trigger='click'
placement='bottom'
rootClose={true}
- overlay={<Popover title='Members'>{popoverHtml}</Popover>}
+ overlay={
+ <Popover
+ title='Members'
+ id='member-list-popover'
+ >
+ {popoverHtml}
+ </Popover>
+ }
>
<div id='member_popover'>
<div>
diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx
index 402e64080..d3a4cfaeb 100644
--- a/web/react/components/rhs_comment.jsx
+++ b/web/react/components/rhs_comment.jsx
@@ -29,7 +29,7 @@ export default class RhsComment extends React.Component {
var post = this.props.post;
Client.createPost(post, post.channel_id,
- function success(data) {
+ (data) => {
AsyncClient.getPosts(post.channel_id);
var channel = ChannelStore.get(post.channel_id);
@@ -43,11 +43,11 @@ export default class RhsComment extends React.Component {
post: data
});
},
- function fail() {
+ () => {
post.state = Constants.POST_FAILED;
PostStore.updatePendingPost(post);
this.forceUpdate();
- }.bind(this)
+ }
);
post.state = Constants.POST_LOADING;
@@ -84,7 +84,10 @@ export default class RhsComment extends React.Component {
if (isOwner) {
dropdownContents.push(
- <li role='presentation'>
+ <li
+ role='presentation'
+ key='edit-button'
+ >
<a
href='#'
role='menuitem'
@@ -95,7 +98,7 @@ export default class RhsComment extends React.Component {
data-postid={post.id}
data-channelid={post.channel_id}
>
- Edit
+ {'Edit'}
</a>
</li>
);
@@ -103,7 +106,10 @@ export default class RhsComment extends React.Component {
if (isOwner || isAdmin) {
dropdownContents.push(
- <li role='presentation'>
+ <li
+ role='presentation'
+ key='delete-button'
+ >
<a
href='#'
role='menuitem'
@@ -114,7 +120,7 @@ export default class RhsComment extends React.Component {
data-channelid={post.channel_id}
data-comments={0}
>
- Delete
+ {'Delete'}
</a>
</li>
);
@@ -162,7 +168,7 @@ export default class RhsComment extends React.Component {
href='#'
onClick={this.retryComment}
>
- Retry
+ {'Retry'}
</a>
);
} else if (post.state === Constants.POST_LOADING) {
@@ -213,14 +219,14 @@ export default class RhsComment extends React.Component {
</li>
</ul>
<div className='post-body'>
- <p className={postClass}>
+ <div className={postClass}>
{loading}
<div
ref='message_holder'
onClick={TextFormatting.handleClick}
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message)}}
/>
- </p>
+ </div>
{fileAttachment}
</div>
</div>
diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx
index 4594555e9..889bc0fbd 100644
--- a/web/react/components/sidebar.jsx
+++ b/web/react/components/sidebar.jsx
@@ -46,7 +46,7 @@ export default class Sidebar extends React.Component {
const state = this.getStateFromStores();
state.newChannelModalType = '';
- state.showMoreDirectChannelsModal = false;
+ state.showDirectChannelsModal = false;
state.loadingDMChannel = -1;
this.state = state;
@@ -471,7 +471,9 @@ export default class Sidebar extends React.Component {
}
let closeButton = null;
- const removeTooltip = <Tooltip>{'Remove from list'}</Tooltip>;
+ const removeTooltip = (
+ <Tooltip id='remove-dm-tooltip'>{'Remove from list'}</Tooltip>
+ );
if (handleClose && !badge) {
closeButton = (
<OverlayTrigger
@@ -564,8 +566,12 @@ export default class Sidebar extends React.Component {
showChannelModal = true;
}
- const createChannelTootlip = <Tooltip>{'Create new channel'}</Tooltip>;
- const createGroupTootlip = <Tooltip>{'Create new group'}</Tooltip>;
+ const createChannelTootlip = (
+ <Tooltip id='new-channel-tooltip' >{'Create new channel'}</Tooltip>
+ );
+ const createGroupTootlip = (
+ <Tooltip id='new-group-tooltip'>{'Create new group'}</Tooltip>
+ );
return (
<div>
diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx
index 47d836321..da0c1aaf9 100644
--- a/web/react/components/user_profile.jsx
+++ b/web/react/components/user_profile.jsx
@@ -65,19 +65,29 @@ export default class UserProfile extends React.Component {
var dataContent = [];
dataContent.push(
- <img className='user-popover__image'
+ <img
+ className='user-popover__image'
src={'/api/v1/users/' + this.state.profile.id + '/image?time=' + this.state.profile.update_at}
height='128'
width='128'
+ key='user-popover-image'
/>
);
if (!global.window.config.ShowEmailAddress === 'true') {
- dataContent.push(<div className='text-nowrap'>{'Email not shared'}</div>);
+ dataContent.push(
+ <div
+ className='text-nowrap'
+ key='user-popover-no-email'
+ >
+ {'Email not shared'}
+ </div>
+ );
} else {
dataContent.push(
<div
data-toggle='tooltip'
title="' + this.state.profile.email + '"
+ key='user-popover-email'
>
<a
href="mailto:' + this.state.profile.email + '"
@@ -94,14 +104,21 @@ export default class UserProfile extends React.Component {
trigger='click'
placement='right'
rootClose={true}
- overlay={<Popover title={this.state.profile.username}>{dataContent}</Popover>}
- >
- <div
- className='user-popover'
- id={'profile_' + this.uniqueId}
+ overlay={
+ <Popover
+ title={this.state.profile.username}
+ id='user-profile-popover'
+ >
+ {dataContent}
+ </Popover>
+ }
>
- {name}
- </div>
+ <div
+ className='user-popover'
+ id={'profile_' + this.uniqueId}
+ >
+ {name}
+ </div>
</OverlayTrigger>
);
}
diff --git a/web/react/components/user_settings/user_settings_appearance.jsx b/web/react/components/user_settings/user_settings_appearance.jsx
index 7f363e92e..8c62a189d 100644
--- a/web/react/components/user_settings/user_settings_appearance.jsx
+++ b/web/react/components/user_settings/user_settings_appearance.jsx
@@ -152,9 +152,8 @@ export default class UserSettingsAppearance extends React.Component {
<input type='radio'
checked={!displayCustom}
onChange={this.updateType.bind(this, 'premade')}
- >
- {'Theme Colors'}
- </input>
+ />
+ {'Theme Colors'}
</label>
<br/>
</div>
@@ -164,9 +163,8 @@ export default class UserSettingsAppearance extends React.Component {
<input type='radio'
checked={displayCustom}
onChange={this.updateType.bind(this, 'custom')}
- >
- {'Custom Theme'}
- </input>
+ />
+ {'Custom Theme'}
</label>
<br/>
</div>
diff --git a/web/react/components/user_settings/user_settings_display.jsx b/web/react/components/user_settings/user_settings_display.jsx
index ec209c218..22a62273c 100644
--- a/web/react/components/user_settings/user_settings_display.jsx
+++ b/web/react/components/user_settings/user_settings_display.jsx
@@ -1,7 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import { savePreferences } from '../../utils/client.jsx';
+import {savePreferences} from '../../utils/client.jsx';
import SettingItemMin from '../setting_item_min.jsx';
import SettingItemMax from '../setting_item_max.jsx';
import Constants from '../../utils/constants.jsx';
@@ -38,7 +38,7 @@ export default class UserSettingsDisplay extends React.Component {
);
}
handleClockRadio(militaryTime) {
- this.setState({militaryTime: militaryTime});
+ this.setState({militaryTime});
}
updateSection(section) {
this.setState(getDisplayStateFromStores());
@@ -57,7 +57,7 @@ export default class UserSettingsDisplay extends React.Component {
const serverError = this.state.serverError || null;
let clockSection;
if (this.props.activeSection === 'clock') {
- let clockFormat = [false, false];
+ const clockFormat = [false, false];
if (this.state.militaryTime === 'true') {
clockFormat[1] = true;
} else {
@@ -77,9 +77,8 @@ export default class UserSettingsDisplay extends React.Component {
type='radio'
checked={clockFormat[0]}
onChange={this.handleClockRadio.bind(this, 'false')}
- >
- 12-hour clock (example: 4:00 PM)
- </input>
+ />
+ {'12-hour clock (example: 4:00 PM)'}
</label>
<br/>
</div>
@@ -89,9 +88,8 @@ export default class UserSettingsDisplay extends React.Component {
type='radio'
checked={clockFormat[1]}
onChange={this.handleClockRadio.bind(this, 'true')}
- >
- 24-hour clock (example: 16:00)
- </input>
+ />
+ {'24-hour clock (example: 16:00)'}
</label>
<br/>
</div>
@@ -99,7 +97,6 @@ export default class UserSettingsDisplay extends React.Component {
</div>
];
-
clockSection = (
<SettingItemMax
title='Clock Display'
@@ -138,13 +135,13 @@ export default class UserSettingsDisplay extends React.Component {
className='close'
data-dismiss='modal'
aria-label='Close'
- >
+ >
<span aria-hidden='true'>{'×'}</span>
</button>
<h4
className='modal-title'
ref='title'
- >
+ >
<i className='modal-back'></i>
{'Display Settings'}
</h4>
diff --git a/web/react/components/user_settings/user_settings_notifications.jsx b/web/react/components/user_settings/user_settings_notifications.jsx
index 4dbb9b96f..8693af494 100644
--- a/web/react/components/user_settings/user_settings_notifications.jsx
+++ b/web/react/components/user_settings/user_settings_notifications.jsx
@@ -228,9 +228,8 @@ export default class NotificationsTab extends React.Component {
<input type='radio'
checked={notifyActive[0]}
onChange={this.handleNotifyRadio.bind(this, 'all')}
- >
- For all activity
- </input>
+ />
+ {'For all activity'}
</label>
<br/>
</div>
@@ -240,9 +239,8 @@ export default class NotificationsTab extends React.Component {
type='radio'
checked={notifyActive[1]}
onChange={this.handleNotifyRadio.bind(this, 'mention')}
- >
- Only for mentions and direct messages
- </input>
+ />
+ {'Only for mentions and direct messages'}
</label>
<br/>
</div>
@@ -252,9 +250,8 @@ export default class NotificationsTab extends React.Component {
type='radio'
checked={notifyActive[2]}
onChange={this.handleNotifyRadio.bind(this, 'none')}
- >
- Never
- </input>
+ />
+ {'Never'}
</label>
</div>
</div>
@@ -320,9 +317,8 @@ export default class NotificationsTab extends React.Component {
type='radio'
checked={soundActive[0]}
onChange={this.handleSoundRadio.bind(this, 'true')}
- >
- On
- </input>
+ />
+ {'On'}
</label>
<br/>
</div>
@@ -332,9 +328,8 @@ export default class NotificationsTab extends React.Component {
type='radio'
checked={soundActive[1]}
onChange={this.handleSoundRadio.bind(this, 'false')}
- >
- Off
- </input>
+ />
+ {'Off'}
</label>
<br/>
</div>
@@ -402,9 +397,8 @@ export default class NotificationsTab extends React.Component {
type='radio'
checked={emailActive[0]}
onChange={this.handleEmailRadio.bind(this, 'true')}
- >
- On
- </input>
+ />
+ {'On'}
</label>
<br/>
</div>
@@ -414,9 +408,8 @@ export default class NotificationsTab extends React.Component {
type='radio'
checked={emailActive[1]}
onChange={this.handleEmailRadio.bind(this, 'false')}
- >
- Off
- </input>
+ />
+ {'Off'}
</label>
<br/>
</div>
@@ -482,9 +475,8 @@ export default class NotificationsTab extends React.Component {
type='checkbox'
checked={this.state.firstNameKey}
onChange={handleUpdateFirstNameKey}
- >
- {'Your case sensitive first name "' + user.first_name + '"'}
- </input>
+ />
+ {'Your case sensitive first name "' + user.first_name + '"'}
</label>
</div>
</div>
@@ -502,9 +494,8 @@ export default class NotificationsTab extends React.Component {
type='checkbox'
checked={this.state.usernameKey}
onChange={handleUpdateUsernameKey}
- >
- {'Your non-case sensitive username "' + user.username + '"'}
- </input>
+ />
+ {'Your non-case sensitive username "' + user.username + '"'}
</label>
</div>
</div>
@@ -521,9 +512,8 @@ export default class NotificationsTab extends React.Component {
type='checkbox'
checked={this.state.mentionKey}
onChange={handleUpdateMentionKey}
- >
- {'Your username mentioned "@' + user.username + '"'}
- </input>
+ />
+ {'Your username mentioned "@' + user.username + '"'}
</label>
</div>
</div>
@@ -540,9 +530,8 @@ export default class NotificationsTab extends React.Component {
type='checkbox'
checked={this.state.allKey}
onChange={handleUpdateAllKey}
- >
- {'Team-wide mentions "@all"'}
- </input>
+ />
+ {'Team-wide mentions "@all"'}
</label>
</div>
</div>
@@ -559,9 +548,8 @@ export default class NotificationsTab extends React.Component {
type='checkbox'
checked={this.state.channelKey}
onChange={handleUpdateChannelKey}
- >
- {'Channel-wide mentions "@channel"'}
- </input>
+ />
+ {'Channel-wide mentions "@channel"'}
</label>
</div>
</div>
@@ -576,9 +564,8 @@ export default class NotificationsTab extends React.Component {
type='checkbox'
checked={this.state.customKeysChecked}
onChange={this.updateCustomMentionKeys}
- >
- {'Other non-case sensitive words, separated by commas:'}
- </input>
+ />
+ {'Other non-case sensitive words, separated by commas:'}
</label>
</div>
<input
diff --git a/web/static/images/webhook_icon.jpg b/web/static/images/webhook_icon.jpg
new file mode 100644
index 000000000..af5303421
--- /dev/null
+++ b/web/static/images/webhook_icon.jpg
Binary files differ
diff --git a/web/web.go b/web/web.go
index 7ab50a073..00e00b3b9 100644
--- a/web/web.go
+++ b/web/web.go
@@ -971,12 +971,20 @@ func incomingWebhook(c *api.Context, w http.ResponseWriter, r *http.Request) {
post := &model.Post{UserId: hook.UserId, ChannelId: channel.Id, Message: text}
post.AddProp("from_webhook", "true")
- if len(overrideUsername) != 0 && utils.Cfg.ServiceSettings.EnablePostUsernameOverride {
- post.AddProp("override_username", overrideUsername)
+ if utils.Cfg.ServiceSettings.EnablePostUsernameOverride {
+ if len(overrideUsername) != 0 {
+ post.AddProp("override_username", overrideUsername)
+ } else {
+ post.AddProp("override_username", model.DEFAULT_WEBHOOK_USERNAME)
+ }
}
- if len(overrideIconUrl) != 0 && utils.Cfg.ServiceSettings.EnablePostIconOverride {
- post.AddProp("override_icon_url", overrideIconUrl)
+ if utils.Cfg.ServiceSettings.EnablePostIconOverride {
+ if len(overrideIconUrl) != 0 {
+ post.AddProp("override_icon_url", overrideIconUrl)
+ } else {
+ post.AddProp("override_icon_url", model.DEFAULT_WEBHOOK_ICON)
+ }
}
if !c.HasPermissionsToChannel(pchan, "createIncomingHook") && channel.Type != model.CHANNEL_OPEN {