Этот коммит содержится в:
=Corey Hulen
2015-09-22 13:18:42 -07:00
родитель e22e7b8b7b ac7918c554
Коммит 6e60768abe
31 изменённых файлов: 1285 добавлений и 102 удалений

Просмотреть файл

@@ -0,0 +1,177 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var Client = require('../../utils/client.jsx');
var Utils = require('../../utils/utils.jsx');
var Constants = require('../../utils/constants.jsx');
var ChannelStore = require('../../stores/channel_store.jsx');
var LoadingScreen = require('../loading_screen.jsx');
export default class ManageIncomingHooks extends React.Component {
constructor() {
super();
this.getHooks = this.getHooks.bind(this);
this.addNewHook = this.addNewHook.bind(this);
this.updateChannelId = this.updateChannelId.bind(this);
this.state = {hooks: [], channelId: ChannelStore.getByName(Constants.DEFAULT_CHANNEL).id, getHooksComplete: false};
}
componentDidMount() {
this.getHooks();
}
addNewHook() {
let hook = {}; //eslint-disable-line prefer-const
hook.channel_id = this.state.channelId;
Client.addIncomingHook(
hook,
(data) => {
let hooks = this.state.hooks;
if (!hooks) {
hooks = [];
}
hooks.push(data);
this.setState({hooks});
},
(err) => {
this.setState({serverError: err});
}
);
}
removeHook(id) {
let data = {}; //eslint-disable-line prefer-const
data.id = id;
Client.deleteIncomingHook(
data,
() => {
let hooks = this.state.hooks; //eslint-disable-line prefer-const
let index = -1;
for (let i = 0; i < hooks.length; i++) {
if (hooks[i].id === id) {
index = i;
break;
}
}
if (index !== -1) {
hooks.splice(index, 1);
}
this.setState({hooks});
},
(err) => {
this.setState({serverError: err});
}
);
}
getHooks() {
Client.listIncomingHooks(
(data) => {
let state = this.state; //eslint-disable-line prefer-const
if (data) {
state.hooks = data;
}
state.getHooksComplete = true;
this.setState(state);
},
(err) => {
this.setState({serverError: err});
}
);
}
updateChannelId(e) {
this.setState({channelId: e.target.value});
}
render() {
let serverError;
if (this.state.serverError) {
serverError = <label className='has-error'>{this.state.serverError}</label>;
}
const channels = ChannelStore.getAll();
let options = []; //eslint-disable-line prefer-const
channels.forEach((channel) => {
options.push(<option value={channel.id}>{channel.name}</option>);
});
let disableButton = '';
if (this.state.channelId === '') {
disableButton = ' disable';
}
let hooks = []; //eslint-disable-line prefer-const
this.state.hooks.forEach((hook) => {
const c = ChannelStore.get(hook.channel_id);
hooks.push(
<div>
<div className='divider-light'></div>
<span>
<strong>{'URL: '}</strong>{Utils.getWindowLocationOrigin() + '/hooks/' + hook.id}
</span>
<br/>
<span>
<strong>{'Channel: '}</strong>{c.name}
</span>
<br/>
<a
className={'btn btn-sm btn-primary'}
href='#'
onClick={this.removeHook.bind(this, hook.id)}
>
{'Remove'}
</a>
</div>
);
});
let displayHooks;
if (!this.state.getHooksComplete) {
displayHooks = <LoadingScreen/>;
} else if (hooks.length > 0) {
displayHooks = hooks;
} else {
displayHooks = <label>{'None'}</label>;
}
const existingHooks = (
<div>
<label className='control-label'>{'Existing incoming webhooks'}</label>
<br/>
{displayHooks}
</div>
);
return (
<div
key='addIncomingHook'
className='form-group'
>
<label className='control-label'>{'Add a new incoming webhook'}</label>
<br/>
<div>
<select
ref='channelName'
value={this.state.channelId}
onChange={this.updateChannelId}
>
{options}
</select>
<br/>
{serverError}
<a
className={'btn btn-sm btn-primary' + disableButton}
href='#'
onClick={this.addNewHook}
>
{'Add'}
</a>
</div>
{existingHooks}
</div>
);
}
}

Просмотреть файл

@@ -1,13 +1,14 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var UserStore = require('../stores/user_store.jsx');
var utils = require('../utils/utils.jsx');
var UserStore = require('../../stores/user_store.jsx');
var utils = require('../../utils/utils.jsx');
var NotificationsTab = require('./user_settings_notifications.jsx');
var SecurityTab = require('./user_settings_security.jsx');
var GeneralTab = require('./user_settings_general.jsx');
var AppearanceTab = require('./user_settings_appearance.jsx');
var DeveloperTab = require('./user_settings_developer.jsx');
var IntegrationsTab = require('./user_settings_integrations.jsx');
export default class UserSettings extends React.Component {
constructor(props) {
@@ -86,6 +87,17 @@ export default class UserSettings extends React.Component {
/>
</div>
);
} else if (this.props.activeTab === 'integrations') {
return (
<div>
<IntegrationsTab
user={this.state.user}
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
updateTab={this.props.updateTab}
/>
</div>
);
}
return <div/>;

Просмотреть файл

@@ -1,11 +1,11 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var UserStore = require('../stores/user_store.jsx');
var SettingItemMin = require('./setting_item_min.jsx');
var SettingItemMax = require('./setting_item_max.jsx');
var Client = require('../utils/client.jsx');
var Utils = require('../utils/utils.jsx');
var UserStore = require('../../stores/user_store.jsx');
var SettingItemMin = require('../setting_item_min.jsx');
var SettingItemMax = require('../setting_item_max.jsx');
var Client = require('../../utils/client.jsx');
var Utils = require('../../utils/utils.jsx');
var ThemeColors = ['#2389d7', '#008a17', '#dc4fad', '#ac193d', '#0072c6', '#d24726', '#ff8f32', '#82ba00', '#03b3b2', '#008299', '#4617b4', '#8c0095', '#004b8b', '#004b8b', '#570000', '#380000', '#585858', '#000000'];

Просмотреть файл

@@ -1,8 +1,8 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var SettingItemMin = require('./setting_item_min.jsx');
var SettingItemMax = require('./setting_item_max.jsx');
var SettingItemMin = require('../setting_item_min.jsx');
var SettingItemMax = require('../setting_item_max.jsx');
export default class DeveloperTab extends React.Component {
constructor(props) {

Просмотреть файл

@@ -1,13 +1,13 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var UserStore = require('../stores/user_store.jsx');
var SettingItemMin = require('./setting_item_min.jsx');
var SettingItemMax = require('./setting_item_max.jsx');
var SettingPicture = require('./setting_picture.jsx');
var client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var utils = require('../utils/utils.jsx');
var UserStore = require('../../stores/user_store.jsx');
var SettingItemMin = require('../setting_item_min.jsx');
var SettingItemMax = require('../setting_item_max.jsx');
var SettingPicture = require('../setting_picture.jsx');
var client = require('../../utils/client.jsx');
var AsyncClient = require('../../utils/async_client.jsx');
var utils = require('../../utils/utils.jsx');
var assign = require('object-assign');
export default class UserSettingsGeneralTab extends React.Component {

Просмотреть файл

@@ -0,0 +1,95 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var SettingItemMin = require('../setting_item_min.jsx');
var SettingItemMax = require('../setting_item_max.jsx');
var ManageIncomingHooks = require('./manage_incoming_hooks.jsx');
export default class UserSettingsIntegrationsTab extends React.Component {
constructor(props) {
super(props);
this.updateSection = this.updateSection.bind(this);
this.handleClose = this.handleClose.bind(this);
this.state = {};
}
updateSection(section) {
this.props.updateSection(section);
}
handleClose() {
this.updateSection('');
}
componentDidMount() {
$('#user_settings').on('hidden.bs.modal', this.handleClose);
}
componentWillUnmount() {
$('#user_settings').off('hidden.bs.modal', this.handleClose);
}
render() {
let incomingHooksSection;
var inputs = [];
if (this.props.activeSection === 'incoming-hooks') {
inputs.push(
<ManageIncomingHooks />
);
incomingHooksSection = (
<SettingItemMax
title='Incoming Webhooks'
inputs={inputs}
updateSection={function clearSection(e) {
this.updateSection('');
e.preventDefault();
}.bind(this)}
/>
);
} else {
incomingHooksSection = (
<SettingItemMin
title='Incoming Webhooks'
describe='Manage your incoming webhooks'
updateSection={function updateNameSection() {
this.updateSection('incoming-hooks');
}.bind(this)}
/>
);
}
return (
<div>
<div className='modal-header'>
<button
type='button'
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>
{'Integration Settings'}
</h4>
</div>
<div className='user-settings'>
<h3 className='tab-header'>{'Integration Settings'}</h3>
<div className='divider-dark first'/>
{incomingHooksSection}
<div className='divider-dark'/>
</div>
</div>
);
}
}
UserSettingsIntegrationsTab.propTypes = {
user: React.PropTypes.object,
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func,
activeSection: React.PropTypes.string
};

Просмотреть файл

@@ -1,7 +1,7 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var SettingsSidebar = require('./settings_sidebar.jsx');
var SettingsSidebar = require('../settings_sidebar.jsx');
var UserSettings = require('./user_settings.jsx');
export default class UserSettingsModal extends React.Component {
@@ -38,6 +38,9 @@ export default class UserSettingsModal extends React.Component {
if (global.window.config.EnableOAuthServiceProvider === 'true') {
tabs.push({name: 'developer', uiName: 'Developer', icon: 'glyphicon glyphicon-th'});
}
if (global.window.config.EnableIncomingWebhooks === 'true') {
tabs.push({name: 'integrations', uiName: 'Integrations', icon: 'glyphicon glyphicon-transfer'});
}
return (
<div

Просмотреть файл

@@ -1,12 +1,12 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var UserStore = require('../stores/user_store.jsx');
var SettingItemMin = require('./setting_item_min.jsx');
var SettingItemMax = require('./setting_item_max.jsx');
var client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var utils = require('../utils/utils.jsx');
var UserStore = require('../../stores/user_store.jsx');
var SettingItemMin = require('../setting_item_min.jsx');
var SettingItemMax = require('../setting_item_max.jsx');
var client = require('../../utils/client.jsx');
var AsyncClient = require('../../utils/async_client.jsx');
var utils = require('../../utils/utils.jsx');
var assign = require('object-assign');
function getNotificationsStateFromStores() {

Просмотреть файл

@@ -1,11 +1,11 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var SettingItemMin = require('./setting_item_min.jsx');
var SettingItemMax = require('./setting_item_max.jsx');
var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var Constants = require('../utils/constants.jsx');
var SettingItemMin = require('../setting_item_min.jsx');
var SettingItemMax = require('../setting_item_max.jsx');
var Client = require('../../utils/client.jsx');
var AsyncClient = require('../../utils/async_client.jsx');
var Constants = require('../../utils/constants.jsx');
export default class SecurityTab extends React.Component {
constructor(props) {

Просмотреть файл

@@ -19,7 +19,7 @@ var DeletePostModal = require('../components/delete_post_modal.jsx');
var MoreChannelsModal = require('../components/more_channels.jsx');
var PostDeletedModal = require('../components/post_deleted_modal.jsx');
var ChannelNotificationsModal = require('../components/channel_notifications.jsx');
var UserSettingsModal = require('../components/user_settings_modal.jsx');
var UserSettingsModal = require('../components/user_settings/user_settings_modal.jsx');
var TeamSettingsModal = require('../components/team_settings_modal.jsx');
var ChannelMembersModal = require('../components/channel_members.jsx');
var ChannelInviteModal = require('../components/channel_invite_modal.jsx');

Просмотреть файл

@@ -57,7 +57,7 @@ export function createTeamFromSignup(teamSignup, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(teamSignup),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('createTeamFromSignup', xhr, status, err);
error(e);
@@ -72,7 +72,7 @@ export function createTeamWithSSO(team, service, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(team),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('createTeamWithSSO', xhr, status, err);
error(e);
@@ -87,7 +87,7 @@ export function createUser(user, data, emailHash, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(user),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('createUser', xhr, status, err);
error(e);
@@ -104,7 +104,7 @@ export function updateUser(user, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(user),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('updateUser', xhr, status, err);
error(e);
@@ -121,7 +121,7 @@ export function updatePassword(data, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('newPassword', xhr, status, err);
error(e);
@@ -138,7 +138,7 @@ export function updateUserNotifyProps(data, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('updateUserNotifyProps', xhr, status, err);
error(e);
@@ -153,7 +153,7 @@ export function updateRoles(data, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('updateRoles', xhr, status, err);
error(e);
@@ -174,7 +174,7 @@ export function updateActive(userId, active, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('updateActive', xhr, status, err);
error(e);
@@ -191,7 +191,7 @@ export function sendPasswordReset(data, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('sendPasswordReset', xhr, status, err);
error(e);
@@ -208,7 +208,7 @@ export function resetPassword(data, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('resetPassword', xhr, status, err);
error(e);
@@ -252,7 +252,7 @@ export function revokeSession(altId, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify({id: altId}),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('revokeSession', xhr, status, err);
error(e);
@@ -267,7 +267,7 @@ export function getSessions(userId, success, error) {
dataType: 'json',
contentType: 'application/json',
type: 'GET',
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('getSessions', xhr, status, err);
error(e);
@@ -281,7 +281,7 @@ export function getAudits(userId, success, error) {
dataType: 'json',
contentType: 'application/json',
type: 'GET',
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('getAudits', xhr, status, err);
error(e);
@@ -380,7 +380,7 @@ export function inviteMembers(data, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('inviteMembers', xhr, status, err);
error(e);
@@ -397,7 +397,7 @@ export function updateTeamDisplayName(data, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('updateTeamDisplayName', xhr, status, err);
error(e);
@@ -414,7 +414,7 @@ export function signupTeam(email, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify({email: email}),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('singupTeam', xhr, status, err);
error(e);
@@ -431,7 +431,7 @@ export function createTeam(team, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(team),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('createTeam', xhr, status, err);
error(e);
@@ -446,7 +446,7 @@ export function findTeamByName(teamName, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify({name: teamName}),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('findTeamByName', xhr, status, err);
error(e);
@@ -461,7 +461,7 @@ export function findTeamsSendEmail(email, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify({email: email}),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('findTeamsSendEmail', xhr, status, err);
error(e);
@@ -478,7 +478,7 @@ export function findTeams(email, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify({email: email}),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('findTeams', xhr, status, err);
error(e);
@@ -493,7 +493,7 @@ export function createChannel(channel, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(channel),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('createChannel', xhr, status, err);
error(e);
@@ -510,7 +510,7 @@ export function createDirectChannel(channel, userId, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify({user_id: userId}),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('createDirectChannel', xhr, status, err);
error(e);
@@ -527,7 +527,7 @@ export function updateChannel(channel, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(channel),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('updateChannel', xhr, status, err);
error(e);
@@ -544,7 +544,7 @@ export function updateChannelDesc(data, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('updateChannelDesc', xhr, status, err);
error(e);
@@ -561,7 +561,7 @@ export function updateNotifyLevel(data, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('updateNotifyLevel', xhr, status, err);
error(e);
@@ -575,7 +575,7 @@ export function joinChannel(id, success, error) {
dataType: 'json',
contentType: 'application/json',
type: 'POST',
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('joinChannel', xhr, status, err);
error(e);
@@ -591,7 +591,7 @@ export function leaveChannel(id, success, error) {
dataType: 'json',
contentType: 'application/json',
type: 'POST',
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('leaveChannel', xhr, status, err);
error(e);
@@ -607,7 +607,7 @@ export function deleteChannel(id, success, error) {
dataType: 'json',
contentType: 'application/json',
type: 'POST',
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('deleteChannel', xhr, status, err);
error(e);
@@ -623,7 +623,7 @@ export function updateLastViewedAt(channelId, success, error) {
dataType: 'json',
contentType: 'application/json',
type: 'POST',
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('updateLastViewedAt', xhr, status, err);
error(e);
@@ -637,7 +637,7 @@ export function getChannels(success, error) {
url: '/api/v1/channels/',
dataType: 'json',
type: 'GET',
success: success,
success,
ifModified: true,
error: function onError(xhr, status, err) {
var e = handleError('getChannels', xhr, status, err);
@@ -652,7 +652,7 @@ export function getChannel(id, success, error) {
url: '/api/v1/channels/' + id + '/',
dataType: 'json',
type: 'GET',
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('getChannel', xhr, status, err);
error(e);
@@ -667,7 +667,7 @@ export function getMoreChannels(success, error) {
url: '/api/v1/channels/more',
dataType: 'json',
type: 'GET',
success: success,
success,
ifModified: true,
error: function onError(xhr, status, err) {
var e = handleError('getMoreChannels', xhr, status, err);
@@ -682,7 +682,7 @@ export function getChannelCounts(success, error) {
url: '/api/v1/channels/counts',
dataType: 'json',
type: 'GET',
success: success,
success,
ifModified: true,
error: function onError(xhr, status, err) {
var e = handleError('getChannelCounts', xhr, status, err);
@@ -696,7 +696,7 @@ export function getChannelExtraInfo(id, success, error) {
url: '/api/v1/channels/' + id + '/extra_info',
dataType: 'json',
type: 'GET',
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('getChannelExtraInfo', xhr, status, err);
error(e);
@@ -711,7 +711,7 @@ export function executeCommand(channelId, command, suggest, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify({channelId: channelId, command: command, suggest: '' + suggest}),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('executeCommand', xhr, status, err);
error(e);
@@ -726,7 +726,7 @@ export function getPostsPage(channelId, offset, limit, success, error, complete)
dataType: 'json',
type: 'GET',
ifModified: true,
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('getPosts', xhr, status, err);
error(e);
@@ -741,7 +741,7 @@ export function getPosts(channelId, since, success, error, complete) {
dataType: 'json',
type: 'GET',
ifModified: true,
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('getPosts', xhr, status, err);
error(e);
@@ -757,7 +757,7 @@ export function getPost(channelId, postId, success, error) {
dataType: 'json',
type: 'GET',
ifModified: false,
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('getPost', xhr, status, err);
error(e);
@@ -771,7 +771,7 @@ export function search(terms, success, error) {
dataType: 'json',
type: 'GET',
data: {terms: terms},
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('search', xhr, status, err);
error(e);
@@ -787,7 +787,7 @@ export function deletePost(channelId, id, success, error) {
dataType: 'json',
contentType: 'application/json',
type: 'POST',
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('deletePost', xhr, status, err);
error(e);
@@ -804,7 +804,7 @@ export function createPost(post, channel, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(post),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('createPost', xhr, status, err);
error(e);
@@ -830,7 +830,7 @@ export function updatePost(post, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(post),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('updatePost', xhr, status, err);
error(e);
@@ -847,7 +847,7 @@ export function addChannelMember(id, data, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('addChannelMember', xhr, status, err);
error(e);
@@ -864,7 +864,7 @@ export function removeChannelMember(id, data, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('removeChannelMember', xhr, status, err);
error(e);
@@ -881,7 +881,7 @@ export function getProfiles(success, error) {
dataType: 'json',
contentType: 'application/json',
type: 'GET',
success: success,
success,
ifModified: true,
error: function onError(xhr, status, err) {
var e = handleError('getProfiles', xhr, status, err);
@@ -898,7 +898,7 @@ export function uploadFile(formData, success, error) {
cache: false,
contentType: false,
processData: false,
success: success,
success,
error: function onError(xhr, status, err) {
if (err !== 'abort') {
var e = handleError('uploadFile', xhr, status, err);
@@ -918,7 +918,7 @@ export function getFileInfo(filename, success, error) {
dataType: 'json',
contentType: 'application/json',
type: 'GET',
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('getFileInfo', xhr, status, err);
error(e);
@@ -932,7 +932,7 @@ export function getPublicLink(data, success, error) {
dataType: 'json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('getPublicLink', xhr, status, err);
error(e);
@@ -948,7 +948,7 @@ export function uploadProfileImage(imageData, success, error) {
cache: false,
contentType: false,
processData: false,
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('uploadProfileImage', xhr, status, err);
error(e);
@@ -964,7 +964,7 @@ export function importSlack(fileData, success, error) {
cache: false,
contentType: false,
processData: false,
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('importTeam', xhr, status, err);
error(e);
@@ -977,7 +977,7 @@ export function exportTeam(success, error) {
url: '/api/v1/teams/export_team',
type: 'GET',
dataType: 'json',
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('exportTeam', xhr, status, err);
error(e);
@@ -991,7 +991,7 @@ export function getStatuses(success, error) {
dataType: 'json',
contentType: 'application/json',
type: 'GET',
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('getStatuses', xhr, status, err);
error(e);
@@ -1004,7 +1004,7 @@ export function getMyTeam(success, error) {
url: '/api/v1/teams/me',
dataType: 'json',
type: 'GET',
success: success,
success,
ifModified: true,
error: function onError(xhr, status, err) {
var e = handleError('getMyTeam', xhr, status, err);
@@ -1020,7 +1020,7 @@ export function updateValetFeature(data, success, error) {
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success: success,
success,
error: function onError(xhr, status, err) {
var e = handleError('updateValetFeature', xhr, status, err);
error(e);
@@ -1053,7 +1053,7 @@ export function allowOAuth2(responseType, clientId, redirectUri, state, scope, s
dataType: 'json',
contentType: 'application/json',
type: 'GET',
success: success,
success,
error: (xhr, status, err) => {
const e = handleError('allowOAuth2', xhr, status, err);
error(e);
@@ -1062,3 +1062,46 @@ export function allowOAuth2(responseType, clientId, redirectUri, state, scope, s
module.exports.track('api', 'api_users_allow_oauth2');
}
export function addIncomingHook(hook, success, error) {
$.ajax({
url: '/api/v1/hooks/incoming/create',
dataType: 'json',
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(hook),
success,
error: (xhr, status, err) => {
var e = handleError('addIncomingHook', xhr, status, err);
error(e);
}
});
}
export function deleteIncomingHook(data, success, error) {
$.ajax({
url: '/api/v1/hooks/incoming/delete',
dataType: 'json',
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success,
error: (xhr, status, err) => {
var e = handleError('deleteIncomingHook', xhr, status, err);
error(e);
}
});
}
export function listIncomingHooks(success, error) {
$.ajax({
url: '/api/v1/hooks/incoming/list',
dataType: 'json',
type: 'GET',
success,
error: (xhr, status, err) => {
var e = handleError('listIncomingHooks', xhr, status, err);
error(e);
}
});
}

Просмотреть файл

@@ -9,11 +9,13 @@ import (
"github.com/gorilla/mux"
"github.com/mattermost/platform/api"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils"
"github.com/mssola/user_agent"
"gopkg.in/fsnotify.v1"
"html/template"
"net/http"
"regexp"
"strconv"
"strings"
)
@@ -63,6 +65,8 @@ func InitWeb() {
mainrouter.Handle("/admin_console", api.UserRequired(adminConsole)).Methods("GET")
mainrouter.Handle("/hooks/{id:[A-Za-z0-9]+}", api.ApiAppHandler(incomingWebhook)).Methods("POST")
// ----------------------------------------------------------------------------------------------
// *ANYTHING* team specific should go below this line
// ----------------------------------------------------------------------------------------------
@@ -834,3 +838,84 @@ func getAccessToken(c *api.Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(accessRsp.ToJson()))
}
func incomingWebhook(c *api.Context, w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id := params["id"]
hchan := api.Srv.Store.Webhook().GetIncoming(id)
r.ParseForm()
props := model.MapFromJson(strings.NewReader(r.FormValue("payload")))
text := props["text"]
if len(text) == 0 {
c.Err = model.NewAppError("incomingWebhook", "No text specified", "")
return
}
channelName := props["channel"]
var hook *model.IncomingWebhook
if result := <-hchan; result.Err != nil {
c.Err = model.NewAppError("incomingWebhook", "Invalid webhook", "err="+result.Err.Message)
return
} else {
hook = result.Data.(*model.IncomingWebhook)
}
var channel *model.Channel
var cchan store.StoreChannel
if len(channelName) != 0 {
if channelName[0] == '@' {
if result := <-api.Srv.Store.User().GetByUsername(hook.TeamId, channelName[1:]); result.Err != nil {
c.Err = model.NewAppError("incomingWebhook", "Couldn't find the user", "err="+result.Err.Message)
return
} else {
channelName = model.GetDMNameFromIds(result.Data.(*model.User).Id, hook.UserId)
}
} else if channelName[0] == '#' {
channelName = channelName[1:]
}
cchan = api.Srv.Store.Channel().GetByName(hook.TeamId, channelName)
} else {
cchan = api.Srv.Store.Channel().Get(hook.ChannelId)
}
// parse links into Markdown format
linkWithTextRegex := regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`)
text = linkWithTextRegex.ReplaceAllString(text, "[${2}](${1})")
linkRegex := regexp.MustCompile(`<\s*(\S*)\s*>`)
text = linkRegex.ReplaceAllString(text, "${1}")
if result := <-cchan; result.Err != nil {
c.Err = model.NewAppError("incomingWebhook", "Couldn't find the channel", "err="+result.Err.Message)
return
} else {
channel = result.Data.(*model.Channel)
}
pchan := api.Srv.Store.Channel().CheckPermissionsTo(hook.TeamId, channel.Id, hook.UserId)
post := &model.Post{UserId: hook.UserId, ChannelId: channel.Id, Message: text}
if !c.HasPermissionsToChannel(pchan, "createIncomingHook") && channel.Type != model.CHANNEL_OPEN {
c.Err = model.NewAppError("incomingWebhook", "Inappropriate channel permissions", "")
return
}
// create a mock session
c.Session = model.Session{UserId: hook.UserId, TeamId: hook.TeamId, IsOAuth: false}
if _, err := api.CreatePost(c, post, false); err != nil {
c.Err = model.NewAppError("incomingWebhook", "Error creating post", "err="+err.Message)
return
}
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte("ok"))
}

Просмотреть файл

@@ -180,6 +180,51 @@ func TestGetAccessToken(t *testing.T) {
}
}
func TestIncomingWebhook(t *testing.T) {
Setup()
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = ApiClient.Must(ApiClient.CreateTeam(team)).Data.(*model.Team)
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user = ApiClient.Must(ApiClient.CreateUser(user, "")).Data.(*model.User)
store.Must(api.Srv.Store.User().VerifyEmail(user.Id))
ApiClient.LoginByEmail(team.Name, user.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = ApiClient.Must(ApiClient.CreateChannel(channel1)).Data.(*model.Channel)
if utils.Cfg.ServiceSettings.EnableIncomingWebhooks {
hook1 := &model.IncomingWebhook{ChannelId: channel1.Id}
hook1 = ApiClient.Must(ApiClient.CreateIncomingWebhook(hook1)).Data.(*model.IncomingWebhook)
payload := "payload={\"text\": \"test text\"}"
if _, err := ApiClient.PostToWebhook(hook1.Id, payload); err != nil {
t.Fatal(err)
}
payload = "payload={\"text\": \"\"}"
if _, err := ApiClient.PostToWebhook(hook1.Id, payload); err == nil {
t.Fatal("should have errored - no text to post")
}
payload = "payload={\"text\": \"test text\", \"channel\": \"junk\"}"
if _, err := ApiClient.PostToWebhook(hook1.Id, payload); err == nil {
t.Fatal("should have errored - bad channel")
}
payload = "payload={\"text\": \"test text\"}"
if _, err := ApiClient.PostToWebhook("abc123", payload); err == nil {
t.Fatal("should have errored - bad hook")
}
} else {
if _, err := ApiClient.PostToWebhook("123", "123"); err == nil {
t.Fatal("should have failed - webhooks turned off")
}
}
}
func TestZZWebTearDown(t *testing.T) {
// *IMPORTANT*
// This should be the last function in any test file