Merge pull request #2052 from mattermost/PLT-1429
PLT-1429 adding user created slash commands
Этот коммит содержится в:
@@ -75,6 +75,8 @@ class ServiceSettings extends React.Component {
|
||||
config.ServiceSettings.EnableTesting = ReactDOM.findDOMNode(this.refs.EnableTesting).checked;
|
||||
config.ServiceSettings.EnableDeveloper = ReactDOM.findDOMNode(this.refs.EnableDeveloper).checked;
|
||||
config.ServiceSettings.EnableSecurityFixAlert = ReactDOM.findDOMNode(this.refs.EnableSecurityFixAlert).checked;
|
||||
config.ServiceSettings.EnableCommands = ReactDOM.findDOMNode(this.refs.EnableCommands).checked;
|
||||
config.ServiceSettings.EnableOnlyAdminIntegrations = ReactDOM.findDOMNode(this.refs.EnableOnlyAdminIntegrations).checked;
|
||||
|
||||
//config.ServiceSettings.EnableOAuthServiceProvider = ReactDOM.findDOMNode(this.refs.EnableOAuthServiceProvider).checked;
|
||||
|
||||
@@ -386,6 +388,100 @@ class ServiceSettings extends React.Component {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label
|
||||
className='control-label col-sm-4'
|
||||
htmlFor='EnableCommands'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='admin.service.cmdsTitle'
|
||||
defaultMessage='Enable Slash Commands: '
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-8'>
|
||||
<label className='radio-inline'>
|
||||
<input
|
||||
type='radio'
|
||||
name='EnableCommands'
|
||||
value='true'
|
||||
ref='EnableCommands'
|
||||
defaultChecked={this.props.config.ServiceSettings.EnableCommands}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='admin.service.true'
|
||||
defaultMessage='true'
|
||||
/>
|
||||
</label>
|
||||
<label className='radio-inline'>
|
||||
<input
|
||||
type='radio'
|
||||
name='EnableCommands'
|
||||
value='false'
|
||||
defaultChecked={!this.props.config.ServiceSettings.EnableCommands}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='admin.service.false'
|
||||
defaultMessage='false'
|
||||
/>
|
||||
</label>
|
||||
<p className='help-text'>
|
||||
<FormattedMessage
|
||||
id='admin.service.cmdsDesc'
|
||||
defaultMessage='When true, user created slash commands will be allowed.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label
|
||||
className='control-label col-sm-4'
|
||||
htmlFor='EnableOnlyAdminIntegrations'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='admin.service.integrationAdmin'
|
||||
defaultMessage='Enable Integrations for Admin Only: '
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-8'>
|
||||
<label className='radio-inline'>
|
||||
<input
|
||||
type='radio'
|
||||
name='EnableOnlyAdminIntegrations'
|
||||
value='true'
|
||||
ref='EnableOnlyAdminIntegrations'
|
||||
defaultChecked={this.props.config.ServiceSettings.EnableOnlyAdminIntegrations}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='admin.service.true'
|
||||
defaultMessage='true'
|
||||
/>
|
||||
</label>
|
||||
<label className='radio-inline'>
|
||||
<input
|
||||
type='radio'
|
||||
name='EnableOnlyAdminIntegrations'
|
||||
value='false'
|
||||
defaultChecked={!this.props.config.ServiceSettings.EnableOnlyAdminIntegrations}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='admin.service.false'
|
||||
defaultMessage='false'
|
||||
/>
|
||||
</label>
|
||||
<p className='help-text'>
|
||||
<FormattedMessage
|
||||
id='admin.service.integrationAdminDesc'
|
||||
defaultMessage='When true, user created integrations can only be created by admins.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label
|
||||
className='control-label col-sm-4'
|
||||
@@ -393,7 +489,7 @@ class ServiceSettings extends React.Component {
|
||||
>
|
||||
<FormattedMessage
|
||||
id='admin.service.overrideTitle'
|
||||
defaultMessage='Enable Overriding Usernames from Webhooks: '
|
||||
defaultMessage='Enable Overriding Usernames from Webhooks and Slash Commands: '
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-8'>
|
||||
@@ -427,7 +523,7 @@ class ServiceSettings extends React.Component {
|
||||
<p className='help-text'>
|
||||
<FormattedMessage
|
||||
id='admin.service.overrideDescription'
|
||||
defaultMessage='When true, webhooks will be allowed to change the username they are posting as. Note, combined with allowing icon overriding, this could open users up to phishing attacks.'
|
||||
defaultMessage='When true, webhooks and slash commands will be allowed to change the username they are posting as. Note, combined with allowing icon overriding, this could open users up to phishing attacks.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
@@ -440,7 +536,7 @@ class ServiceSettings extends React.Component {
|
||||
>
|
||||
<FormattedMessage
|
||||
id='admin.service.iconTitle'
|
||||
defaultMessage='Enable Overriding Icon from Webhooks: '
|
||||
defaultMessage='Enable Overriding Icon from Webhooks and Slash Commands: '
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-8'>
|
||||
@@ -474,7 +570,7 @@ class ServiceSettings extends React.Component {
|
||||
<p className='help-text'>
|
||||
<FormattedMessage
|
||||
id='admin.service.iconDescription'
|
||||
defaultMessage='When true, webhooks will be allowed to change the icon they post with. Note, combined with allowing username overriding, this could open users up to phishing attacks.'
|
||||
defaultMessage='When true, webhooks and slash commands will be allowed to change the icon they post with. Note, combined with allowing username overriding, this could open users up to phishing attacks.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -155,15 +155,10 @@ class CreatePost extends React.Component {
|
||||
post.message,
|
||||
false,
|
||||
(data) => {
|
||||
if (data.response === 'not implemented') {
|
||||
this.sendMessage(post);
|
||||
return;
|
||||
}
|
||||
|
||||
PostStore.storeDraft(data.channel_id, null);
|
||||
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
|
||||
|
||||
if (data.goto_location.length > 0) {
|
||||
if (data.goto_location && data.goto_location.length > 0) {
|
||||
window.location.href = data.goto_location;
|
||||
}
|
||||
},
|
||||
|
||||
652
web/react/components/user_settings/manage_command_hooks.jsx
Обычный файл
652
web/react/components/user_settings/manage_command_hooks.jsx
Обычный файл
@@ -0,0 +1,652 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import LoadingScreen from '../loading_screen.jsx';
|
||||
|
||||
import * as Client from '../../utils/client.jsx';
|
||||
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
requestTypePost: {
|
||||
id: 'user.settings.cmds.request_type_post',
|
||||
defaultMessage: 'POST'
|
||||
},
|
||||
requestTypeGet: {
|
||||
id: 'user.settings.cmds.request_type_get',
|
||||
defaultMessage: 'GET'
|
||||
},
|
||||
addDisplayNamePlaceholder: {
|
||||
id: 'user.settings.cmds.add_display_name.placeholder',
|
||||
defaultMessage: 'Display Name'
|
||||
},
|
||||
addUsernamePlaceholder: {
|
||||
id: 'user.settings.cmds.add_username.placeholder',
|
||||
defaultMessage: 'Username'
|
||||
},
|
||||
addTriggerPlaceholder: {
|
||||
id: 'user.settings.cmds.add_trigger.placeholder',
|
||||
defaultMessage: 'Command trigger e.g. "hello" not including the slash'
|
||||
},
|
||||
addAutoCompleteDescPlaceholder: {
|
||||
id: 'user.settings.cmds.auto_complete_desc.placeholder',
|
||||
defaultMessage: 'A short description of what this commands does.'
|
||||
},
|
||||
addAutoCompleteHintPlaceholder: {
|
||||
id: 'user.settings.cmds.auto_complete_hint.placeholder',
|
||||
defaultMessage: '[zipcode]'
|
||||
},
|
||||
adUrlPlaceholder: {
|
||||
id: 'user.settings.cmds.url.placeholder',
|
||||
defaultMessage: 'Must start with http:// or https://'
|
||||
}
|
||||
});
|
||||
|
||||
export default class ManageCommandCmds extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.getCmds = this.getCmds.bind(this);
|
||||
this.addNewCmd = this.addNewCmd.bind(this);
|
||||
this.emptyCmd = this.emptyCmd.bind(this);
|
||||
this.updateTrigger = this.updateTrigger.bind(this);
|
||||
this.updateURL = this.updateURL.bind(this);
|
||||
this.updateMethod = this.updateMethod.bind(this);
|
||||
this.updateUsername = this.updateUsername.bind(this);
|
||||
this.updateIconURL = this.updateIconURL.bind(this);
|
||||
this.updateDisplayName = this.updateDisplayName.bind(this);
|
||||
this.updateAutoComplete = this.updateAutoComplete.bind(this);
|
||||
this.updateAutoCompleteDesc = this.updateAutoCompleteDesc.bind(this);
|
||||
this.updateAutoCompleteHint = this.updateAutoCompleteHint.bind(this);
|
||||
|
||||
this.state = {cmds: [], cmd: this.emptyCmd(), getCmdsComplete: false};
|
||||
}
|
||||
|
||||
static propTypes() {
|
||||
return {
|
||||
intl: intlShape.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
emptyCmd() {
|
||||
var cmd = {};
|
||||
cmd.url = '';
|
||||
cmd.trigger = '';
|
||||
cmd.method = 'P';
|
||||
cmd.username = '';
|
||||
cmd.icon_url = '';
|
||||
cmd.auto_complete = false;
|
||||
cmd.auto_complete_desc = '';
|
||||
cmd.auto_complete_hint = '';
|
||||
cmd.display_name = '';
|
||||
return cmd;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getCmds();
|
||||
}
|
||||
|
||||
addNewCmd(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (this.state.cmd.trigger === '' || this.state.cmd.url === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
var cmd = this.state.cmd;
|
||||
if (cmd.trigger.length !== 0) {
|
||||
cmd.trigger = cmd.trigger.trim();
|
||||
}
|
||||
cmd.url = cmd.url.trim();
|
||||
|
||||
Client.addCommand(
|
||||
cmd,
|
||||
(data) => {
|
||||
let cmds = Object.assign([], this.state.cmds);
|
||||
if (!cmds) {
|
||||
cmds = [];
|
||||
}
|
||||
cmds.push(data);
|
||||
this.setState({cmds, addError: null, cmd: this.emptyCmd()});
|
||||
},
|
||||
(err) => {
|
||||
this.setState({addError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
removeCmd(id) {
|
||||
const data = {};
|
||||
data.id = id;
|
||||
|
||||
Client.deleteCommand(
|
||||
data,
|
||||
() => {
|
||||
const cmds = this.state.cmds;
|
||||
let index = -1;
|
||||
for (let i = 0; i < cmds.length; i++) {
|
||||
if (cmds[i].id === id) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index !== -1) {
|
||||
cmds.splice(index, 1);
|
||||
}
|
||||
|
||||
this.setState({cmds});
|
||||
},
|
||||
(err) => {
|
||||
this.setState({editError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
regenToken(id) {
|
||||
const regenData = {};
|
||||
regenData.id = id;
|
||||
|
||||
Client.regenCommandToken(
|
||||
regenData,
|
||||
(data) => {
|
||||
const cmds = Object.assign([], this.state.cmds);
|
||||
for (let i = 0; i < cmds.length; i++) {
|
||||
if (cmds[i].id === id) {
|
||||
cmds[i] = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({cmds, editError: null});
|
||||
},
|
||||
(err) => {
|
||||
this.setState({editError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getCmds() {
|
||||
Client.listTeamCommands(
|
||||
(data) => {
|
||||
if (data) {
|
||||
this.setState({cmds: data, getCmdsComplete: true, editError: null});
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
this.setState({editError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
updateTrigger(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.trigger = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateURL(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.url = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateMethod(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.method = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateUsername(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.username = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateIconURL(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.icon_url = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateDisplayName(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.display_name = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateAutoComplete(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.auto_complete = e.target.checked;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateAutoCompleteDesc(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.auto_complete_desc = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateAutoCompleteHint(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.auto_complete_hint = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
render() {
|
||||
let addError;
|
||||
if (this.state.addError) {
|
||||
addError = <label className='has-error'>{this.state.addError}</label>;
|
||||
}
|
||||
|
||||
let editError;
|
||||
if (this.state.editError) {
|
||||
addError = <label className='has-error'>{this.state.editError}</label>;
|
||||
}
|
||||
|
||||
const cmds = [];
|
||||
this.state.cmds.forEach((cmd) => {
|
||||
let triggerDiv;
|
||||
if (cmd.trigger && cmd.trigger.length !== 0) {
|
||||
triggerDiv = (
|
||||
<div className='padding-top'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.trigger'
|
||||
defaultMessage='Trigger: '
|
||||
/>
|
||||
</strong>{cmd.trigger}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
cmds.push(
|
||||
<div
|
||||
key={cmd.id}
|
||||
className='webcmd__item'
|
||||
>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.display_name'
|
||||
defaultMessage='Display Name: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.display_name}</span>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.username'
|
||||
defaultMessage='Username: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.username}</span>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.icon_url'
|
||||
defaultMessage='Icon URL: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.icon_url}</span>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete'
|
||||
defaultMessage='Auto Complete: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.auto_complete ? 'yes' : 'no'}</span>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_desc'
|
||||
defaultMessage='Auto Complete Description: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.auto_complete_desc}</span>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_hint'
|
||||
defaultMessage='Auto Complete Hint: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.auto_complete_hint}</span>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.request_type'
|
||||
defaultMessage='Request Type: '
|
||||
/>
|
||||
</strong>
|
||||
<span className='word-break--all'>
|
||||
{
|
||||
cmd.method === 'P' ?
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.request_type_post'
|
||||
defaultMessage='POST'
|
||||
/> :
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.request_type_get'
|
||||
defaultMessage='GET'
|
||||
/>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<div className='padding-top x2 webcmd__url'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.url'
|
||||
defaultMessage='URL: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.url}</span>
|
||||
</div>
|
||||
{triggerDiv}
|
||||
<div className='padding-top'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.token'
|
||||
defaultMessage='Token: '
|
||||
/>
|
||||
</strong>{cmd.token}
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<a
|
||||
className='text-danger'
|
||||
href='#'
|
||||
onClick={this.regenToken.bind(this, cmd.id)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.regen'
|
||||
defaultMessage='Regen Token'
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
className='webcmd__remove'
|
||||
href='#'
|
||||
onClick={this.removeCmd.bind(this, cmd.id)}
|
||||
>
|
||||
<span aria-hidden='true'>{'×'}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div className='padding-top x2 divider-light'></div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
let displayCmds;
|
||||
if (!this.state.getCmdsComplete) {
|
||||
displayCmds = <LoadingScreen/>;
|
||||
} else if (cmds.length > 0) {
|
||||
displayCmds = cmds;
|
||||
} else {
|
||||
displayCmds = (
|
||||
<div className='padding-top x2'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.none'
|
||||
defaultMessage='None'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const existingCmds = (
|
||||
<div className='webcmds__container'>
|
||||
<label className='control-label padding-top x2'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.existing'
|
||||
defaultMessage='Existing commands'
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top divider-light'></div>
|
||||
<div className='webcmds__list'>
|
||||
{displayCmds}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const disableButton = this.state.cmd.trigger === '' || this.state.cmd.url === '';
|
||||
|
||||
return (
|
||||
<div key='addCommandCmd'>
|
||||
<FormattedHTMLMessage
|
||||
id='user.settings.cmds.add_desc'
|
||||
defaultMessage='Create commands to send message events to an external integration. Please see <a href="http://mattermost.org/commands">http://mattermost.org/commands</a> to learn more.'
|
||||
/>
|
||||
<div><label className='control-label padding-top x2'>{'Add a new command'}</label></div>
|
||||
<div className='padding-top divider-light'></div>
|
||||
<div className='padding-top'>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.display_name'
|
||||
defaultMessage='Display Name: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='displayName'
|
||||
className='form-control'
|
||||
value={this.state.cmd.display_name}
|
||||
onChange={this.updateDisplayName}
|
||||
placeholder={this.props.intl.formatMessage(holders.addDisplayNamePlaceholder)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>{'Command display name.'}</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.username'
|
||||
defaultMessage='Username: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='username'
|
||||
className='form-control'
|
||||
value={this.state.cmd.username}
|
||||
onChange={this.updateUsername}
|
||||
placeholder={this.props.intl.formatMessage(holders.addUsernamePlaceholder)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.username_desc'
|
||||
defaultMessage='The username to use when overriding the post.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.icon_url'
|
||||
defaultMessage='Icon URL: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='iconURL'
|
||||
className='form-control'
|
||||
value={this.state.cmd.icon_url}
|
||||
onChange={this.updateIconURL}
|
||||
placeholder='https://www.example.com/myicon.png'
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.icon_url_desc'
|
||||
defaultMessage='URL to an icon'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.trigger'
|
||||
defaultMessage='Trigger: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='trigger'
|
||||
className='form-control'
|
||||
value={this.state.cmd.trigger}
|
||||
onChange={this.updateTrigger}
|
||||
placeholder={this.props.intl.formatMessage(holders.addTriggerPlaceholder)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.trigger_desc'
|
||||
defaultMessage='Word to trigger on'
|
||||
/>
|
||||
{''}</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete'
|
||||
defaultMessage='Auto Complete: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<label>
|
||||
<input
|
||||
type='checkbox'
|
||||
checked={this.state.cmd.auto_complete}
|
||||
onChange={this.updateAutoComplete}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_desc_desc'
|
||||
defaultMessage='A short description of what this commands does'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_help'
|
||||
defaultMessage='Show this command in autocomplete list.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_desc'
|
||||
defaultMessage='Auto Complete Description: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='autoCompleteDesc'
|
||||
className='form-control'
|
||||
value={this.state.cmd.auto_complete_desc}
|
||||
onChange={this.updateAutoCompleteDesc}
|
||||
placeholder={this.props.intl.formatMessage(holders.addAutoCompleteDescPlaceholder)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_desc_desc'
|
||||
defaultMessage='A short description of what this commands does'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_hint'
|
||||
defaultMessage='Auto Complete Hint: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='autoCompleteHint'
|
||||
className='form-control'
|
||||
value={this.state.cmd.auto_complete_hint}
|
||||
onChange={this.updateAutoCompleteHint}
|
||||
placeholder={this.props.intl.formatMessage(holders.addAutoCompleteHintPlaceholder)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_hint_desc'
|
||||
defaultMessage='List parameters to be passed to the command.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.request_type'
|
||||
defaultMessage='Request Type: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<select
|
||||
ref='method'
|
||||
className='form-control'
|
||||
value={this.state.cmd.method}
|
||||
onChange={this.updateMethod}
|
||||
>
|
||||
<option value='P'>
|
||||
{this.props.intl.formatMessage(holders.requestTypePost)}
|
||||
</option>
|
||||
<option value='G'>
|
||||
{this.props.intl.formatMessage(holders.requestTypeGet)}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.request_type_desc'
|
||||
defaultMessage='Command request type issued to the callback URL.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.url'
|
||||
defaultMessage='URL: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='URL'
|
||||
className='form-control'
|
||||
value={this.state.cmd.url}
|
||||
rows={1}
|
||||
onChange={this.updateURL}
|
||||
placeholder={this.props.intl.formatMessage(holders.adUrlPlaceholder)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.url_desc'
|
||||
defaultMessage='URL that will receive the HTTP POST or GET event'
|
||||
/>
|
||||
</div>
|
||||
{addError}
|
||||
</div>
|
||||
<div className='padding-top padding-bottom'>
|
||||
<a
|
||||
className={'btn btn-sm btn-primary'}
|
||||
href='#'
|
||||
disabled={disableButton}
|
||||
onClick={this.addNewCmd}
|
||||
>
|
||||
{'Add'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{existingCmds}
|
||||
{editError}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectIntl(ManageCommandCmds);
|
||||
@@ -5,6 +5,7 @@ import SettingItemMin from '../setting_item_min.jsx';
|
||||
import SettingItemMax from '../setting_item_max.jsx';
|
||||
import ManageIncomingHooks from './manage_incoming_hooks.jsx';
|
||||
import ManageOutgoingHooks from './manage_outgoing_hooks.jsx';
|
||||
import ManageCommandHooks from './manage_command_hooks.jsx';
|
||||
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||
|
||||
@@ -24,6 +25,14 @@ const holders = defineMessages({
|
||||
outDesc: {
|
||||
id: 'user.settings.integrations.outWebhooksDescription',
|
||||
defaultMessage: 'Manage your outgoing webhooks'
|
||||
},
|
||||
cmdName: {
|
||||
id: 'user.settings.integrations.commands',
|
||||
defaultMessage: 'Commands'
|
||||
},
|
||||
cmdDesc: {
|
||||
id: 'user.settings.integrations.commandsDescription',
|
||||
defaultMessage: 'Manage your commands'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -41,6 +50,7 @@ class UserSettingsIntegrationsTab extends React.Component {
|
||||
render() {
|
||||
let incomingHooksSection;
|
||||
let outgoingHooksSection;
|
||||
let commandHooksSection;
|
||||
var inputs = [];
|
||||
const {formatMessage} = this.props.intl;
|
||||
|
||||
@@ -106,6 +116,37 @@ class UserSettingsIntegrationsTab extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
if (global.window.mm_config.EnableCommands === 'true') {
|
||||
if (this.props.activeSection === 'command-hooks') {
|
||||
inputs.push(
|
||||
<ManageCommandHooks key='command-hook-ui' />
|
||||
);
|
||||
|
||||
commandHooksSection = (
|
||||
<SettingItemMax
|
||||
title={formatMessage(holders.cmdName)}
|
||||
width='medium'
|
||||
inputs={inputs}
|
||||
updateSection={(e) => {
|
||||
this.updateSection('');
|
||||
e.preventDefault();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
commandHooksSection = (
|
||||
<SettingItemMin
|
||||
title={formatMessage(holders.cmdName)}
|
||||
width='medium'
|
||||
describe={formatMessage(holders.cmdDesc)}
|
||||
updateSection={() => {
|
||||
this.updateSection('command-hooks');
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='modal-header'>
|
||||
@@ -144,6 +185,8 @@ class UserSettingsIntegrationsTab extends React.Component {
|
||||
<div className='divider-light'/>
|
||||
{outgoingHooksSection}
|
||||
<div className='divider-dark'/>
|
||||
{commandHooksSection}
|
||||
<div className='divider-dark'/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -774,20 +774,27 @@ export function savePreferences(preferences, success, error) {
|
||||
}
|
||||
|
||||
export function getSuggestedCommands(command, suggestionId, component) {
|
||||
client.executeCommand(
|
||||
'',
|
||||
command,
|
||||
true,
|
||||
client.listCommands(
|
||||
(data) => {
|
||||
var matches = [];
|
||||
data.forEach((cmd) => {
|
||||
if (('/' + cmd.trigger).indexOf(command) === 0) {
|
||||
matches.push({
|
||||
suggestion: '/' + cmd.trigger + ' ' + cmd.auto_complete_hint,
|
||||
description: cmd.auto_complete_desc
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// pull out the suggested commands from the returned data
|
||||
const terms = data.suggestions.map((suggestion) => suggestion.suggestion);
|
||||
const terms = matches.map((suggestion) => suggestion.suggestion);
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS,
|
||||
id: suggestionId,
|
||||
matchedPretext: command,
|
||||
terms,
|
||||
items: data.suggestions,
|
||||
items: matches,
|
||||
component
|
||||
});
|
||||
},
|
||||
|
||||
@@ -908,11 +908,11 @@ export function getChannelExtraInfo(id, memberLimit, success, error) {
|
||||
|
||||
export function executeCommand(channelId, command, suggest, success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/command',
|
||||
url: '/api/v1/commands/execute',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'POST',
|
||||
data: JSON.stringify({channelId: channelId, command: command, suggest: '' + suggest}),
|
||||
data: JSON.stringify({channelId, command, suggest: '' + suggest}),
|
||||
success,
|
||||
error: function onError(xhr, status, err) {
|
||||
var e = handleError('executeCommand', xhr, status, err);
|
||||
@@ -921,6 +921,78 @@ export function executeCommand(channelId, command, suggest, success, error) {
|
||||
});
|
||||
}
|
||||
|
||||
export function addCommand(cmd, success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/commands/create',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(cmd),
|
||||
success,
|
||||
error: (xhr, status, err) => {
|
||||
var e = handleError('addCommand', xhr, status, err);
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteCommand(data, success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/commands/delete',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(data),
|
||||
success,
|
||||
error: (xhr, status, err) => {
|
||||
var e = handleError('deleteCommand', xhr, status, err);
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function listTeamCommands(success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/commands/list_team_commands',
|
||||
dataType: 'json',
|
||||
type: 'GET',
|
||||
success,
|
||||
error: (xhr, status, err) => {
|
||||
var e = handleError('listTeamCommands', xhr, status, err);
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function regenCommandToken(data, success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/commands/regen_token',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(data),
|
||||
success,
|
||||
error: (xhr, status, err) => {
|
||||
var e = handleError('regenCommandToken', xhr, status, err);
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function listCommands(success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/commands/list',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'GET',
|
||||
success,
|
||||
error: function onError(xhr, status, err) {
|
||||
var e = handleError('listCommands', xhr, status, err);
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function getPostsPage(channelId, offset, limit, success, error, complete) {
|
||||
$.ajax({
|
||||
cache: false,
|
||||
|
||||
Ссылка в новой задаче
Block a user