summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/post.go2
-rw-r--r--i18n/en.json24
-rw-r--r--model/command.go9
-rw-r--r--model/command_test.go20
-rw-r--r--model/incoming_webhook.go24
-rw-r--r--model/incoming_webhook_test.go20
-rw-r--r--model/outgoing_webhook.go10
-rw-r--r--model/outgoing_webhook_test.go20
-rw-r--r--store/sql_command_store.go2
-rw-r--r--store/sql_webhook_store.go9
-rw-r--r--webapp/components/backstage/add_incoming_webhook.jsx27
-rw-r--r--webapp/components/backstage/add_outgoing_webhook.jsx29
-rw-r--r--webapp/components/backstage/installed_incoming_webhook.jsx16
-rw-r--r--webapp/components/backstage/installed_outgoing_webhook.jsx18
-rw-r--r--webapp/components/post.jsx11
-rw-r--r--webapp/components/posts_view.jsx2
-rw-r--r--webapp/components/rhs_root_post.jsx13
-rw-r--r--webapp/components/root.jsx23
-rw-r--r--webapp/components/search_results_item.jsx35
-rw-r--r--webapp/components/signup_team_complete/components/signup_team_complete.jsx4
-rw-r--r--webapp/i18n/en.json1
-rw-r--r--webapp/root.jsx20
-rw-r--r--webapp/sass/routes/_backstage.scss7
-rw-r--r--webapp/utils/async_client.jsx8
-rw-r--r--webapp/utils/utils.jsx15
25 files changed, 275 insertions, 94 deletions
diff --git a/api/post.go b/api/post.go
index 2fe5feb8e..6f88c815b 100644
--- a/api/post.go
+++ b/api/post.go
@@ -544,7 +544,7 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
}
for _, userId := range userIds {
- if post.UserId == userId {
+ if post.UserId == userId && post.Props["from_webhook"] != "true" {
continue
}
sendEmail := true
diff --git a/i18n/en.json b/i18n/en.json
index 6292c1e03..0dca6ca04 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -2060,6 +2060,14 @@
"translation": "Create at must be a valid time"
},
{
+ "id": "model.command.is_valid.description.app_error",
+ "translation": "Invalid description"
+ },
+ {
+ "id": "model.command.is_valid.display_name.app_error",
+ "translation": "Invalid display name"
+ },
+ {
"id": "model.command.is_valid.id.app_error",
"translation": "Invalid Id"
},
@@ -2216,6 +2224,14 @@
"translation": "Create at must be a valid time"
},
{
+ "id": "model.incoming_hook.description.app_error",
+ "translation": "Invalid description"
+ },
+ {
+ "id": "model.incoming_hook.display_name.app_error",
+ "translation": "Invalid display name"
+ },
+ {
"id": "model.incoming_hook.id.app_error",
"translation": "Invalid Id"
},
@@ -2280,6 +2296,14 @@
"translation": "Create at must be a valid time"
},
{
+ "id": "model.outgoing_hook.is_valid.description.app_error",
+ "translation": "Invalid description"
+ },
+ {
+ "id": "model.outgoing_hook.is_valid.display_name.app_error",
+ "translation": "Invalid display name"
+ },
+ {
"id": "model.outgoing_hook.is_valid.id.app_error",
"translation": "Invalid Id"
},
diff --git a/model/command.go b/model/command.go
index 56d88f13c..ca3038122 100644
--- a/model/command.go
+++ b/model/command.go
@@ -29,6 +29,7 @@ type Command struct {
AutoCompleteDesc string `json:"auto_complete_desc"`
AutoCompleteHint string `json:"auto_complete_hint"`
DisplayName string `json:"display_name"`
+ Description string `json:"description"`
URL string `json:"url"`
}
@@ -114,6 +115,14 @@ func (o *Command) IsValid() *AppError {
return NewLocAppError("Command.IsValid", "model.command.is_valid.method.app_error", nil, "")
}
+ if len(o.DisplayName) > 64 {
+ return NewLocAppError("Command.IsValid", "model.command.is_valid.display_name.app_error", nil, "")
+ }
+
+ if len(o.Description) > 128 {
+ return NewLocAppError("Command.IsValid", "model.command.is_valid.description.app_error", nil, "")
+ }
+
return nil
}
diff --git a/model/command_test.go b/model/command_test.go
index 0581625d9..d362d8f2c 100644
--- a/model/command_test.go
+++ b/model/command_test.go
@@ -84,6 +84,26 @@ func TestCommandIsValid(t *testing.T) {
if err := o.IsValid(); err != nil {
t.Fatal(err)
}
+
+ o.DisplayName = strings.Repeat("1", 65)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.DisplayName = strings.Repeat("1", 64)
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
+
+ o.Description = strings.Repeat("1", 129)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.Description = strings.Repeat("1", 128)
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
}
func TestCommandPreSave(t *testing.T) {
diff --git a/model/incoming_webhook.go b/model/incoming_webhook.go
index 8432f5fea..0763b443e 100644
--- a/model/incoming_webhook.go
+++ b/model/incoming_webhook.go
@@ -14,13 +14,15 @@ const (
)
type IncomingWebhook struct {
- Id string `json:"id"`
- CreateAt int64 `json:"create_at"`
- UpdateAt int64 `json:"update_at"`
- DeleteAt int64 `json:"delete_at"`
- UserId string `json:"user_id"`
- ChannelId string `json:"channel_id"`
- TeamId string `json:"team_id"`
+ Id string `json:"id"`
+ CreateAt int64 `json:"create_at"`
+ UpdateAt int64 `json:"update_at"`
+ DeleteAt int64 `json:"delete_at"`
+ UserId string `json:"user_id"`
+ ChannelId string `json:"channel_id"`
+ TeamId string `json:"team_id"`
+ DisplayName string `json:"display_name"`
+ Description string `json:"description"`
}
type IncomingWebhookRequest struct {
@@ -99,6 +101,14 @@ func (o *IncomingWebhook) IsValid() *AppError {
return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.team_id.app_error", nil, "")
}
+ if len(o.DisplayName) > 64 {
+ return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.display_name.app_error", nil, "")
+ }
+
+ if len(o.Description) > 128 {
+ return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.description.app_error", nil, "")
+ }
+
return nil
}
diff --git a/model/incoming_webhook_test.go b/model/incoming_webhook_test.go
index 5297d7d90..3f1754244 100644
--- a/model/incoming_webhook_test.go
+++ b/model/incoming_webhook_test.go
@@ -69,6 +69,26 @@ func TestIncomingWebhookIsValid(t *testing.T) {
if err := o.IsValid(); err != nil {
t.Fatal(err)
}
+
+ o.DisplayName = strings.Repeat("1", 65)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.DisplayName = strings.Repeat("1", 64)
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
+
+ o.Description = strings.Repeat("1", 129)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.Description = strings.Repeat("1", 128)
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
}
func TestIncomingWebhookPreSave(t *testing.T) {
diff --git a/model/outgoing_webhook.go b/model/outgoing_webhook.go
index 70de4d26e..e13de9080 100644
--- a/model/outgoing_webhook.go
+++ b/model/outgoing_webhook.go
@@ -20,6 +20,8 @@ type OutgoingWebhook struct {
TeamId string `json:"team_id"`
TriggerWords StringArray `json:"trigger_words"`
CallbackURLs StringArray `json:"callback_urls"`
+ DisplayName string `json:"display_name"`
+ Description string `json:"description"`
}
func (o *OutgoingWebhook) ToJson() string {
@@ -106,6 +108,14 @@ func (o *OutgoingWebhook) IsValid() *AppError {
}
}
+ if len(o.DisplayName) > 64 {
+ return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.display_name.app_error", nil, "")
+ }
+
+ if len(o.Description) > 128 {
+ return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.description.app_error", nil, "")
+ }
+
return nil
}
diff --git a/model/outgoing_webhook_test.go b/model/outgoing_webhook_test.go
index 665b85b6f..72c842e03 100644
--- a/model/outgoing_webhook_test.go
+++ b/model/outgoing_webhook_test.go
@@ -89,6 +89,26 @@ func TestOutgoingWebhookIsValid(t *testing.T) {
if err := o.IsValid(); err != nil {
t.Fatal(err)
}
+
+ o.DisplayName = strings.Repeat("1", 65)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.DisplayName = strings.Repeat("1", 64)
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
+
+ o.Description = strings.Repeat("1", 129)
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
+ o.Description = strings.Repeat("1", 128)
+ if err := o.IsValid(); err != nil {
+ t.Fatal(err)
+ }
}
func TestOutgoingWebhookPreSave(t *testing.T) {
diff --git a/store/sql_command_store.go b/store/sql_command_store.go
index 074a6e588..02b3a0cc5 100644
--- a/store/sql_command_store.go
+++ b/store/sql_command_store.go
@@ -28,12 +28,14 @@ func NewSqlCommandStore(sqlStore *SqlStore) CommandStore {
tableo.ColMap("AutoCompleteDesc").SetMaxSize(1024)
tableo.ColMap("AutoCompleteHint").SetMaxSize(1024)
tableo.ColMap("DisplayName").SetMaxSize(64)
+ tableo.ColMap("Description").SetMaxSize(128)
}
return s
}
func (s SqlCommandStore) UpgradeSchemaIfNeeded() {
+ s.CreateColumnIfNotExists("Commands", "Description", "varchar(128)", "varchar(128)", "")
}
func (s SqlCommandStore) CreateIndexesIfNotExists() {
diff --git a/store/sql_webhook_store.go b/store/sql_webhook_store.go
index 740c9a33f..3c1f404e1 100644
--- a/store/sql_webhook_store.go
+++ b/store/sql_webhook_store.go
@@ -20,6 +20,8 @@ func NewSqlWebhookStore(sqlStore *SqlStore) WebhookStore {
table.ColMap("UserId").SetMaxSize(26)
table.ColMap("ChannelId").SetMaxSize(26)
table.ColMap("TeamId").SetMaxSize(26)
+ table.ColMap("DisplayName").SetMaxSize(64)
+ table.ColMap("Description").SetMaxSize(128)
tableo := db.AddTableWithName(model.OutgoingWebhook{}, "OutgoingWebhooks").SetKeys(false, "Id")
tableo.ColMap("Id").SetMaxSize(26)
@@ -29,12 +31,19 @@ func NewSqlWebhookStore(sqlStore *SqlStore) WebhookStore {
tableo.ColMap("TeamId").SetMaxSize(26)
tableo.ColMap("TriggerWords").SetMaxSize(1024)
tableo.ColMap("CallbackURLs").SetMaxSize(1024)
+ tableo.ColMap("DisplayName").SetMaxSize(64)
+ tableo.ColMap("Description").SetMaxSize(128)
}
return s
}
func (s SqlWebhookStore) UpgradeSchemaIfNeeded() {
+ s.CreateColumnIfNotExists("IncomingWebhooks", "DisplayName", "varchar(64)", "varchar(64)", "")
+ s.CreateColumnIfNotExists("IncomingWebhooks", "Description", "varchar(128)", "varchar(128)", "")
+
+ s.CreateColumnIfNotExists("OutgoingWebhooks", "DisplayName", "varchar(64)", "varchar(64)", "")
+ s.CreateColumnIfNotExists("OutgoingWebhooks", "Description", "varchar(128)", "varchar(128)", "")
}
func (s SqlWebhookStore) CreateIndexesIfNotExists() {
diff --git a/webapp/components/backstage/add_incoming_webhook.jsx b/webapp/components/backstage/add_incoming_webhook.jsx
index 83027c6b3..f3601cb07 100644
--- a/webapp/components/backstage/add_incoming_webhook.jsx
+++ b/webapp/components/backstage/add_incoming_webhook.jsx
@@ -18,12 +18,12 @@ export default class AddIncomingWebhook extends React.Component {
this.handleSubmit = this.handleSubmit.bind(this);
- this.updateName = this.updateName.bind(this);
+ this.updateDisplayName = this.updateDisplayName.bind(this);
this.updateDescription = this.updateDescription.bind(this);
this.updateChannelId = this.updateChannelId.bind(this);
this.state = {
- name: '',
+ displayName: '',
description: '',
channelId: '',
saving: false,
@@ -60,7 +60,9 @@ export default class AddIncomingWebhook extends React.Component {
}
const hook = {
- channel_id: this.state.channelId
+ channel_id: this.state.channelId,
+ display_name: this.state.displayName,
+ description: this.state.description
};
AsyncClient.addIncomingHook(
@@ -70,15 +72,16 @@ export default class AddIncomingWebhook extends React.Component {
},
(err) => {
this.setState({
+ saving: false,
serverError: err.message
});
}
);
}
- updateName(e) {
+ updateDisplayName(e) {
this.setState({
- name: e.target.value
+ displayName: e.target.value
});
}
@@ -112,20 +115,21 @@ export default class AddIncomingWebhook extends React.Component {
<div className='form-group'>
<label
className='control-label col-sm-3'
- htmlFor='name'
+ htmlFor='displayName'
>
<FormattedMessage
- id='add_incoming_webhook.name'
- defaultMessage='Name'
+ id='add_incoming_webhook.displayName'
+ defaultMessage='Display Name'
/>
</label>
<div className='col-md-5 col-sm-9'>
<input
- id='name'
+ id='displayName'
type='text'
+ maxLength='64'
className='form-control'
- value={this.state.name}
- onChange={this.updateName}
+ value={this.state.displayName}
+ onChange={this.updateDisplayName}
/>
</div>
</div>
@@ -143,6 +147,7 @@ export default class AddIncomingWebhook extends React.Component {
<input
id='description'
type='text'
+ maxLength='128'
className='form-control'
value={this.state.description}
onChange={this.updateDescription}
diff --git a/webapp/components/backstage/add_outgoing_webhook.jsx b/webapp/components/backstage/add_outgoing_webhook.jsx
index 5d98138df..ef57c6d05 100644
--- a/webapp/components/backstage/add_outgoing_webhook.jsx
+++ b/webapp/components/backstage/add_outgoing_webhook.jsx
@@ -18,14 +18,14 @@ export default class AddOutgoingWebhook extends React.Component {
this.handleSubmit = this.handleSubmit.bind(this);
- this.updateName = this.updateName.bind(this);
+ this.updateDisplayName = this.updateDisplayName.bind(this);
this.updateDescription = this.updateDescription.bind(this);
this.updateChannelId = this.updateChannelId.bind(this);
this.updateTriggerWords = this.updateTriggerWords.bind(this);
this.updateCallbackUrls = this.updateCallbackUrls.bind(this);
this.state = {
- name: '',
+ displayName: '',
description: '',
channelId: '',
triggerWords: '',
@@ -80,7 +80,9 @@ export default class AddOutgoingWebhook extends React.Component {
const hook = {
channel_id: this.state.channelId,
trigger_words: this.state.triggerWords.split('\n').map((word) => word.trim()),
- callback_urls: this.state.callbackUrls.split('\n').map((url) => url.trim())
+ callback_urls: this.state.callbackUrls.split('\n').map((url) => url.trim()),
+ display_name: this.state.displayName,
+ description: this.state.description
};
AsyncClient.addOutgoingHook(
@@ -90,15 +92,16 @@ export default class AddOutgoingWebhook extends React.Component {
},
(err) => {
this.setState({
+ saving: false,
serverError: err.message
});
}
);
}
- updateName(e) {
+ updateDisplayName(e) {
this.setState({
- name: e.target.value
+ displayName: e.target.value
});
}
@@ -144,20 +147,21 @@ export default class AddOutgoingWebhook extends React.Component {
<div className='form-group'>
<label
className='control-label col-sm-3'
- htmlFor='name'
+ htmlFor='displayName'
>
<FormattedMessage
- id='add_outgoing_webhook.name'
- defaultMessage='Name'
+ id='add_outgoing_webhook.displayName'
+ defaultMessage='Display Name'
/>
</label>
<div className='col-md-5 col-sm-9'>
<input
- id='name'
+ id='displayName'
type='text'
+ maxLength='64'
className='form-control'
- value={this.state.name}
- onChange={this.updateName}
+ value={this.state.displayName}
+ onChange={this.updateDisplayName}
/>
</div>
</div>
@@ -175,6 +179,7 @@ export default class AddOutgoingWebhook extends React.Component {
<input
id='description'
type='text'
+ maxLength='128'
className='form-control'
value={this.state.description}
onChange={this.updateDescription}
@@ -213,6 +218,7 @@ export default class AddOutgoingWebhook extends React.Component {
<textarea
id='triggerWords'
rows='3'
+ maxLength='1000'
className='form-control'
value={this.state.triggerWords}
onChange={this.updateTriggerWords}
@@ -233,6 +239,7 @@ export default class AddOutgoingWebhook extends React.Component {
<textarea
id='callbackUrls'
rows='3'
+ maxLength='1000'
className='form-control'
value={this.state.callbackUrls}
onChange={this.updateCallbackUrls}
diff --git a/webapp/components/backstage/installed_incoming_webhook.jsx b/webapp/components/backstage/installed_incoming_webhook.jsx
index f65cf6327..95a303edc 100644
--- a/webapp/components/backstage/installed_incoming_webhook.jsx
+++ b/webapp/components/backstage/installed_incoming_webhook.jsx
@@ -39,7 +39,7 @@ export default class InstalledIncomingWebhook extends React.Component {
<div className='item-details'>
<div className='item-details__row'>
<span className='item-details__name'>
- {channelName}
+ {incomingWebhook.display_name || channelName}
</span>
<span className='item-details__type'>
<FormattedMessage
@@ -50,7 +50,19 @@ export default class InstalledIncomingWebhook extends React.Component {
</div>
<div className='item-details__row'>
<span className='item-details__description'>
- {Utils.getWindowLocationOrigin() + '/hooks/' + incomingWebhook.id}
+ {incomingWebhook.description}
+ </span>
+ </div>
+ <div className='tem-details__row'>
+ <span className='item-details__creation'>
+ <FormattedMessage
+ id='installed_integrations.creation'
+ defaultMessage='Created by {creator} on {createAt, date, full}'
+ values={{
+ creator: Utils.displayUsername(incomingWebhook.user_id),
+ createAt: incomingWebhook.create_at
+ }}
+ />
</span>
</div>
</div>
diff --git a/webapp/components/backstage/installed_outgoing_webhook.jsx b/webapp/components/backstage/installed_outgoing_webhook.jsx
index fee427260..530474dc3 100644
--- a/webapp/components/backstage/installed_outgoing_webhook.jsx
+++ b/webapp/components/backstage/installed_outgoing_webhook.jsx
@@ -47,7 +47,7 @@ export default class InstalledOutgoingWebhook extends React.Component {
<div className='item-details'>
<div className='item-details__row'>
<span className='item-details__name'>
- {channelName}
+ {outgoingWebhook.display_name || channelName}
</span>
<span className='item-details__type'>
<FormattedMessage
@@ -58,9 +58,19 @@ export default class InstalledOutgoingWebhook extends React.Component {
</div>
<div className='item-details__row'>
<span className='item-details__description'>
- {Utils.getWindowLocationOrigin() + '/hooks/' + outgoingWebhook.id}
- {' - '}
- {outgoingWebhook.token}
+ {outgoingWebhook.description}
+ </span>
+ </div>
+ <div className='item-details__row'>
+ <span className='item-details__creation'>
+ <FormattedMessage
+ id='installed_integrations.creation'
+ defaultMessage='Created by {creator} on {createAt, date, full}'
+ values={{
+ creator: Utils.displayUsername(outgoingWebhook.creator_id),
+ createAt: outgoingWebhook.create_at
+ }}
+ />
</span>
</div>
</div>
diff --git a/webapp/components/post.jsx b/webapp/components/post.jsx
index bbf8d9bf6..7294cf163 100644
--- a/webapp/components/post.jsx
+++ b/webapp/components/post.jsx
@@ -185,18 +185,9 @@ export default class Post extends React.Component {
let profilePic = null;
if (!this.props.hideProfilePic) {
- let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp;
- if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
- if (post.props.override_icon_url) {
- src = post.props.override_icon_url;
- } else {
- src = Constants.DEFAULT_WEBHOOK_LOGO;
- }
- }
-
profilePic = (
<img
- src={src}
+ src={Utils.getProfilePicSrcForPost(post, timestamp)}
height='36'
width='36'
/>
diff --git a/webapp/components/posts_view.jsx b/webapp/components/posts_view.jsx
index ffe04daa1..aa7f445ce 100644
--- a/webapp/components/posts_view.jsx
+++ b/webapp/components/posts_view.jsx
@@ -204,10 +204,12 @@ export default class PostsView extends React.Component {
// the previous post was made by the same user as the current post,
// the previous post is not a comment,
// the current post is not a comment,
+ // the previous post is not from a webhook
// the current post is not from a webhook
if (prevPostUserId === postUserId &&
!prevPostIsComment &&
!postIsComment &&
+ !prevPostFromWebhook &&
!postFromWebhook) {
hideProfilePic = true;
}
diff --git a/webapp/components/rhs_root_post.jsx b/webapp/components/rhs_root_post.jsx
index 7a7c5f692..9a207e429 100644
--- a/webapp/components/rhs_root_post.jsx
+++ b/webapp/components/rhs_root_post.jsx
@@ -213,21 +213,10 @@ export default class RhsRootPost extends React.Component {
);
}
- let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp;
- if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
- if (post.props.override_icon_url) {
- src = post.props.override_icon_url;
- } else {
- src = Constants.DEFAULT_WEBHOOK_LOGO;
- }
- } else if (Utils.isSystemMessage(post)) {
- src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
- }
-
const profilePic = (
<img
className='post-profile-img'
- src={src}
+ src={Utils.getProfilePicSrcForPost(post, timestamp)}
height='36'
width='36'
/>
diff --git a/webapp/components/root.jsx b/webapp/components/root.jsx
index 3b85b23fd..59364d085 100644
--- a/webapp/components/root.jsx
+++ b/webapp/components/root.jsx
@@ -3,6 +3,7 @@
import $ from 'jquery';
import * as GlobalActions from 'action_creators/global_actions.jsx';
+import * as Client from 'utils/client.jsx';
import BrowserStore from 'stores/browser_store.jsx';
import LocalizationStore from 'stores/localization_store.jsx';
@@ -12,6 +13,8 @@ import React from 'react';
import FastClick from 'fastclick';
+import {browserHistory} from 'react-router';
+
export default class Root extends React.Component {
constructor(props) {
super(props);
@@ -21,10 +24,25 @@ export default class Root extends React.Component {
};
this.localizationChanged = this.localizationChanged.bind(this);
+ this.redirectIfNecessary = this.redirectIfNecessary.bind(this);
}
localizationChanged() {
this.setState({locale: LocalizationStore.getLocale(), translations: LocalizationStore.getTranslations()});
}
+ redirectIfNecessary(props) {
+ if (props.location.pathname === '/') {
+ Client.getMeLoggedIn((data) => {
+ if (!data || data.logged_in === 'false') {
+ browserHistory.push('/signup_team');
+ } else {
+ browserHistory.push('/' + data.team_name + '/channels/town-square');
+ }
+ });
+ }
+ }
+ componentWillReceiveProps(newProps) {
+ this.redirectIfNecessary(newProps);
+ }
componentWillMount() {
// Setup localization listener
LocalizationStore.addChangeListener(this.localizationChanged);
@@ -70,12 +88,15 @@ export default class Root extends React.Component {
// Get our localizaiton
GlobalActions.loadBrowserLocale();
+
+ // Redirect if Necessary
+ this.redirectIfNecessary(this.props);
}
componentWillUnmount() {
LocalizationStore.removeChangeListener(this.localizationChanged);
}
render() {
- if (this.state.translations == null) {
+ if (this.state.translations == null || this.props.children == null) {
return <div/>;
}
diff --git a/webapp/components/search_results_item.jsx b/webapp/components/search_results_item.jsx
index 75cbcb2a0..58f09c0e0 100644
--- a/webapp/components/search_results_item.jsx
+++ b/webapp/components/search_results_item.jsx
@@ -1,11 +1,13 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import UserStore from 'stores/user_store.jsx';
import UserProfile from './user_profile.jsx';
+
+import UserStore from 'stores/user_store.jsx';
+
import * as GlobalActions from 'action_creators/global_actions.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
-
+import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
import {FormattedMessage, FormattedDate} from 'react-intl';
@@ -29,6 +31,7 @@ export default class SearchResultsItem extends React.Component {
const channel = this.props.channel;
const timestamp = UserStore.getCurrentUser().update_at;
const user = this.props.user || {};
+ const post = this.props.post;
if (channel) {
channelName = channel.display_name;
@@ -47,13 +50,23 @@ export default class SearchResultsItem extends React.Component {
mentionHighlight: this.props.isMentionSearch
};
+ let overrideUsername;
+ let disableProfilePopover = false;
+ if (post.props &&
+ post.props.from_webhook &&
+ post.props.override_username &&
+ global.window.mm_config.EnablePostUsernameOverride === 'true') {
+ overrideUsername = post.props.override_username;
+ disableProfilePopover = true;
+ }
+
return (
<div className='search-item__container'>
<div className='date-separator'>
<hr className='separator__hr'/>
<div className='separator__text'>
<FormattedDate
- value={this.props.post.create_at}
+ value={post.create_at}
day='numeric'
month='long'
year='numeric'
@@ -67,18 +80,24 @@ export default class SearchResultsItem extends React.Component {
<div className='post__content'>
<div className='post__img'>
<img
- src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp}
+ src={Utils.getProfilePicSrcForPost(post, timestamp)}
height='36'
width='36'
/>
</div>
<div>
<ul className='post__header'>
- <li className='col__name'><strong><UserProfile user={user}/></strong></li>
+ <li className='col__name'><strong>
+ <UserProfile
+ user={user}
+ overwriteName={overrideUsername}
+ disablePopover={disableProfilePopover}
+ />
+ </strong></li>
<li className='col'>
<time className='search-item-time'>
<FormattedDate
- value={this.props.post.create_at}
+ value={post.create_at}
hour12={true}
hour='2-digit'
minute='2-digit'
@@ -87,7 +106,7 @@ export default class SearchResultsItem extends React.Component {
</li>
<li>
<Link
- to={'/' + window.location.pathname.split('/')[1] + '/pl/' + this.props.post.id}
+ to={'/' + window.location.pathname.split('/')[1] + '/pl/' + post.id}
className='search-item__jump'
>
<FormattedMessage
@@ -112,7 +131,7 @@ export default class SearchResultsItem extends React.Component {
<div className='search-item-snippet'>
<span
onClick={TextFormatting.handleClick}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, formattingOptions)}}
+ dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message, formattingOptions)}}
/>
</div>
</div>
diff --git a/webapp/components/signup_team_complete/components/signup_team_complete.jsx b/webapp/components/signup_team_complete/components/signup_team_complete.jsx
index e5d310151..95b41dbde 100644
--- a/webapp/components/signup_team_complete/components/signup_team_complete.jsx
+++ b/webapp/components/signup_team_complete/components/signup_team_complete.jsx
@@ -5,10 +5,8 @@ import BrowserStore from 'stores/browser_store.jsx';
import {FormattedMessage} from 'react-intl';
-import {browserHistory} from 'react-router';
-
import React from 'react';
-import {Link} from 'react-router';
+import {Link, browserHistory} from 'react-router';
export default class SignupTeamComplete extends React.Component {
constructor(props) {
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index 7dc6486ab..8fdb42b4c 100644
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -814,6 +814,7 @@
"get_team_invite_link_modal.title": "Team Invite Link",
"installed_integrations.add": "Add Integration",
"installed_integrations.allFilter": "All ({count})",
+ "installed_integrations.creation": "Created by {creator} on {createAt, date, full}",
"installed_integrations.delete": "Delete",
"installed_integrations.header": "Installed Integrations",
"installed_integrations.incomingWebhookType": "(Incoming Webhook)",
diff --git a/webapp/root.jsx b/webapp/root.jsx
index 10cc63643..c88b0a7b3 100644
--- a/webapp/root.jsx
+++ b/webapp/root.jsx
@@ -158,23 +158,6 @@ function preLoggedIn(nextState, replace, callback) {
});
}
-function onRootEnter(nextState, replace, callback) {
- if (nextState.location.pathname === '/') {
- Client.getMeLoggedIn((data) => {
- if (!data || data.logged_in === 'false') {
- replace({pathname: '/signup_team'});
- callback();
- } else {
- replace({pathname: '/' + data.team_name + '/channels/town-square'});
- callback();
- }
- });
- return;
- }
-
- callback();
-}
-
function onPermalinkEnter(nextState) {
const postId = nextState.params.postid;
@@ -231,7 +214,6 @@ function renderRootComponent() {
<Route
path='/'
component={Root}
- onEnter={onRootEnter}
>
<Route
path='error'
@@ -338,7 +320,7 @@ function renderRootComponent() {
component={TeamURLPage}
/>
<Route
- path='invites'
+ path='send_invites'
component={SendInivtesPage}
/>
<Route
diff --git a/webapp/sass/routes/_backstage.scss b/webapp/sass/routes/_backstage.scss
index 729c8c912..13c51c8b4 100644
--- a/webapp/sass/routes/_backstage.scss
+++ b/webapp/sass/routes/_backstage.scss
@@ -196,7 +196,12 @@
margin-bottom: 1em;
}
- .list-item__actions {
+ .item-details__creation {
+ color: $dark-gray;
+ margin-bottom: 1em;
+ }
+
+ .item-actions {
flex-grow: 0;
flex-shrink: 0;
padding-left: 20px;
diff --git a/webapp/utils/async_client.jsx b/webapp/utils/async_client.jsx
index cc19baa7e..db0b2258c 100644
--- a/webapp/utils/async_client.jsx
+++ b/webapp/utils/async_client.jsx
@@ -1182,10 +1182,10 @@ export function addIncomingHook(hook, success, error) {
}
},
(err) => {
- dispatchError(err, 'addIncomingHook');
-
if (error) {
error(err);
+ } else {
+ dispatchError(err, 'addIncomingHook');
}
}
);
@@ -1205,10 +1205,10 @@ export function addOutgoingHook(hook, success, error) {
}
},
(err) => {
- dispatchError(err, 'addOutgoingHook');
-
if (error) {
error(err);
+ } else {
+ dispatchError(err, 'addOutgoingHook');
}
}
);
diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx
index 35c347f9f..399dd0985 100644
--- a/webapp/utils/utils.jsx
+++ b/webapp/utils/utils.jsx
@@ -1396,3 +1396,18 @@ export function localizeMessage(id, defaultMessage) {
return id;
}
+
+export function getProfilePicSrcForPost(post, timestamp) {
+ let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp;
+ if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
+ if (post.props.override_icon_url) {
+ src = post.props.override_icon_url;
+ } else {
+ src = Constants.DEFAULT_WEBHOOK_LOGO;
+ }
+ } else if (isSystemMessage(post)) {
+ src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
+ }
+
+ return src;
+}