diff options
Diffstat (limited to 'web/react/components/user_settings')
17 files changed, 0 insertions, 6171 deletions
diff --git a/web/react/components/user_settings/custom_theme_chooser.jsx b/web/react/components/user_settings/custom_theme_chooser.jsx deleted file mode 100644 index 4ee9fd0e2..000000000 --- a/web/react/components/user_settings/custom_theme_chooser.jsx +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import Constants from '../../utils/constants.jsx'; - -const OverlayTrigger = ReactBootstrap.OverlayTrigger; -const Popover = ReactBootstrap.Popover; - -import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl'; - -const messages = defineMessages({ - sidebarBg: { - id: 'user.settings.custom_theme.sidebarBg', - defaultMessage: 'Sidebar BG' - }, - sidebarText: { - id: 'user.settings.custom_theme.sidebarText', - defaultMessage: 'Sidebar Text' - }, - sidebarHeaderBg: { - id: 'user.settings.custom_theme.sidebarHeaderBg', - defaultMessage: 'Sidebar Header BG' - }, - sidebarHeaderTextColor: { - id: 'user.settings.custom_theme.sidebarHeaderTextColor', - defaultMessage: 'Sidebar Header Text' - }, - sidebarUnreadText: { - id: 'user.settings.custom_theme.sidebarUnreadText', - defaultMessage: 'Sidebar Unread Text' - }, - sidebarTextHoverBg: { - id: 'user.settings.custom_theme.sidebarTextHoverBg', - defaultMessage: 'Sidebar Text Hover BG' - }, - sidebarTextActiveBorder: { - id: 'user.settings.custom_theme.sidebarTextActiveBorder', - defaultMessage: 'Sidebar Text Active Border' - }, - sidebarTextActiveColor: { - id: 'user.settings.custom_theme.sidebarTextActiveColor', - defaultMessage: 'Sidebar Text Active Color' - }, - onlineIndicator: { - id: 'user.settings.custom_theme.onlineIndicator', - defaultMessage: 'Online Indicator' - }, - awayIndicator: { - id: 'user.settings.custom_theme.awayIndicator', - defaultMessage: 'Away Indicator' - }, - mentionBj: { - id: 'user.settings.custom_theme.mentionBj', - defaultMessage: 'Mention Jewel BG' - }, - mentionColor: { - id: 'user.settings.custom_theme.mentionColor', - defaultMessage: 'Mention Jewel Text' - }, - centerChannelBg: { - id: 'user.settings.custom_theme.centerChannelBg', - defaultMessage: 'Center Channel BG' - }, - centerChannelColor: { - id: 'user.settings.custom_theme.centerChannelColor', - defaultMessage: 'Center Channel Text' - }, - newMessageSeparator: { - id: 'user.settings.custom_theme.newMessageSeparator', - defaultMessage: 'New Message Separator' - }, - linkColor: { - id: 'user.settings.custom_theme.linkColor', - defaultMessage: 'Link Color' - }, - buttonBg: { - id: 'user.settings.custom_theme.buttonBg', - defaultMessage: 'Button BG' - }, - buttonColor: { - id: 'user.settings.custom_theme.buttonColor', - defaultMessage: 'Button Text' - }, - mentionHighlightBg: { - id: 'user.settings.custom_theme.mentionHighlightBg', - defaultMessage: 'Mention Highlight BG' - }, - mentionHighlightLink: { - id: 'user.settings.custom_theme.mentionHighlightLink', - defaultMessage: 'Mention Highlight Link' - }, - codeTheme: { - id: 'user.settings.custom_theme.codeTheme', - defaultMessage: 'Code Theme' - } -}); - -class CustomThemeChooser extends React.Component { - constructor(props) { - super(props); - - this.onPickerChange = this.onPickerChange.bind(this); - this.onInputChange = this.onInputChange.bind(this); - this.pasteBoxChange = this.pasteBoxChange.bind(this); - this.toggleContent = this.toggleContent.bind(this); - - this.state = {}; - } - componentDidMount() { - $('.color-picker').colorpicker({ - format: 'hex' - }); - $('.color-picker').on('changeColor', this.onPickerChange); - } - componentDidUpdate() { - const theme = this.props.theme; - Constants.THEME_ELEMENTS.forEach((element) => { - if (theme.hasOwnProperty(element.id) && element.id !== 'codeTheme') { - $('#' + element.id).data('colorpicker').color.setColor(theme[element.id]); - $('#' + element.id).colorpicker('update'); - } - }); - } - onPickerChange(e) { - const theme = this.props.theme; - theme[e.target.id] = e.color.toHex(); - theme.type = 'custom'; - this.props.updateTheme(theme); - } - onInputChange(e) { - const theme = this.props.theme; - theme[e.target.parentNode.id] = e.target.value; - theme.type = 'custom'; - this.props.updateTheme(theme); - } - pasteBoxChange(e) { - const text = e.target.value; - - if (text.length === 0) { - return; - } - - const colors = text.split(','); - - const theme = {type: 'custom'}; - let index = 0; - Constants.THEME_ELEMENTS.forEach((element) => { - if (index < colors.length - 1) { - theme[element.id] = colors[index]; - } - index++; - }); - theme.codeTheme = colors[colors.length - 1]; - - this.props.updateTheme(theme); - } - toggleContent(e) { - e.stopPropagation(); - if ($(e.target).hasClass('theme-elements__header')) { - $(e.target).next().slideToggle(); - $(e.target).toggleClass('open'); - } else { - $(e.target).closest('.theme-elements__header').next().slideToggle(); - $(e.target).closest('.theme-elements__header').toggleClass('open'); - } - } - render() { - const {formatMessage} = this.props.intl; - const theme = this.props.theme; - - const sidebarElements = []; - const centerChannelElements = []; - const linkAndButtonElements = []; - let colors = ''; - Constants.THEME_ELEMENTS.forEach((element, index) => { - if (element.id === 'codeTheme') { - const codeThemeOptions = []; - - element.themes.forEach((codeTheme, codeThemeIndex) => { - codeThemeOptions.push( - <option - key={'code-theme-key' + codeThemeIndex} - value={codeTheme.id} - > - {codeTheme.uiName} - </option> - ); - }); - - var popoverContent = ( - <Popover - bsStyle='info' - id='code-popover' - className='code-popover' - > - <img - width='200' - src={'/static/images/themes/code_themes/' + theme[element.id] + '.png'} - /> - </Popover> - ); - - centerChannelElements.push( - <div - className='col-sm-6 form-group' - key={'custom-theme-key' + index} - > - <label className='custom-label'>{formatMessage(messages[element.id])}</label> - <div - className='input-group theme-group group--code dropdown' - id={element.id} - > - <select - className='form-control' - type='text' - value={theme[element.id]} - onChange={this.onInputChange} - > - {codeThemeOptions} - </select> - <OverlayTrigger - placement='top' - overlay={popoverContent} - ref='headerOverlay' - > - <span className='input-group-addon'> - <img - src={'/static/images/themes/code_themes/' + theme[element.id] + '.png'} - /> - </span> - </OverlayTrigger> - </div> - </div> - ); - } else if (element.group === 'centerChannelElements') { - centerChannelElements.push( - <div - className='col-sm-6 form-group element' - key={'custom-theme-key' + index} - > - <label className='custom-label'>{formatMessage(messages[element.id])}</label> - <div - className='input-group color-picker' - id={element.id} - > - <input - className='form-control' - type='text' - value={theme[element.id]} - onChange={this.onInputChange} - /> - <span className='input-group-addon'><i></i></span> - </div> - </div> - ); - - colors += theme[element.id] + ','; - } else if (element.group === 'sidebarElements') { - sidebarElements.push( - <div - className='col-sm-6 form-group element' - key={'custom-theme-key' + index} - > - <label className='custom-label'>{formatMessage(messages[element.id])}</label> - <div - className='input-group color-picker' - id={element.id} - > - <input - className='form-control' - type='text' - value={theme[element.id]} - onChange={this.onInputChange} - /> - <span className='input-group-addon'><i></i></span> - </div> - </div> - ); - - colors += theme[element.id] + ','; - } else { - linkAndButtonElements.push( - <div - className='col-sm-6 form-group element' - key={'custom-theme-key' + index} - > - <label className='custom-label'>{formatMessage(messages[element.id])}</label> - <div - className='input-group color-picker' - id={element.id} - > - <input - className='form-control' - type='text' - value={theme[element.id]} - onChange={this.onInputChange} - /> - <span className='input-group-addon'><i></i></span> - </div> - </div> - ); - - colors += theme[element.id] + ','; - } - }); - - colors += theme.codeTheme; - - const pasteBox = ( - <div className='col-sm-12'> - <label className='custom-label'> - <FormattedMessage - id='user.settings.custom_theme.copyPaste' - defaultMessage='Copy and paste to share theme colors:' - /> - </label> - <input - type='text' - className='form-control' - value={colors} - onChange={this.pasteBoxChange} - /> - </div> - ); - - return ( - <div className='appearance-section padding-top'> - <div className='theme-elements row'> - <div - className='theme-elements__header' - onClick={this.toggleContent} - > - {'Sidebar Styles'} - <div className='header__icon'> - <i className='fa fa-plus'></i> - <i className='fa fa-minus'></i> - </div> - </div> - <div className='theme-elements__body'> - {sidebarElements} - </div> - </div> - <div className='theme-elements row'> - <div - className='theme-elements__header' - onClick={this.toggleContent} - > - {'Center Channel Styles'} - <div className='header__icon'> - <i className='fa fa-plus'></i> - <i className='fa fa-minus'></i> - </div> - </div> - <div className='theme-elements__body'> - {centerChannelElements} - </div> - </div> - <div className='theme-elements row form-group'> - <div - className='theme-elements__header' - onClick={this.toggleContent} - > - {'Link and Button Styles'} - <div className='header__icon'> - <i className='fa fa-plus'></i> - <i className='fa fa-minus'></i> - </div> - </div> - <div className='theme-elements__body'> - {linkAndButtonElements} - </div> - </div> - <div className='row'> - {pasteBox} - </div> - </div> - ); - } -} - -CustomThemeChooser.propTypes = { - intl: intlShape.isRequired, - theme: React.PropTypes.object.isRequired, - updateTheme: React.PropTypes.func.isRequired -}; - -export default injectIntl(CustomThemeChooser); diff --git a/web/react/components/user_settings/import_theme_modal.jsx b/web/react/components/user_settings/import_theme_modal.jsx deleted file mode 100644 index e9e90a936..000000000 --- a/web/react/components/user_settings/import_theme_modal.jsx +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import ModalStore from '../../stores/modal_store.jsx'; -import UserStore from '../../stores/user_store.jsx'; -import * as Utils from '../../utils/utils.jsx'; -import * as Client from '../../utils/client.jsx'; -const Modal = ReactBootstrap.Modal; - -import AppDispatcher from '../../dispatcher/app_dispatcher.jsx'; -import Constants from '../../utils/constants.jsx'; - -import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl'; - -const holders = defineMessages({ - submitError: { - id: 'user.settings.import_theme.submitError', - defaultMessage: 'Invalid format, please try copying and pasting in again.' - } -}); - -const ActionTypes = Constants.ActionTypes; - -class ImportThemeModal extends React.Component { - constructor(props) { - super(props); - - this.updateShow = this.updateShow.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - this.handleChange = this.handleChange.bind(this); - - this.state = { - inputError: '', - show: false - }; - } - componentDidMount() { - ModalStore.addModalListener(ActionTypes.TOGGLE_IMPORT_THEME_MODAL, this.updateShow); - } - componentWillUnmount() { - ModalStore.removeModalListener(ActionTypes.TOGGLE_IMPORT_THEME_MODAL, this.updateShow); - } - updateShow(show) { - this.setState({show}); - } - handleSubmit(e) { - e.preventDefault(); - - const text = ReactDOM.findDOMNode(this.refs.input).value; - - if (!this.isInputValid(text)) { - this.setState({inputError: this.props.intl.formatMessage(holders.submitError)}); - return; - } - - const colors = text.split(','); - const theme = {type: 'custom'}; - - theme.sidebarBg = colors[0]; - theme.sidebarText = colors[5]; - theme.sidebarUnreadText = colors[5]; - theme.sidebarTextHoverBg = colors[4]; - theme.sidebarTextActiveBorder = colors[2]; - theme.sidebarTextActiveColor = colors[3]; - theme.sidebarHeaderBg = colors[1]; - theme.sidebarHeaderTextColor = colors[5]; - theme.onlineIndicator = colors[6]; - theme.awayIndicator = '#E0B333'; - theme.mentionBj = colors[7]; - theme.mentionColor = '#ffffff'; - theme.centerChannelBg = '#ffffff'; - theme.centerChannelColor = '#333333'; - theme.newMessageSeparator = '#F80'; - theme.linkColor = '#2389d7'; - theme.buttonBg = '#26a970'; - theme.buttonColor = '#ffffff'; - theme.mentionHighlightBg = '#fff2bb'; - theme.mentionHighlightLink = '#2f81b7'; - theme.codeTheme = 'github'; - - let user = UserStore.getCurrentUser(); - user.theme_props = theme; - - Client.updateUser(user, - (data) => { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_ME, - me: data - }); - - this.setState({show: false}); - Utils.applyTheme(theme); - }, - (err) => { - var state = this.getStateFromStores(); - state.serverError = err; - this.setState(state); - } - ); - } - isInputValid(text) { - if (text.length === 0) { - return false; - } - - if (text.indexOf(' ') !== -1) { - return false; - } - - if (text.length > 0 && text.indexOf(',') === -1) { - return false; - } - - if (text.length > 0) { - const colors = text.split(','); - - if (colors.length !== 8) { - return false; - } - - for (let i = 0; i < colors.length; i++) { - if (colors[i].length !== 7 && colors[i].length !== 4) { - return false; - } - - if (colors[i].charAt(0) !== '#') { - return false; - } - } - } - - return true; - } - handleChange(e) { - if (this.isInputValid(e.target.value)) { - this.setState({inputError: null}); - } else { - this.setState({inputError: this.props.intl.formatMessage(holders.submitError)}); - } - } - render() { - return ( - <span> - <Modal - show={this.state.show} - onHide={() => this.setState({show: false})} - > - <Modal.Header closeButton={true}> - <Modal.Title> - <FormattedMessage - id='user.settings.import_theme.importHeader' - defaultMessage='Import Slack Theme' - /> - </Modal.Title> - </Modal.Header> - <form - role='form' - className='form-horizontal' - > - <Modal.Body> - <p> - <FormattedMessage - id='user.settings.import_theme.importBody' - defaultMessage='To import a theme, go to a Slack team and look for “Preferences -> Sidebar Theme”. Open the custom theme option, copy the theme color values and paste them here:' - /> - </p> - <div className='form-group less'> - <div className='col-sm-9'> - <input - ref='input' - type='text' - className='form-control' - onChange={this.handleChange} - /> - <div className='input__help'> - {this.state.inputError} - </div> - </div> - </div> - </Modal.Body> - <Modal.Footer> - <button - type='button' - className='btn btn-default' - onClick={() => this.setState({show: false})} - > - <FormattedMessage - id='user.settings.import_theme.cancel' - defaultMessage='Cancel' - /> - </button> - <button - onClick={this.handleSubmit} - type='submit' - className='btn btn-primary' - tabIndex='3' - > - <FormattedMessage - id='user.settings.import_theme.submit' - defaultMessage='Submit' - /> - </button> - </Modal.Footer> - </form> - </Modal> - </span> - ); - } -} - -ImportThemeModal.propTypes = { - intl: intlShape.isRequired -}; - -export default injectIntl(ImportThemeModal); diff --git a/web/react/components/user_settings/manage_command_hooks.jsx b/web/react/components/user_settings/manage_command_hooks.jsx deleted file mode 100644 index 2947138be..000000000 --- a/web/react/components/user_settings/manage_command_hooks.jsx +++ /dev/null @@ -1,679 +0,0 @@ -// 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: 'Example: "Search patient records"' - }, - 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: 'Example: "Returns search results for patient records"' - }, - addAutoCompleteHintPlaceholder: { - id: 'user.settings.cmds.auto_complete_hint.placeholder', - defaultMessage: 'Example: [Patient Name]' - }, - adUrlPlaceholder: { - id: 'user.settings.cmds.url.placeholder', - defaultMessage: 'Must start with http:// or https://' - }, - autocompleteYes: { - id: 'user.settings.cmds.auto_complete.yes', - defaultMessage: 'yes' - }, - autocompleteNo: { - id: 'user.settings.cmds.auto_complete.no', - defaultMessage: 'no' - } -}); - -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 x2'> - <strong> - <FormattedMessage - id='user.settings.cmds.trigger' - defaultMessage='Command Trigger Word: ' - /> - </strong>{cmd.trigger} - </div> - ); - } - - cmds.push( - <div - key={cmd.id} - className='webhook__item webcmd__item' - > - {triggerDiv} - <div className='padding-top x2 webcmd__url'> - <strong> - <FormattedMessage - id='user.settings.cmds.url' - defaultMessage='Request URL: ' - /> - </strong><span className='word-break--all'>{cmd.url}</span> - </div> - <div className='padding-top x2'> - <strong> - <FormattedMessage - id='user.settings.cmds.request_type' - defaultMessage='Request Method: ' - /> - </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'> - <strong> - <FormattedMessage - id='user.settings.cmds.username' - defaultMessage='Response 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='Response Icon: ' - /> - </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='Autocomplete: ' - /> - </strong><span className='word-break--all'>{cmd.auto_complete ? this.props.intl.formatMessage(holders.autocompleteYes) : this.props.intl.formatMessage(holders.autocompleteNo)}</span> - </div> - <div className='padding-top x2'> - <strong> - <FormattedMessage - id='user.settings.cmds.auto_complete_hint' - defaultMessage='Autocomplete Hint: ' - /> - </strong><span className='word-break--all'>{cmd.auto_complete_hint}</span> - </div> - <div className='padding-top x2'> - <strong> - <FormattedMessage - id='user.settings.cmds.auto_complete_desc' - defaultMessage='Autocomplete Description: ' - /> - </strong><span className='word-break--all'>{cmd.auto_complete_desc}</span> - </div> - <div className='padding-top x2'> - <strong> - <FormattedMessage - id='user.settings.cmds.display_name' - defaultMessage='Descriptive Label: ' - /> - </strong><span className='word-break--all'>{cmd.display_name}</span> - </div> - <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='webhook__remove 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='webhooks__container 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='webhooks__list 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 slash commands to send events to external integrations and receive a response. For example typing `/patient Joe Smith` could bring back search results from your internal health records management system for the name “Joe Smith”. Please see <a href="http://docs.mattermost.com/developer/slash-commands.html">Slash commands documentation</a> for detailed instructions. View all slash commands configured on this team below.' - /> - <div><label className='control-label padding-top x2'> - <FormattedMessage - id='user.settings.cmds.add_new' - defaultMessage='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.trigger' - defaultMessage='Command Trigger Word: ' - /> - </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='Examples: /patient, /client, /employee Reserved: /echo, /join, /logout, /me, /shrug' - /> - </div> - </div> - - <div className='padding-top x2'> - <label className='control-label'> - <FormattedMessage - id='user.settings.cmds.url' - defaultMessage='Request 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='The callback URL to receive the HTTP POST or GET event request when the slash command is run.' - /> - </div> - </div> - - <div className='padding-top x2'> - <label className='control-label'> - <FormattedMessage - id='user.settings.cmds.request_type' - defaultMessage='Request Method: ' - /> - </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='The type of command request issued to the Request URL.' - /> - </div> - </div> - - <div className='padding-top x2'> - <label className='control-label'> - <FormattedMessage - id='user.settings.cmds.username' - defaultMessage='Response 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='Choose a username override for responses for this slash command. Usernames can consist of up to 22 characters consisting of lowercase letters, numbers and they symbols "-", "_", and "." .' - /> - </div> - </div> - - <div className='padding-top x2'> - <label className='control-label'> - <FormattedMessage - id='user.settings.cmds.icon_url' - defaultMessage='Response Icon: ' - /> - </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='Choose a profile picture override for the post responses to this slash command. Enter the URL of a .png or .jpg file at least 128 pixels by 128 pixels.' - /> - </div> - </div> - - <div className='padding-top x2'> - <label className='control-label'> - <FormattedMessage - id='user.settings.cmds.auto_complete' - defaultMessage='Autocomplete: ' - /> - </label> - <div className='padding-top'> - <div className='checkbox'> - <label> - <input - type='checkbox' - checked={this.state.cmd.auto_complete} - onChange={this.updateAutoComplete} - /> - <FormattedMessage - id='user.settings.cmds.auto_complete_help' - defaultMessage=' Show this command in the autocomplete list.' - /> - </label> - </div> - </div> - </div> - - <div className='padding-top x2'> - <label className='control-label'> - <FormattedMessage - id='user.settings.cmds.auto_complete_hint' - defaultMessage='Autocomplete 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='Optional hint in the autocomplete list about parameters needed for command.' - /> - </div> - </div> - - <div className='padding-top x2'> - <label className='control-label'> - <FormattedMessage - id='user.settings.cmds.auto_complete_desc' - defaultMessage='Autocomplete 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='Optional short description of slash command for the autocomplete list.' - /> - </div> - </div> - - <div className='padding-top x2'> - <label className='control-label'> - <FormattedMessage - id='user.settings.cmds.display_name' - defaultMessage='Descriptive Label: ' - /> - </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'> - <FormattedMessage - id='user.settings.cmds.cmd_display_name' - defaultMessage='Brief description of slash command to show in listings.' - /> - </div> - {addError} - </div> - - <div className='padding-top x2 padding-bottom'> - <a - className={'btn btn-sm btn-primary'} - href='#' - disabled={disableButton} - onClick={this.addNewCmd} - > - <FormattedMessage - id='user.settings.cmds.add' - defaultMessage='Add' - /> - </a> - </div> - </div> - {existingCmds} - {editError} - </div> - ); - } -} - -export default injectIntl(ManageCommandCmds); diff --git a/web/react/components/user_settings/manage_incoming_hooks.jsx b/web/react/components/user_settings/manage_incoming_hooks.jsx deleted file mode 100644 index 79a71b5ac..000000000 --- a/web/react/components/user_settings/manage_incoming_hooks.jsx +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import * as Client from '../../utils/client.jsx'; -import * as Utils from '../../utils/utils.jsx'; -import Constants from '../../utils/constants.jsx'; -import ChannelStore from '../../stores/channel_store.jsx'; -import LoadingScreen from '../loading_screen.jsx'; - -import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl'; - -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() { - const hook = {}; - 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) { - const data = {}; - data.id = id; - - Client.deleteIncomingHook( - data, - () => { - const hooks = this.state.hooks; - 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) => { - const state = this.state; - - 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(); - const options = []; - channels.forEach((channel) => { - if (channel.type !== Constants.DM_CHANNEL) { - options.push( - <option - key={'incoming-hook' + channel.id} - value={channel.id} - > - {channel.display_name} - </option> - ); - } - }); - - let disableButton = ''; - if (this.state.channelId === '') { - disableButton = ' disable'; - } - - const hooks = []; - this.state.hooks.forEach((hook) => { - const c = ChannelStore.get(hook.channel_id); - if (c) { - hooks.push( - <div - key={hook.id} - className='webhook__item' - > - <div className='padding-top x2 webhook__url'> - <strong>{'URL: '}</strong> - <span className='word-break--all'>{Utils.getWindowLocationOrigin() + '/hooks/' + hook.id}</span> - </div> - <div className='padding-top'> - <strong> - <FormattedMessage - id='user.settings.hooks_in.channel' - defaultMessage='Channel: ' - /> - </strong>{c.display_name} - </div> - <a - className={'webhook__remove'} - href='#' - onClick={this.removeHook.bind(this, hook.id)} - > - <span aria-hidden='true'>{'×'}</span> - </a> - <div className='padding-top x2 divider-light'></div> - </div> - ); - } - }); - - let displayHooks; - if (!this.state.getHooksComplete) { - displayHooks = <LoadingScreen/>; - } else if (hooks.length > 0) { - displayHooks = hooks; - } else { - displayHooks = ( - <div className='padding-top x2'> - <FormattedMessage - id='user.settings.hooks_in.none' - defaultMessage='None' - /> - </div> - ); - } - - const existingHooks = ( - <div className='webhooks__container'> - <label className='control-label padding-top x2'> - <FormattedMessage - id='user.settings.hooks_in.existing' - defaultMessage='Existing incoming webhooks' - /> - </label> - <div className='padding-top divider-light'></div> - <div className='webhooks__list'> - {displayHooks} - </div> - </div> - ); - - return ( - <div key='addIncomingHook'> - <FormattedHTMLMessage - id='user.settings.hooks_in.description' - defaultMessage='Create webhook URLs for use in external integrations. Please see <a href="http://docs.mattermost.com/developer/webhooks-incoming.html" target="_blank">incoming webhooks documentation</a> to learn more. View all incoming webhooks configured on this team below.' - /> - <div><label className='control-label padding-top x2'> - <FormattedMessage - id='user.settings.hooks_in.addTitle' - defaultMessage='Add a new incoming webhook' - /> - </label></div> - <div className='row padding-top'> - <div className='col-sm-10 padding-bottom'> - <select - ref='channelName' - className='form-control' - value={this.state.channelId} - onChange={this.updateChannelId} - > - {options} - </select> - {serverError} - </div> - <div className='col-sm-2 col-xs-4 no-padding--left padding-bottom'> - <a - className={'btn form-control no-padding btn-sm btn-primary' + disableButton} - href='#' - onClick={this.addNewHook} - > - <FormattedMessage - id='user.settings.hooks_in.add' - defaultMessage='Add' - /> - </a> - </div> - </div> - {existingHooks} - </div> - ); - } -} diff --git a/web/react/components/user_settings/manage_languages.jsx b/web/react/components/user_settings/manage_languages.jsx deleted file mode 100644 index 6b00a65c7..000000000 --- a/web/react/components/user_settings/manage_languages.jsx +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. - -import SettingItemMax from '../setting_item_max.jsx'; - -import * as Client from '../../utils/client.jsx'; -import * as Utils from '../../utils/utils.jsx'; -import * as GlobalActions from '../../action_creators/global_actions.jsx'; - -import {FormattedMessage} from 'mm-intl'; - -export default class ManageLanguage extends React.Component { - constructor(props) { - super(props); - - this.setupInitialState = this.setupInitialState.bind(this); - this.setLanguage = this.setLanguage.bind(this); - this.changeLanguage = this.changeLanguage.bind(this); - this.submitUser = this.submitUser.bind(this); - this.state = this.setupInitialState(props); - } - setupInitialState(props) { - var user = props.user; - return { - languages: Utils.languages(), - locale: user.locale - }; - } - setLanguage(e) { - this.setState({locale: e.target.value}); - } - changeLanguage(e) { - e.preventDefault(); - - var user = this.props.user; - var locale = this.state.locale; - - user.locale = locale; - - this.submitUser(user); - } - submitUser(user) { - Client.updateUser(user, - () => { - GlobalActions.newLocalizationSelected(user.locale); - }, - (err) => { - let serverError; - if (err.message) { - serverError = err.message; - } else { - serverError = err; - } - this.setState({serverError}); - } - ); - } - render() { - let serverError; - if (this.state.serverError) { - serverError = <label className='has-error'>{this.state.serverError}</label>; - } - - const options = []; - this.state.languages.forEach((lang) => { - options.push( - <option - key={lang.value} - value={lang.value} - > - {lang.name} - </option>); - }); - - const input = ( - <div key='changeLanguage'> - <br/> - <label className='control-label'> - <FormattedMessage - id='user.settings.languages.change' - defaultMessage='Change interface language' - /> - </label> - <div className='padding-top'> - <select - ref='language' - className='form-control' - value={this.state.locale} - onChange={this.setLanguage} - > - {options} - </select> - {serverError} - </div> - </div> - ); - - return ( - <SettingItemMax - title={ - <FormattedMessage - id='user.settings.display.language' - defaultMessage='Language' - /> - } - width='medium' - submit={this.changeLanguage} - inputs={[input]} - updateSection={this.props.updateSection} - /> - ); - } -} - -ManageLanguage.propTypes = { - user: React.PropTypes.object.isRequired, - updateSection: React.PropTypes.func.isRequired -}; diff --git a/web/react/components/user_settings/manage_outgoing_hooks.jsx b/web/react/components/user_settings/manage_outgoing_hooks.jsx deleted file mode 100644 index 487254d15..000000000 --- a/web/react/components/user_settings/manage_outgoing_hooks.jsx +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import LoadingScreen from '../loading_screen.jsx'; - -import ChannelStore from '../../stores/channel_store.jsx'; - -import * as Client from '../../utils/client.jsx'; -import Constants from '../../utils/constants.jsx'; - -import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl'; - -const holders = defineMessages({ - optional: { - id: 'user.settings.hooks_out.optional', - defaultMessage: 'Optional if channel selected' - }, - callbackHolder: { - id: 'user.settings.hooks_out.callbackHolder', - defaultMessage: 'Each URL must start with http:// or https://' - }, - select: { - id: 'user.settings.hooks_out.select', - defaultMessage: '--- Select a channel ---' - } -}); - -class ManageOutgoingHooks extends React.Component { - constructor() { - super(); - - this.getHooks = this.getHooks.bind(this); - this.addNewHook = this.addNewHook.bind(this); - this.updateChannelId = this.updateChannelId.bind(this); - this.updateTriggerWords = this.updateTriggerWords.bind(this); - this.updateCallbackURLs = this.updateCallbackURLs.bind(this); - - this.state = {hooks: [], channelId: '', triggerWords: '', callbackURLs: '', getHooksComplete: false}; - } - componentDidMount() { - this.getHooks(); - } - addNewHook(e) { - e.preventDefault(); - - if ((this.state.channelId === '' && this.state.triggerWords === '') || - this.state.callbackURLs === '') { - return; - } - - const hook = {}; - hook.channel_id = this.state.channelId; - if (this.state.triggerWords.length !== 0) { - hook.trigger_words = this.state.triggerWords.trim().split(','); - } - hook.callback_urls = this.state.callbackURLs.split('\n').map((url) => url.trim()); - - Client.addOutgoingHook( - hook, - (data) => { - let hooks = Object.assign([], this.state.hooks); - if (!hooks) { - hooks = []; - } - hooks.push(data); - this.setState({hooks, addError: null, channelId: '', triggerWords: '', callbackURLs: ''}); - }, - (err) => { - this.setState({addError: err.message}); - } - ); - } - removeHook(id) { - const data = {}; - data.id = id; - - Client.deleteOutgoingHook( - data, - () => { - const hooks = this.state.hooks; - 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({editError: err.message}); - } - ); - } - regenToken(id) { - const regenData = {}; - regenData.id = id; - - Client.regenOutgoingHookToken( - regenData, - (data) => { - const hooks = Object.assign([], this.state.hooks); - for (let i = 0; i < hooks.length; i++) { - if (hooks[i].id === id) { - hooks[i] = data; - break; - } - } - - this.setState({hooks, editError: null}); - }, - (err) => { - this.setState({editError: err.message}); - } - ); - } - getHooks() { - Client.listOutgoingHooks( - (data) => { - if (data) { - this.setState({hooks: data, getHooksComplete: true, editError: null}); - } - }, - (err) => { - this.setState({editError: err.message}); - } - ); - } - updateChannelId(e) { - this.setState({channelId: e.target.value}); - } - updateTriggerWords(e) { - this.setState({triggerWords: e.target.value}); - } - updateCallbackURLs(e) { - this.setState({callbackURLs: e.target.value}); - } - 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 channels = ChannelStore.getAll(); - const options = []; - options.push( - <option - key='select-channel' - value='' - > - {this.props.intl.formatMessage(holders.select)} - </option> - ); - - channels.forEach((channel) => { - if (channel.type === Constants.OPEN_CHANNEL) { - options.push( - <option - key={'outgoing-hook' + channel.id} - value={channel.id} - > - {channel.display_name} - </option> - ); - } - }); - - const hooks = []; - this.state.hooks.forEach((hook) => { - const c = ChannelStore.get(hook.channel_id); - - if (!c && hook.channel_id && hook.channel_id.length !== 0) { - return; - } - - let channelDiv; - if (c) { - channelDiv = ( - <div className='padding-top'> - <strong> - <FormattedMessage - id='user.settings.hooks_out.channel' - defaultMessage='Channel: ' - /> - </strong>{c.display_name} - </div> - ); - } - - let triggerDiv; - if (hook.trigger_words && hook.trigger_words.length !== 0) { - triggerDiv = ( - <div className='padding-top'> - <strong> - <FormattedMessage - id='user.settings.hooks_out.trigger' - defaultMessage='Trigger Words: ' - /> - </strong>{hook.trigger_words.join(', ')} - </div> - ); - } - - hooks.push( - <div - key={hook.id} - className='webhook__item' - > - <div className='padding-top x2 webhook__url'> - <strong>{'URLs: '}</strong><span className='word-break--all'>{hook.callback_urls.join(', ')}</span> - </div> - {channelDiv} - {triggerDiv} - <div className='padding-top'> - <strong>{'Token: '}</strong>{hook.token} - </div> - <div className='padding-top'> - <a - className='text-danger' - href='#' - onClick={this.regenToken.bind(this, hook.id)} - > - <FormattedMessage - id='user.settings.hooks_out.regen' - defaultMessage='Regen Token' - /> - </a> - <a - className='webhook__remove' - href='#' - onClick={this.removeHook.bind(this, hook.id)} - > - <span aria-hidden='true'>{'×'}</span> - </a> - </div> - <div className='padding-top x2 divider-light'></div> - </div> - ); - }); - - let displayHooks; - if (!this.state.getHooksComplete) { - displayHooks = <LoadingScreen/>; - } else if (hooks.length > 0) { - displayHooks = hooks; - } else { - displayHooks = ( - <div className='padding-top x2'> - <FormattedMessage - id='user.settings.hooks_out.none' - defaultMessage='None' - /> - </div> - ); - } - - const existingHooks = ( - <div className='webhooks__container'> - <label className='control-label padding-top x2'> - <FormattedMessage - id='user.settings.hooks_out.existing' - defaultMessage='Existing outgoing webhooks' - /> - </label> - <div className='padding-top divider-light'></div> - <div className='webhooks__list'> - {displayHooks} - </div> - </div> - ); - - const disableButton = (this.state.channelId === '' && this.state.triggerWords === '') || this.state.callbackURLs === ''; - - return ( - <div key='addOutgoingHook'> - <FormattedHTMLMessage - id='user.settings.hooks_out.addDescription' - defaultMessage='Create webhooks to send new message events to an external integration. Please see <a href="http://docs.mattermost.com/developer/webhooks-outgoing.html" target="_blank">outgoing webhooks documentation</a> to learn more. View all outgoing webhooks configured on this team below.' - /> - <div><label className='control-label padding-top x2'> - <FormattedMessage - id='user.settings.hooks_out.addTitle' - defaultMessage='Add a new outgoing webhook' - /> - </label></div> - <div className='padding-top divider-light'></div> - <div className='padding-top'> - <div> - <label className='control-label'> - <FormattedMessage - id='user.settings.hooks_out.channel' - defaultMessage='Channel: ' - /> - </label> - <div className='padding-top'> - <select - ref='channelName' - className='form-control' - value={this.state.channelId} - onChange={this.updateChannelId} - > - {options} - </select> - </div> - <div className='padding-top'> - <FormattedMessage - id='user.settings.hooks_out.only' - defaultMessage='Only public channels can be used' - /> - </div> - </div> - <div className='padding-top x2'> - <label className='control-label'> - <FormattedMessage - id='user.settings.hooks_out.trigger' - defaultMessage='Trigger Words: ' - /> - </label> - <div className='padding-top'> - <input - ref='triggerWords' - className='form-control' - value={this.state.triggerWords} - onChange={this.updateTriggerWords} - placeholder={this.props.intl.formatMessage(holders.optional)} - /> - </div> - <div className='padding-top'> - <FormattedMessage - id='user.settings.hooks_out.comma' - defaultMessage='Comma separated words to trigger on' - /> - </div> - </div> - <div className='padding-top x2'> - <label className='control-label'> - <FormattedMessage - id='user.settings.hooks_out.callback' - defaultMessage='Callback URLs: ' - /> - </label> - <div className='padding-top'> - <textarea - ref='callbackURLs' - className='form-control no-resize' - value={this.state.callbackURLs} - resize={false} - rows={3} - onChange={this.updateCallbackURLs} - placeholder={this.props.intl.formatMessage(holders.callbackHolder)} - /> - </div> - <div className='padding-top'> - <FormattedMessage - id='user.settings.hooks_out.callbackDesc' - defaultMessage='New line separated URLs that will receive the HTTP POST event' - /> - </div> - {addError} - </div> - <div className='padding-top padding-bottom'> - <a - className={'btn btn-sm btn-primary'} - href='#' - disabled={disableButton} - onClick={this.addNewHook} - > - <FormattedMessage - id='user.settings.hooks_out.add' - defaultMessage='Add' - /> - </a> - </div> - </div> - {existingHooks} - {editError} - </div> - ); - } -} - -ManageOutgoingHooks.propTypes = { - intl: intlShape.isRequired -}; - -export default injectIntl(ManageOutgoingHooks); diff --git a/web/react/components/user_settings/premade_theme_chooser.jsx b/web/react/components/user_settings/premade_theme_chooser.jsx deleted file mode 100644 index 80ff8c4de..000000000 --- a/web/react/components/user_settings/premade_theme_chooser.jsx +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import * as Utils from '../../utils/utils.jsx'; -import Constants from '../../utils/constants.jsx'; - -export default class PremadeThemeChooser extends React.Component { - constructor(props) { - super(props); - this.state = {}; - } - render() { - const theme = this.props.theme; - - const premadeThemes = []; - for (const k in Constants.THEMES) { - if (Constants.THEMES.hasOwnProperty(k)) { - const premadeTheme = $.extend(true, {}, Constants.THEMES[k]); - - let activeClass = ''; - if (premadeTheme.type === theme.type) { - activeClass = 'active'; - } - - premadeThemes.push( - <div - className='col-xs-6 col-sm-3 premade-themes' - key={'premade-theme-key' + k} - > - <div - className={activeClass} - onClick={() => this.props.updateTheme(premadeTheme)} - > - <label> - <img - className='img-responsive' - src={'/static/images/themes/' + premadeTheme.type.toLowerCase() + '.png'} - /> - <div className='theme-label'>{Utils.toTitleCase(premadeTheme.type)}</div> - </label> - </div> - </div> - ); - } - } - - return ( - <div className='row appearance-section'> - {premadeThemes} - </div> - ); - } -} - -PremadeThemeChooser.propTypes = { - theme: React.PropTypes.object.isRequired, - updateTheme: React.PropTypes.func.isRequired -}; diff --git a/web/react/components/user_settings/user_settings.jsx b/web/react/components/user_settings/user_settings.jsx deleted file mode 100644 index 4da51fa5f..000000000 --- a/web/react/components/user_settings/user_settings.jsx +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import UserStore from '../../stores/user_store.jsx'; -import * as utils from '../../utils/utils.jsx'; -import NotificationsTab from './user_settings_notifications.jsx'; -import SecurityTab from './user_settings_security.jsx'; -import GeneralTab from './user_settings_general.jsx'; -import DeveloperTab from './user_settings_developer.jsx'; -import IntegrationsTab from './user_settings_integrations.jsx'; -import DisplayTab from './user_settings_display.jsx'; -import AdvancedTab from './user_settings_advanced.jsx'; - -export default class UserSettings extends React.Component { - constructor(props) { - super(props); - - this.getActiveTab = this.getActiveTab.bind(this); - this.onListenerChange = this.onListenerChange.bind(this); - - this.state = {user: UserStore.getCurrentUser()}; - } - - componentDidMount() { - UserStore.addChangeListener(this.onListenerChange); - } - - componentWillUnmount() { - UserStore.removeChangeListener(this.onListenerChange); - } - - getActiveTab() { - return this.refs.activeTab; - } - - onListenerChange() { - var user = UserStore.getCurrentUser(); - if (!utils.areObjectsEqual(this.state.user, user)) { - this.setState({user}); - } - } - - render() { - if (this.props.activeTab === 'general') { - return ( - <div> - <GeneralTab - ref='activeTab' - user={this.state.user} - activeSection={this.props.activeSection} - updateSection={this.props.updateSection} - updateTab={this.props.updateTab} - closeModal={this.props.closeModal} - collapseModal={this.props.collapseModal} - /> - </div> - ); - } else if (this.props.activeTab === 'security') { - return ( - <div> - <SecurityTab - ref='activeTab' - user={this.state.user} - activeSection={this.props.activeSection} - updateSection={this.props.updateSection} - updateTab={this.props.updateTab} - closeModal={this.props.closeModal} - collapseModal={this.props.collapseModal} - setEnforceFocus={this.props.setEnforceFocus} - /> - </div> - ); - } else if (this.props.activeTab === 'notifications') { - return ( - <div> - <NotificationsTab - ref='activeTab' - user={this.state.user} - activeSection={this.props.activeSection} - updateSection={this.props.updateSection} - updateTab={this.props.updateTab} - closeModal={this.props.closeModal} - collapseModal={this.props.collapseModal} - /> - </div> - ); - } else if (this.props.activeTab === 'developer') { - return ( - <div> - <DeveloperTab - ref='activeTab' - activeSection={this.props.activeSection} - updateSection={this.props.updateSection} - closeModal={this.props.closeModal} - collapseModal={this.props.collapseModal} - /> - </div> - ); - } else if (this.props.activeTab === 'integrations') { - return ( - <div> - <IntegrationsTab - ref='activeTab' - user={this.state.user} - activeSection={this.props.activeSection} - updateSection={this.props.updateSection} - updateTab={this.props.updateTab} - closeModal={this.props.closeModal} - collapseModal={this.props.collapseModal} - /> - </div> - ); - } else if (this.props.activeTab === 'display') { - return ( - <div> - <DisplayTab - ref='activeTab' - user={this.state.user} - activeSection={this.props.activeSection} - updateSection={this.props.updateSection} - updateTab={this.props.updateTab} - closeModal={this.props.closeModal} - collapseModal={this.props.collapseModal} - setEnforceFocus={this.props.setEnforceFocus} - setRequireConfirm={this.props.setRequireConfirm} - /> - </div> - ); - } else if (this.props.activeTab === 'advanced') { - return ( - <div> - <AdvancedTab - ref='activeTab' - user={this.state.user} - activeSection={this.props.activeSection} - updateSection={this.props.updateSection} - updateTab={this.props.updateTab} - closeModal={this.props.closeModal} - collapseModal={this.props.collapseModal} - /> - </div> - ); - } - - return <div/>; - } -} - -UserSettings.propTypes = { - activeTab: React.PropTypes.string, - activeSection: React.PropTypes.string, - updateSection: React.PropTypes.func, - updateTab: React.PropTypes.func, - closeModal: React.PropTypes.func.isRequired, - collapseModal: React.PropTypes.func.isRequired, - setEnforceFocus: React.PropTypes.func.isRequired, - setRequireConfirm: React.PropTypes.func.isRequired -}; diff --git a/web/react/components/user_settings/user_settings_advanced.jsx b/web/react/components/user_settings/user_settings_advanced.jsx deleted file mode 100644 index cdaa5fd8a..000000000 --- a/web/react/components/user_settings/user_settings_advanced.jsx +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import * as Client from '../../utils/client.jsx'; -import SettingItemMin from '../setting_item_min.jsx'; -import SettingItemMax from '../setting_item_max.jsx'; -import Constants from '../../utils/constants.jsx'; -import PreferenceStore from '../../stores/preference_store.jsx'; - -import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl'; - -const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES; - -const holders = defineMessages({ - sendTitle: { - id: 'user.settings.advance.sendTitle', - defaultMessage: 'Send messages on Ctrl + Enter' - }, - on: { - id: 'user.settings.advance.on', - defaultMessage: 'On' - }, - off: { - id: 'user.settings.advance.off', - defaultMessage: 'Off' - }, - preReleaseTitle: { - id: 'user.settings.advance.preReleaseTitle', - defaultMessage: 'Preview pre-release features' - }, - feature: { - id: 'user.settings.advance.feature', - defaultMessage: ' Feature ' - }, - features: { - id: 'user.settings.advance.features', - defaultMessage: ' Features ' - }, - enabled: { - id: 'user.settings.advance.enabled', - defaultMessage: 'enabled' - }, - MARKDOWN_PREVIEW: { - id: 'user.settings.advance.markdown_preview', - defaultMessage: 'Show markdown preview option in message input box' - }, - EMBED_PREVIEW: { - id: 'user.settings.advance.embed_preview', - defaultMessage: 'Show preview snippet of links below message' - }, - EMBED_TOGGLE: { - id: 'user.settings.advance.embed_toggle', - defaultMessage: 'Show toggle for all embed previews' - } -}); - -class AdvancedSettingsDisplay extends React.Component { - constructor(props) { - super(props); - - this.updateSection = this.updateSection.bind(this); - this.updateSetting = this.updateSetting.bind(this); - this.toggleFeature = this.toggleFeature.bind(this); - this.saveEnabledFeatures = this.saveEnabledFeatures.bind(this); - - const preReleaseFeaturesKeys = Object.keys(PreReleaseFeatures); - const advancedSettings = PreferenceStore.getCategory(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS); - const settings = { - send_on_ctrl_enter: PreferenceStore.getPreference( - Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, - 'send_on_ctrl_enter', - {value: 'false'} - ).value - }; - - let enabledFeatures = 0; - advancedSettings.forEach((setting) => { - preReleaseFeaturesKeys.forEach((key) => { - const feature = PreReleaseFeatures[key]; - if (setting.name === Constants.FeatureTogglePrefix + feature.label) { - settings[setting.name] = setting.value; - if (setting.value === 'true') { - enabledFeatures++; - } - } - }); - }); - - this.state = {preReleaseFeatures: PreReleaseFeatures, settings, preReleaseFeaturesKeys, enabledFeatures}; - } - - updateSetting(setting, value) { - const settings = this.state.settings; - settings[setting] = value; - this.setState(settings); - } - - toggleFeature(feature, checked) { - const settings = this.state.settings; - settings[Constants.FeatureTogglePrefix + feature] = String(checked); - - let enabledFeatures = 0; - Object.keys(this.state.settings).forEach((setting) => { - if (setting.lastIndexOf(Constants.FeatureTogglePrefix) === 0 && this.state.settings[setting] === 'true') { - enabledFeatures++; - } - }); - - this.setState({settings, enabledFeatures}); - } - - saveEnabledFeatures() { - const features = []; - Object.keys(this.state.settings).forEach((setting) => { - if (setting.lastIndexOf(Constants.FeatureTogglePrefix) === 0) { - features.push(setting); - } - }); - - this.handleSubmit(features); - } - - handleSubmit(settings) { - const preferences = []; - - (Array.isArray(settings) ? settings : [settings]).forEach((setting) => { - preferences.push( - PreferenceStore.setPreference( - Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, - setting, - String(this.state.settings[setting]) - ) - ); - }); - - Client.savePreferences(preferences, - () => { - PreferenceStore.emitChange(); - this.updateSection(''); - }, - (err) => { - this.setState({serverError: err.message}); - } - ); - } - - updateSection(section) { - this.props.updateSection(section); - } - - render() { - const serverError = this.state.serverError || null; - const {formatMessage} = this.props.intl; - let ctrlSendSection; - - if (this.props.activeSection === 'advancedCtrlSend') { - const ctrlSendActive = [ - this.state.settings.send_on_ctrl_enter === 'true', - this.state.settings.send_on_ctrl_enter === 'false' - ]; - - const inputs = [ - <div key='ctrlSendSetting'> - <div className='radio'> - <label> - <input - type='radio' - checked={ctrlSendActive[0]} - onChange={this.updateSetting.bind(this, 'send_on_ctrl_enter', 'true')} - /> - <FormattedMessage - id='user.settings.advance.on' - defaultMessage='On' - /> - </label> - <br/> - </div> - <div className='radio'> - <label> - <input - type='radio' - checked={ctrlSendActive[1]} - onChange={this.updateSetting.bind(this, 'send_on_ctrl_enter', 'false')} - /> - <FormattedMessage - id='user.settings.advance.off' - defaultMessage='Off' - /> - </label> - <br/> - </div> - <div> - <br/> - <FormattedMessage - id='user.settings.advance.sendDesc' - defaultMessage="If enabled 'Enter' inserts a new line and 'Ctrl + Enter' submits the message." - /> - </div> - </div> - ]; - - ctrlSendSection = ( - <SettingItemMax - title={formatMessage(holders.sendTitle)} - inputs={inputs} - submit={() => this.handleSubmit('send_on_ctrl_enter')} - server_error={serverError} - updateSection={(e) => { - this.updateSection(''); - e.preventDefault(); - }} - /> - ); - } else { - ctrlSendSection = ( - <SettingItemMin - title={formatMessage(holders.sendTitle)} - describe={this.state.settings.send_on_ctrl_enter === 'true' ? formatMessage(holders.on) : formatMessage(holders.off)} - updateSection={() => this.props.updateSection('advancedCtrlSend')} - /> - ); - } - - let previewFeaturesSection; - let previewFeaturesSectionDivider; - if (this.state.preReleaseFeaturesKeys.length > 0) { - previewFeaturesSectionDivider = ( - <div className='divider-light'/> - ); - - if (this.props.activeSection === 'advancedPreviewFeatures') { - const inputs = []; - - this.state.preReleaseFeaturesKeys.forEach((key) => { - const feature = this.state.preReleaseFeatures[key]; - inputs.push( - <div key={'advancedPreviewFeatures_' + feature.label}> - <div className='checkbox'> - <label> - <input - type='checkbox' - checked={this.state.settings[Constants.FeatureTogglePrefix + feature.label] === 'true'} - onChange={(e) => { - this.toggleFeature(feature.label, e.target.checked); - }} - /> - {formatMessage(holders[key])} - </label> - </div> - </div> - ); - }); - - inputs.push( - <div key='advancedPreviewFeatures_helptext'> - <br/> - <FormattedMessage - id='user.settings.advance.preReleaseDesc' - defaultMessage="Check any pre-released features you'd like to preview. You may also need to refresh the page before the setting will take effect." - /> - </div> - ); - - previewFeaturesSection = ( - <SettingItemMax - title={formatMessage(holders.preReleaseTitle)} - inputs={inputs} - submit={this.saveEnabledFeatures} - server_error={serverError} - updateSection={(e) => { - this.updateSection(''); - e.preventDefault(); - }} - /> - ); - } else { - previewFeaturesSection = ( - <SettingItemMin - title={formatMessage(holders.preReleaseTitle)} - describe={this.state.enabledFeatures + (this.state.enabledFeatures === 1 ? formatMessage(holders.feature) : formatMessage(holders.features)) + formatMessage(holders.enabled)} - updateSection={() => this.props.updateSection('advancedPreviewFeatures')} - /> - ); - } - } - - return ( - <div> - <div className='modal-header'> - <button - type='button' - className='close' - data-dismiss='modal' - aria-label='Close' - onClick={this.props.closeModal} - > - <span aria-hidden='true'>{'×'}</span> - </button> - <h4 - className='modal-title' - ref='title' - > - <div className='modal-back'> - <i - className='fa fa-angle-left' - onClick={this.props.collapseModal} - /> - </div> - <FormattedMessage - id='user.settings.advance.title' - defaultMessage='Advanced Settings' - /> - </h4> - </div> - <div className='user-settings'> - <h3 className='tab-header'> - <FormattedMessage - id='user.settings.advance.title' - defaultMessage='Advanced Settings' - /> - </h3> - <div className='divider-dark first'/> - {ctrlSendSection} - {previewFeaturesSectionDivider} - {previewFeaturesSection} - <div className='divider-dark'/> - </div> - </div> - ); - } -} - -AdvancedSettingsDisplay.propTypes = { - intl: intlShape.isRequired, - user: React.PropTypes.object, - updateSection: React.PropTypes.func, - updateTab: React.PropTypes.func, - activeSection: React.PropTypes.string, - closeModal: React.PropTypes.func.isRequired, - collapseModal: React.PropTypes.func.isRequired -}; - -export default injectIntl(AdvancedSettingsDisplay); diff --git a/web/react/components/user_settings/user_settings_developer.jsx b/web/react/components/user_settings/user_settings_developer.jsx deleted file mode 100644 index 1dd564c8d..000000000 --- a/web/react/components/user_settings/user_settings_developer.jsx +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import SettingItemMin from '../setting_item_min.jsx'; -import SettingItemMax from '../setting_item_max.jsx'; -import * as GlobalActions from '../../action_creators/global_actions.jsx'; - -import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl'; - -const holders = defineMessages({ - applicationsPreview: { - id: 'user.settings.developer.applicationsPreview', - defaultMessage: 'Applications (Preview)' - }, - thirdParty: { - id: 'user.settings.developer.thirdParty', - defaultMessage: 'Open to register a new third-party application' - } -}); - -class DeveloperTab extends React.Component { - constructor(props) { - super(props); - - this.register = this.register.bind(this); - - this.state = {}; - } - register() { - this.props.closeModal(); - GlobalActions.showRegisterAppModal(); - } - render() { - var appSection; - var self = this; - const {formatMessage} = this.props.intl; - if (this.props.activeSection === 'app') { - var inputs = []; - - inputs.push( - <div - key='registerbtn' - className='form-group' - > - <div className='col-sm-7'> - <a - className='btn btn-sm btn-primary' - onClick={this.register} - > - <FormattedMessage - id='user.settings.developer.register' - defaultMessage='Register New Application' - /> - </a> - </div> - </div> - ); - - appSection = ( - <SettingItemMax - title={formatMessage(holders.applicationsPreview)} - inputs={inputs} - updateSection={function updateSection(e) { - self.props.updateSection(''); - e.preventDefault(); - }} - /> - ); - } else { - appSection = ( - <SettingItemMin - title={formatMessage(holders.applicationsPreview)} - describe={formatMessage(holders.thirdParty)} - updateSection={function updateSection() { - self.props.updateSection('app'); - }} - /> - ); - } - - return ( - <div> - <div className='modal-header'> - <button - type='button' - className='close' - data-dismiss='modal' - aria-label='Close' - onClick={this.props.closeModal} - > - <span aria-hidden='true'>{'×'}</span> - </button> - <h4 - className='modal-title' - ref='title' - > - <div className='modal-back'> - <i - className='fa fa-angle-left' - onClick={this.props.collapseModal} - /> - </div> - <FormattedMessage - id='user.settings.developer.title' - defaultMessage='Developer Settings' - /> - </h4> - </div> - <div className='user-settings'> - <h3 className='tab-header'> - <FormattedMessage - id='user.settings.developer.title' - defaultMessage='Developer Settings' - /> - </h3> - <div className='divider-dark first'/> - {appSection} - <div className='divider-dark'/> - </div> - </div> - ); - } -} - -DeveloperTab.defaultProps = { - activeSection: '' -}; -DeveloperTab.propTypes = { - intl: intlShape.isRequired, - activeSection: React.PropTypes.string, - updateSection: React.PropTypes.func, - closeModal: React.PropTypes.func.isRequired, - collapseModal: React.PropTypes.func.isRequired -}; - -export default injectIntl(DeveloperTab);
\ No newline at end of file diff --git a/web/react/components/user_settings/user_settings_display.jsx b/web/react/components/user_settings/user_settings_display.jsx deleted file mode 100644 index b0e64b0aa..000000000 --- a/web/react/components/user_settings/user_settings_display.jsx +++ /dev/null @@ -1,496 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import SettingItemMin from '../setting_item_min.jsx'; -import SettingItemMax from '../setting_item_max.jsx'; -import ManageLanguages from './manage_languages.jsx'; -import ThemeSetting from './user_settings_theme.jsx'; - -import PreferenceStore from '../../stores/preference_store.jsx'; -import * as Utils from '../../utils/utils.jsx'; - -import Constants from '../../utils/constants.jsx'; - -import {savePreferences} from '../../utils/client.jsx'; -import {FormattedMessage} from 'mm-intl'; - -function getDisplayStateFromStores() { - const militaryTime = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', {value: 'false'}); - const nameFormat = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', {value: 'username'}); - const selectedFont = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'selected_font', {value: Constants.DEFAULT_FONT}); - - return { - militaryTime: militaryTime.value, - nameFormat: nameFormat.value, - selectedFont: selectedFont.value - }; -} - -export default class UserSettingsDisplay extends React.Component { - constructor(props) { - super(props); - - this.handleSubmit = this.handleSubmit.bind(this); - this.handleClockRadio = this.handleClockRadio.bind(this); - this.handleNameRadio = this.handleNameRadio.bind(this); - this.handleFont = this.handleFont.bind(this); - this.updateSection = this.updateSection.bind(this); - this.updateState = this.updateState.bind(this); - this.deactivate = this.deactivate.bind(this); - - this.state = getDisplayStateFromStores(); - } - handleSubmit() { - const timePreference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', this.state.militaryTime); - const namePreference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', this.state.nameFormat); - const fontPreference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'selected_font', this.state.selectedFont); - - savePreferences([timePreference, namePreference, fontPreference], - () => { - PreferenceStore.emitChange(); - this.updateSection(''); - }, - (err) => { - this.setState({serverError: err.message}); - } - ); - } - handleClockRadio(militaryTime) { - this.setState({militaryTime}); - } - handleNameRadio(nameFormat) { - this.setState({nameFormat}); - } - handleFont(selectedFont) { - Utils.applyFont(selectedFont); - this.setState({selectedFont}); - } - updateSection(section) { - this.updateState(); - this.props.updateSection(section); - } - updateState() { - const newState = getDisplayStateFromStores(); - if (!Utils.areObjectsEqual(newState, this.state)) { - this.handleFont(newState.selectedFont); - this.setState(newState); - } - } - deactivate() { - this.updateState(); - } - render() { - const serverError = this.state.serverError || null; - let clockSection; - let nameFormatSection; - let fontSection; - let languagesSection; - - if (this.props.activeSection === 'clock') { - const clockFormat = [false, false]; - if (this.state.militaryTime === 'true') { - clockFormat[1] = true; - } else { - clockFormat[0] = true; - } - - const handleUpdateClockSection = (e) => { - this.updateSection(''); - e.preventDefault(); - }; - - const inputs = [ - <div key='userDisplayClockOptions'> - <div className='radio'> - <label> - <input - type='radio' - checked={clockFormat[0]} - onChange={this.handleClockRadio.bind(this, 'false')} - /> - <FormattedMessage - id='user.settings.display.normalClock' - defaultMessage='12-hour clock (example: 4:00 PM)' - /> - </label> - <br/> - </div> - <div className='radio'> - <label> - <input - type='radio' - checked={clockFormat[1]} - onChange={this.handleClockRadio.bind(this, 'true')} - /> - <FormattedMessage - id='user.settings.display.militaryClock' - defaultMessage='24-hour clock (example: 16:00)' - /> - </label> - <br/> - </div> - <div> - <br/> - <FormattedMessage - id='user.settings.display.preferTime' - defaultMessage='Select how you prefer time displayed.' - /> - </div> - </div> - ]; - - clockSection = ( - <SettingItemMax - title={ - <FormattedMessage - id='user.settings.display.clockDisplay' - defaultMessage='Clock Display' - /> - } - inputs={inputs} - submit={this.handleSubmit} - server_error={serverError} - updateSection={handleUpdateClockSection} - /> - ); - } else { - let describe; - if (this.state.militaryTime === 'true') { - describe = ( - <FormattedMessage - id='user.settings.display.militaryClock' - defaultMessage='24-hour clock (example: 16:00)' - /> - ); - } else { - describe = ( - <FormattedMessage - id='user.settings.display.normalClock' - defaultMessage='12-hour clock (example: 4:00 PM)' - /> - ); - } - - const handleUpdateClockSection = () => { - this.props.updateSection('clock'); - }; - - clockSection = ( - <SettingItemMin - title={ - <FormattedMessage - id='user.settings.display.clockDisplay' - defaultMessage='Clock Display' - /> - } - describe={describe} - updateSection={handleUpdateClockSection} - /> - ); - } - - const showUsername = ( - <FormattedMessage - id='user.settings.display.showUsername' - defaultMessage='Show username (team default)' - /> - ); - const showNickname = ( - <FormattedMessage - id='user.settings.display.showNickname' - defaultMessage='Show nickname if one exists, otherwise show first and last name' - /> - ); - const showFullName = ( - <FormattedMessage - id='user.settings.display.showFullname' - defaultMessage='Show first and last name' - /> - ); - if (this.props.activeSection === 'name_format') { - const nameFormat = [false, false, false]; - if (this.state.nameFormat === 'nickname_full_name') { - nameFormat[0] = true; - } else if (this.state.nameFormat === 'full_name') { - nameFormat[2] = true; - } else { - nameFormat[1] = true; - } - - const inputs = [ - <div key='userDisplayNameOptions'> - <div className='radio'> - <label> - <input - type='radio' - checked={nameFormat[1]} - onChange={this.handleNameRadio.bind(this, 'username')} - /> - {showUsername} - </label> - <br/> - </div> - <div className='radio'> - <label> - <input - type='radio' - checked={nameFormat[0]} - onChange={this.handleNameRadio.bind(this, 'nickname_full_name')} - /> - {showNickname} - </label> - <br/> - </div> - <div className='radio'> - <label> - <input - type='radio' - checked={nameFormat[2]} - onChange={this.handleNameRadio.bind(this, 'full_name')} - /> - {showFullName} - </label> - <br/> - </div> - <div> - <br/> - <FormattedMessage - id='user.settings.display.nameOptsDesc' - defaultMessage="Set how to display other user's names in posts and the Direct Messages list." - /> - </div> - </div> - ]; - - nameFormatSection = ( - <SettingItemMax - title={ - <FormattedMessage - id='user.settings.display.teammateDisplay' - defaultMessage='Teammate Name Display' - /> - } - inputs={inputs} - submit={this.handleSubmit} - server_error={serverError} - updateSection={(e) => { - this.updateSection(''); - e.preventDefault(); - }} - /> - ); - } else { - let describe; - if (this.state.nameFormat === 'username') { - describe = ( - <FormattedMessage - id='user.settings.display.showUsername' - defaultMessage='Show username (team default)' - /> - ); - } else if (this.state.nameFormat === 'full_name') { - describe = ( - <FormattedMessage - id='user.settings.display.showFullname' - defaultMessage='Show first and last name' - /> - ); - } else { - describe = ( - <FormattedMessage - id='user.settings.display.showNickname' - defaultMessage='Show nickname if one exists, otherwise show first and last name' - /> - ); - } - - nameFormatSection = ( - <SettingItemMin - title={ - <FormattedMessage - id='user.settings.display.teammateDisplay' - defaultMessage='Teammate Name Display' - /> - } - describe={describe} - updateSection={() => { - this.props.updateSection('name_format'); - }} - /> - ); - } - - if (this.props.activeSection === 'font') { - const options = []; - Object.keys(Constants.FONTS).forEach((fontName, idx) => { - const className = Constants.FONTS[fontName]; - options.push( - <option - key={'font_' + idx} - value={fontName} - className={className} - > - {fontName} - </option> - ); - }); - - const inputs = [ - <div key='userDisplayNameOptions'> - <div - className='dropdown' - > - <select - className='form-control' - type='text' - value={this.state.selectedFont} - onChange={(e) => this.handleFont(e.target.value)} - > - {options} - </select> - </div> - <div> - <br/> - <FormattedMessage - id='user.settings.display.fontDesc' - defaultMessage='Select the font displayed in the Mattermost user interface.' - /> - </div> - </div> - ]; - - fontSection = ( - <SettingItemMax - title={ - <FormattedMessage - id='user.settings.display.fontTitle' - defaultMessage='Display Font' - /> - } - inputs={inputs} - submit={this.handleSubmit} - server_error={serverError} - updateSection={(e) => { - this.updateSection(''); - e.preventDefault(); - }} - /> - ); - } else { - fontSection = ( - <SettingItemMin - title={ - <FormattedMessage - id='user.settings.display.fontTitle' - defaultMessage='Display Font' - /> - } - describe={this.state.selectedFont} - updateSection={() => { - this.props.updateSection('font'); - }} - /> - ); - } - - if (this.props.activeSection === 'languages') { - languagesSection = ( - <ManageLanguages - user={this.props.user} - updateSection={(e) => { - this.updateSection(''); - e.preventDefault(); - }} - /> - ); - } else { - var locale = 'English'; - Utils.languages().forEach((l) => { - if (l.value === this.props.user.locale) { - locale = l.name; - } - }); - - languagesSection = ( - <SettingItemMin - title={ - <FormattedMessage - id='user.settings.display.language' - defaultMessage='Language' - /> - } - width='medium' - describe={locale} - updateSection={() => { - this.updateSection('languages'); - }} - /> - ); - } - - return ( - <div> - <div className='modal-header'> - <button - type='button' - className='close' - data-dismiss='modal' - aria-label='Close' - onClick={this.props.closeModal} - > - <span aria-hidden='true'>{'×'}</span> - </button> - <h4 - className='modal-title' - ref='title' - > - <div className='modal-back'> - <i - className='fa fa-angle-left' - onClick={this.props.collapseModal} - /> - </div> - <FormattedMessage - id='user.settings.display.title' - defaultMessage='Display Settings' - /> - </h4> - </div> - <div className='user-settings'> - <h3 className='tab-header'> - <FormattedMessage - id='user.settings.display.title' - defaultMessage='Display Settings' - /> - </h3> - <div className='divider-dark first'/> - <ThemeSetting - selected={this.props.activeSection === 'theme'} - updateSection={this.updateSection} - setRequireConfirm={this.props.setRequireConfirm} - setEnforceFocus={this.props.setEnforceFocus} - /> - <div className='divider-dark'/> - {fontSection} - <div className='divider-dark'/> - {clockSection} - <div className='divider-dark'/> - {nameFormatSection} - <div className='divider-dark'/> - {languagesSection} - </div> - </div> - ); - } -} - -UserSettingsDisplay.propTypes = { - user: React.PropTypes.object, - updateSection: React.PropTypes.func, - updateTab: React.PropTypes.func, - activeSection: React.PropTypes.string, - closeModal: React.PropTypes.func.isRequired, - collapseModal: React.PropTypes.func.isRequired, - setRequireConfirm: React.PropTypes.func.isRequired, - setEnforceFocus: React.PropTypes.func.isRequired -}; diff --git a/web/react/components/user_settings/user_settings_general.jsx b/web/react/components/user_settings/user_settings_general.jsx deleted file mode 100644 index 235892819..000000000 --- a/web/react/components/user_settings/user_settings_general.jsx +++ /dev/null @@ -1,815 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import SettingItemMin from '../setting_item_min.jsx'; -import SettingItemMax from '../setting_item_max.jsx'; -import SettingPicture from '../setting_picture.jsx'; - -import UserStore from '../../stores/user_store.jsx'; -import ErrorStore from '../../stores/error_store.jsx'; - -import * as Client from '../../utils/client.jsx'; -import Constants from '../../utils/constants.jsx'; -import * as AsyncClient from '../../utils/async_client.jsx'; -import * as Utils from '../../utils/utils.jsx'; - -import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedDate} from 'mm-intl'; - -const holders = defineMessages({ - usernameReserved: { - id: 'user.settings.general.usernameReserved', - defaultMessage: 'This username is reserved, please choose a new one.' - }, - usernameRestrictions: { - id: 'user.settings.general.usernameRestrictions', - defaultMessage: "Username must begin with a letter, and contain between {min} to {max} lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'." - }, - validEmail: { - id: 'user.settings.general.validEmail', - defaultMessage: 'Please enter a valid email address' - }, - emailMatch: { - id: 'user.settings.general.emailMatch', - defaultMessage: 'The new emails you entered do not match.' - }, - checkEmail: { - id: 'user.settings.general.checkEmail', - defaultMessage: 'Check your email at {email} to verify the address.' - }, - newAddress: { - id: 'user.settings.general.newAddress', - defaultMessage: 'New Address: {email}<br />Check your email to verify the above address.' - }, - checkEmailNoAddress: { - id: 'user.settings.general.checkEmailNoAddress', - defaultMessage: 'Check your email to verify your new address' - }, - loginGitlab: { - id: 'user.settings.general.loginGitlab', - defaultMessage: 'Log in done through GitLab' - }, - validImage: { - id: 'user.settings.general.validImage', - defaultMessage: 'Only JPG or PNG images may be used for profile pictures' - }, - imageTooLarge: { - id: 'user.settings.general.imageTooLarge', - defaultMessage: 'Unable to upload profile image. File is too large.' - }, - uploadImage: { - id: 'user.settings.general.uploadImage', - defaultMessage: "Click 'Edit' to upload an image." - }, - imageUpdated: { - id: 'user.settings.general.imageUpdated', - defaultMessage: 'Image last updated {date}' - }, - fullName: { - id: 'user.settings.general.fullName', - defaultMessage: 'Full Name' - }, - nickname: { - id: 'user.settings.general.nickname', - defaultMessage: 'Nickname' - }, - username: { - id: 'user.settings.general.username', - defaultMessage: 'Username' - }, - email: { - id: 'user.settings.general.email', - defaultMessage: 'Email' - }, - profilePicture: { - id: 'user.settings.general.profilePicture', - defaultMessage: 'Profile Picture' - }, - close: { - id: 'user.settings.general.close', - defaultMessage: 'Close' - } -}); - -class UserSettingsGeneralTab extends React.Component { - constructor(props) { - super(props); - this.submitActive = false; - - this.submitUsername = this.submitUsername.bind(this); - this.submitNickname = this.submitNickname.bind(this); - this.submitName = this.submitName.bind(this); - this.submitEmail = this.submitEmail.bind(this); - this.submitUser = this.submitUser.bind(this); - this.submitPicture = this.submitPicture.bind(this); - - this.updateUsername = this.updateUsername.bind(this); - this.updateFirstName = this.updateFirstName.bind(this); - this.updateLastName = this.updateLastName.bind(this); - this.updateNickname = this.updateNickname.bind(this); - this.updateEmail = this.updateEmail.bind(this); - this.updateConfirmEmail = this.updateConfirmEmail.bind(this); - this.updatePicture = this.updatePicture.bind(this); - this.updateSection = this.updateSection.bind(this); - - this.state = this.setupInitialState(props); - } - submitUsername(e) { - e.preventDefault(); - - const user = Object.assign({}, this.props.user); - const username = this.state.username.trim().toLowerCase(); - - const {formatMessage} = this.props.intl; - const usernameError = Utils.isValidUsername(username); - if (usernameError === 'Cannot use a reserved word as a username.') { - this.setState({clientError: formatMessage(holders.usernameReserved)}); - return; - } else if (usernameError) { - this.setState({clientError: formatMessage(holders.usernameRestrictions, {min: Constants.MIN_USERNAME_LENGTH, max: Constants.MAX_USERNAME_LENGTH})}); - return; - } - - if (user.username === username) { - this.updateSection(''); - return; - } - - user.username = username; - - this.submitUser(user, false); - } - submitNickname(e) { - e.preventDefault(); - - const user = Object.assign({}, this.props.user); - const nickname = this.state.nickname.trim(); - - if (user.nickname === nickname) { - this.updateSection(''); - return; - } - - user.nickname = nickname; - - this.submitUser(user, false); - } - submitName(e) { - e.preventDefault(); - - const user = Object.assign({}, this.props.user); - const firstName = this.state.firstName.trim(); - const lastName = this.state.lastName.trim(); - - if (user.first_name === firstName && user.last_name === lastName) { - this.updateSection(''); - return; - } - - user.first_name = firstName; - user.last_name = lastName; - - this.submitUser(user, false); - } - submitEmail(e) { - e.preventDefault(); - - const user = Object.assign({}, this.props.user); - const email = this.state.email.trim().toLowerCase(); - const confirmEmail = this.state.confirmEmail.trim().toLowerCase(); - - const {formatMessage} = this.props.intl; - if (email === '' || !Utils.isEmail(email)) { - this.setState({emailError: formatMessage(holders.validEmail), clientError: '', serverError: ''}); - return; - } - - if (email !== confirmEmail) { - this.setState({emailError: formatMessage(holders.emailMatch), clientError: '', serverError: ''}); - return; - } - - if (user.email === email) { - this.updateSection(''); - return; - } - - user.email = email; - this.submitUser(user, true); - } - submitUser(user, emailUpdated) { - Client.updateUser(user, - () => { - this.updateSection(''); - AsyncClient.getMe(); - const verificationEnabled = global.window.mm_config.SendEmailNotifications === 'true' && global.window.mm_config.RequireEmailVerification === 'true' && emailUpdated; - - if (verificationEnabled) { - ErrorStore.storeLastError({message: this.props.intl.formatMessage(holders.checkEmail, {email: user.email})}); - ErrorStore.emitChange(); - this.setState({emailChangeInProgress: true}); - } - }, - (err) => { - let serverError; - if (err.message) { - serverError = err.message; - } else { - serverError = err; - } - this.setState({serverError, emailError: '', clientError: ''}); - } - ); - } - submitPicture(e) { - e.preventDefault(); - - if (!this.state.picture) { - return; - } - - if (!this.submitActive) { - return; - } - - const {formatMessage} = this.props.intl; - const picture = this.state.picture; - - if (picture.type !== 'image/jpeg' && picture.type !== 'image/png') { - this.setState({clientError: formatMessage(holders.validImage)}); - return; - } else if (picture.size > Constants.MAX_FILE_SIZE) { - this.setState({clientError: formatMessage(holders.imageTooLarge)}); - return; - } - - var formData = new FormData(); - formData.append('image', picture, picture.name); - this.setState({loadingPicture: true}); - - Client.uploadProfileImage(formData, - () => { - this.submitActive = false; - AsyncClient.getMe(); - window.location.reload(); - }, - (err) => { - var state = this.setupInitialState(this.props); - state.serverError = err.message; - this.setState(state); - } - ); - } - updateUsername(e) { - this.setState({username: e.target.value}); - } - updateFirstName(e) { - this.setState({firstName: e.target.value}); - } - updateLastName(e) { - this.setState({lastName: e.target.value}); - } - updateNickname(e) { - this.setState({nickname: e.target.value}); - } - updateEmail(e) { - this.setState({email: e.target.value}); - } - updateConfirmEmail(e) { - this.setState({confirmEmail: e.target.value}); - } - updatePicture(e) { - if (e.target.files && e.target.files[0]) { - this.setState({picture: e.target.files[0]}); - - this.submitActive = true; - this.setState({clientError: null}); - } else { - this.setState({picture: null}); - } - } - updateSection(section) { - const emailChangeInProgress = this.state.emailChangeInProgress; - this.setState(Object.assign({}, this.setupInitialState(this.props), {emailChangeInProgress, clientError: '', serverError: '', emailError: ''})); - this.submitActive = false; - this.props.updateSection(section); - } - setupInitialState(props) { - const user = props.user; - - return {username: user.username, firstName: user.first_name, lastName: user.last_name, nickname: user.nickname, - email: user.email, confirmEmail: '', picture: null, loadingPicture: false, emailChangeInProgress: false}; - } - render() { - const user = this.props.user; - const {formatMessage, formatHTMLMessage} = this.props.intl; - - let clientError = null; - if (this.state.clientError) { - clientError = this.state.clientError; - } - let serverError = null; - if (this.state.serverError) { - serverError = this.state.serverError; - } - let emailError = null; - if (this.state.emailError) { - emailError = this.state.emailError; - } - - let nameSection; - const inputs = []; - - if (this.props.activeSection === 'name') { - inputs.push( - <div - key='firstNameSetting' - className='form-group' - > - <label className='col-sm-5 control-label'> - <FormattedMessage - id='user.settings.general.firstName' - defaultMessage='First Name' - /> - </label> - <div className='col-sm-7'> - <input - className='form-control' - type='text' - onChange={this.updateFirstName} - value={this.state.firstName} - /> - </div> - </div> - ); - - inputs.push( - <div - key='lastNameSetting' - className='form-group' - > - <label className='col-sm-5 control-label'> - <FormattedMessage - id='user.settings.general.lastName' - defaultMessage='Last Name' - /> - </label> - <div className='col-sm-7'> - <input - className='form-control' - type='text' - onChange={this.updateLastName} - value={this.state.lastName} - /> - </div> - </div> - ); - - function notifClick(e) { - e.preventDefault(); - this.updateSection(''); - this.props.updateTab('notifications'); - } - - const notifLink = ( - <a - href='#' - onClick={notifClick.bind(this)} - > - <FormattedMessage - id='user.settings.general.notificationsLink' - defaultMessage='Notifications' - /> - </a> - ); - - const extraInfo = ( - <span> - <FormattedMessage - id='user.settings.general.notificationsExtra' - defaultMessage='By default, you will receive mention notifications when someone types your first name. Go to {notify} settings to change this default.' - values={{ - notify: (notifLink) - }} - /> - </span> - ); - - nameSection = ( - <SettingItemMax - title={formatMessage(holders.fullName)} - inputs={inputs} - submit={this.submitName} - server_error={serverError} - client_error={clientError} - updateSection={(e) => { - this.updateSection(''); - e.preventDefault(); - }} - extraInfo={extraInfo} - /> - ); - } else { - let fullName = ''; - - if (user.first_name && user.last_name) { - fullName = user.first_name + ' ' + user.last_name; - } else if (user.first_name) { - fullName = user.first_name; - } else if (user.last_name) { - fullName = user.last_name; - } - - nameSection = ( - <SettingItemMin - title={formatMessage(holders.fullName)} - describe={fullName} - updateSection={() => { - this.updateSection('name'); - }} - /> - ); - } - - let nicknameSection; - if (this.props.activeSection === 'nickname') { - let nicknameLabel = ( - <FormattedMessage - id='user.settings.general.nickname' - defaultMessage='Nickname' - /> - ); - if (Utils.isMobile()) { - nicknameLabel = ''; - } - - inputs.push( - <div - key='nicknameSetting' - className='form-group' - > - <label className='col-sm-5 control-label'>{nicknameLabel}</label> - <div className='col-sm-7'> - <input - className='form-control' - type='text' - onChange={this.updateNickname} - value={this.state.nickname} - /> - </div> - </div> - ); - - const extraInfo = ( - <span> - <FormattedMessage - id='user.settings.general.nicknameExtra' - defaultMessage='Use Nickname for a name you might be called that is different from your first name and username. This is most often used when two or more people have similar sounding names and usernames.' - /> - </span> - ); - - nicknameSection = ( - <SettingItemMax - title={formatMessage(holders.nickname)} - inputs={inputs} - submit={this.submitNickname} - server_error={serverError} - client_error={clientError} - updateSection={(e) => { - this.updateSection(''); - e.preventDefault(); - }} - extraInfo={extraInfo} - /> - ); - } else { - nicknameSection = ( - <SettingItemMin - title={formatMessage(holders.nickname)} - describe={UserStore.getCurrentUser().nickname} - updateSection={() => { - this.updateSection('nickname'); - }} - /> - ); - } - - let usernameSection; - if (this.props.activeSection === 'username') { - let usernameLabel = ( - <FormattedMessage - id='user.settings.general.username' - defaultMessage='Username' - /> - ); - if (Utils.isMobile()) { - usernameLabel = ''; - } - - inputs.push( - <div - key='usernameSetting' - className='form-group' - > - <label className='col-sm-5 control-label'>{usernameLabel}</label> - <div className='col-sm-7'> - <input - maxLength={Constants.MAX_USERNAME_LENGTH} - className='form-control' - type='text' - onChange={this.updateUsername} - value={this.state.username} - /> - </div> - </div> - ); - - const extraInfo = ( - <span> - <FormattedMessage - id='user.settings.general.usernameInfo' - defaultMessage='Pick something easy for teammates to recognize and recall.' - /> - </span> - ); - - usernameSection = ( - <SettingItemMax - title={formatMessage(holders.username)} - inputs={inputs} - submit={this.submitUsername} - server_error={serverError} - client_error={clientError} - updateSection={(e) => { - this.updateSection(''); - e.preventDefault(); - }} - extraInfo={extraInfo} - /> - ); - } else { - usernameSection = ( - <SettingItemMin - title={formatMessage(holders.username)} - describe={UserStore.getCurrentUser().username} - updateSection={() => { - this.updateSection('username'); - }} - /> - ); - } - - let emailSection; - if (this.props.activeSection === 'email') { - const emailEnabled = global.window.mm_config.SendEmailNotifications === 'true'; - const emailVerificationEnabled = global.window.mm_config.RequireEmailVerification === 'true'; - let helpText = ( - <FormattedMessage - id='user.settings.general.emailHelp1' - defaultMessage='Email is used for sign-in, notifications, and password reset. Email requires verification if changed.' - /> - ); - - if (!emailEnabled) { - helpText = ( - <div className='setting-list__hint text-danger'> - <FormattedMessage - id='user.settings.general.emailHelp2' - defaultMessage='Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.' - /> - </div> - ); - } else if (!emailVerificationEnabled) { - helpText = ( - <FormattedMessage - id='user.settings.general.emailHelp3' - defaultMessage='Email is used for sign-in, notifications, and password reset.' - /> - ); - } else if (this.state.emailChangeInProgress) { - const newEmail = UserStore.getCurrentUser().email; - if (newEmail) { - helpText = ( - <FormattedMessage - id='user.settings.general.emailHelp4' - defaultMessage='A verification email was sent to {email}.' - values={{ - email: newEmail - }} - /> - ); - } - } - - let submit = null; - - if (this.props.user.auth_service === '') { - inputs.push( - <div key='emailSetting'> - <div className='form-group'> - <label className='col-sm-5 control-label'> - <FormattedMessage - id='user.settings.general.primaryEmail' - defaultMessage='Primary Email' - /> - </label> - <div className='col-sm-7'> - <input - className='form-control' - type='text' - onChange={this.updateEmail} - value={this.state.email} - /> - </div> - </div> - </div> - ); - - inputs.push( - <div key='confirmEmailSetting'> - <div className='form-group'> - <label className='col-sm-5 control-label'> - <FormattedMessage - id='user.settings.general.confirmEmail' - defaultMessage='Confirm Email' - /> - </label> - <div className='col-sm-7'> - <input - className='form-control' - type='text' - onChange={this.updateConfirmEmail} - value={this.state.confirmEmail} - /> - </div> - </div> - {helpText} - </div> - ); - - submit = this.submitEmail; - } else if (this.props.user.auth_service === Constants.GITLAB_SERVICE) { - inputs.push( - <div - key='oauthEmailInfo' - className='form-group' - > - <div className='setting-list__hint'> - <FormattedMessage - id='user.settings.general.emailCantUpdate' - defaultMessage='Log in occurs through GitLab. Email cannot be updated.' - /> - </div> - {helpText} - </div> - ); - } - - emailSection = ( - <SettingItemMax - title='Email' - inputs={inputs} - submit={submit} - server_error={serverError} - client_error={emailError} - updateSection={(e) => { - this.updateSection(''); - e.preventDefault(); - }} - /> - ); - } else { - let describe = ''; - if (this.props.user.auth_service === '') { - if (this.state.emailChangeInProgress) { - const newEmail = UserStore.getCurrentUser().email; - if (newEmail) { - describe = formatHTMLMessage(holders.newAddress, {email: newEmail}); - } else { - describe = formatMessage(holders.checkEmailNoAddress); - } - } else { - describe = UserStore.getCurrentUser().email; - } - } else if (this.props.user.auth_service === Constants.GITLAB_SERVICE) { - describe = formatMessage(holders.loginGitlab); - } - - emailSection = ( - <SettingItemMin - title={formatMessage(holders.email)} - describe={describe} - updateSection={() => { - this.updateSection('email'); - }} - /> - ); - } - - let pictureSection; - if (this.props.activeSection === 'picture') { - pictureSection = ( - <SettingPicture - title={formatMessage(holders.profilePicture)} - submit={this.submitPicture} - src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update} - server_error={serverError} - client_error={clientError} - updateSection={(e) => { - this.updateSection(''); - e.preventDefault(); - }} - picture={this.state.picture} - pictureChange={this.updatePicture} - submitActive={this.submitActive} - loadingPicture={this.state.loadingPicture} - /> - ); - } else { - let minMessage = formatMessage(holders.uploadImage); - if (user.last_picture_update) { - minMessage = formatMessage(holders.imageUpdated, { - date: ( - <FormattedDate - value={new Date(user.last_picture_update)} - day='2-digit' - month='short' - year='numeric' - /> - ) - }); - } - pictureSection = ( - <SettingItemMin - title={formatMessage(holders.profilePicture)} - describe={minMessage} - updateSection={() => { - this.updateSection('picture'); - }} - /> - ); - } - - return ( - <div> - <div className='modal-header'> - <button - type='button' - className='close' - data-dismiss='modal' - aria-label={formatMessage(holders.close)} - onClick={this.props.closeModal} - > - <span aria-hidden='true'>{'×'}</span> - </button> - <h4 - className='modal-title' - ref='title' - > - <div className='modal-back'> - <i - className='fa fa-angle-left' - onClick={this.props.collapseModal} - /> - </div> - <FormattedMessage - id='user.settings.general.title' - defaultMessage='General Settings' - /> - </h4> - </div> - <div className='user-settings'> - <h3 className='tab-header'> - <FormattedMessage - id='user.settings.general.title' - defaultMessage='General Settings' - /> - </h3> - <div className='divider-dark first'/> - {nameSection} - <div className='divider-light'/> - {usernameSection} - <div className='divider-light'/> - {nicknameSection} - <div className='divider-light'/> - {emailSection} - <div className='divider-light'/> - {pictureSection} - <div className='divider-dark'/> - </div> - </div> - ); - } -} - -UserSettingsGeneralTab.propTypes = { - intl: intlShape.isRequired, - user: React.PropTypes.object.isRequired, - updateSection: React.PropTypes.func.isRequired, - updateTab: React.PropTypes.func.isRequired, - activeSection: React.PropTypes.string.isRequired, - closeModal: React.PropTypes.func.isRequired, - collapseModal: React.PropTypes.func.isRequired -}; - -export default injectIntl(UserSettingsGeneralTab); diff --git a/web/react/components/user_settings/user_settings_integrations.jsx b/web/react/components/user_settings/user_settings_integrations.jsx deleted file mode 100644 index 7633b2f95..000000000 --- a/web/react/components/user_settings/user_settings_integrations.jsx +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -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'; - -const holders = defineMessages({ - inName: { - id: 'user.settings.integrations.incomingWebhooks', - defaultMessage: 'Incoming Webhooks' - }, - inDesc: { - id: 'user.settings.integrations.incomingWebhooksDescription', - defaultMessage: 'Manage your incoming webhooks' - }, - outName: { - id: 'user.settings.integrations.outWebhooks', - defaultMessage: 'Outgoing Webhooks' - }, - outDesc: { - id: 'user.settings.integrations.outWebhooksDescription', - defaultMessage: 'Manage your outgoing webhooks' - }, - cmdName: { - id: 'user.settings.integrations.commands', - defaultMessage: 'Slash Commands' - }, - cmdDesc: { - id: 'user.settings.integrations.commandsDescription', - defaultMessage: 'Manage your slash commands' - } -}); - -class UserSettingsIntegrationsTab extends React.Component { - constructor(props) { - super(props); - - this.updateSection = this.updateSection.bind(this); - - this.state = {}; - } - updateSection(section) { - this.props.updateSection(section); - } - render() { - let incomingHooksSection; - let outgoingHooksSection; - let commandHooksSection; - var inputs = []; - const {formatMessage} = this.props.intl; - - if (global.window.mm_config.EnableIncomingWebhooks === 'true') { - if (this.props.activeSection === 'incoming-hooks') { - inputs.push( - <ManageIncomingHooks key='incoming-hook-ui'/> - ); - - incomingHooksSection = ( - <SettingItemMax - title={formatMessage(holders.inName)} - width='medium' - inputs={inputs} - updateSection={(e) => { - this.updateSection(''); - e.preventDefault(); - }} - /> - ); - } else { - incomingHooksSection = ( - <SettingItemMin - title={formatMessage(holders.inName)} - width='medium' - describe={formatMessage(holders.inDesc)} - updateSection={() => { - this.updateSection('incoming-hooks'); - }} - /> - ); - } - } - - if (global.window.mm_config.EnableOutgoingWebhooks === 'true') { - if (this.props.activeSection === 'outgoing-hooks') { - inputs.push( - <ManageOutgoingHooks key='outgoing-hook-ui'/> - ); - - outgoingHooksSection = ( - <SettingItemMax - title={formatMessage(holders.outName)} - width='medium' - inputs={inputs} - updateSection={(e) => { - this.updateSection(''); - e.preventDefault(); - }} - /> - ); - } else { - outgoingHooksSection = ( - <SettingItemMin - title={formatMessage(holders.outName)} - width='medium' - describe={formatMessage(holders.outDesc)} - updateSection={() => { - this.updateSection('outgoing-hooks'); - }} - /> - ); - } - } - - 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'> - <button - type='button' - className='close' - data-dismiss='modal' - aria-label='Close' - onClick={this.props.closeModal} - > - <span aria-hidden='true'>{'×'}</span> - </button> - <h4 - className='modal-title' - ref='title' - > - <div className='modal-back'> - <i - className='fa fa-angle-left' - onClick={this.props.collapseModal} - /> - </div> - <FormattedMessage - id='user.settings.integrations.title' - defaultMessage='Integration Settings' - /> - </h4> - </div> - <div className='user-settings'> - <h3 className='tab-header'> - <FormattedMessage - id='user.settings.integrations.title' - defaultMessage='Integration Settings' - /> - </h3> - <div className='divider-dark first'/> - {incomingHooksSection} - <div className='divider-light'/> - {outgoingHooksSection} - <div className='divider-dark'/> - {commandHooksSection} - <div className='divider-dark'/> - </div> - </div> - ); - } -} - -UserSettingsIntegrationsTab.propTypes = { - intl: intlShape.isRequired, - user: React.PropTypes.object, - updateSection: React.PropTypes.func, - updateTab: React.PropTypes.func, - activeSection: React.PropTypes.string, - closeModal: React.PropTypes.func.isRequired, - collapseModal: React.PropTypes.func.isRequired -}; - -export default injectIntl(UserSettingsIntegrationsTab);
\ No newline at end of file diff --git a/web/react/components/user_settings/user_settings_modal.jsx b/web/react/components/user_settings/user_settings_modal.jsx deleted file mode 100644 index 0c4a3d526..000000000 --- a/web/react/components/user_settings/user_settings_modal.jsx +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import ConfirmModal from '../confirm_modal.jsx'; -import UserSettings from './user_settings.jsx'; -import SettingsSidebar from '../settings_sidebar.jsx'; - -import UserStore from '../../stores/user_store.jsx'; -import * as Utils from '../../utils/utils.jsx'; -import Constants from '../../utils/constants.jsx'; - -const Modal = ReactBootstrap.Modal; - -import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl'; - -const holders = defineMessages({ - general: { - id: 'user.settings.modal.general', - defaultMessage: 'General' - }, - security: { - id: 'user.settings.modal.security', - defaultMessage: 'Security' - }, - notifications: { - id: 'user.settings.modal.notifications', - defaultMessage: 'Notifications' - }, - developer: { - id: 'user.settings.modal.developer', - defaultMessage: 'Developer' - }, - integrations: { - id: 'user.settings.modal.integrations', - defaultMessage: 'Integrations' - }, - display: { - id: 'user.settings.modal.display', - defaultMessage: 'Display' - }, - advanced: { - id: 'user.settings.modal.advanced', - defaultMessage: 'Advanced' - }, - confirmTitle: { - id: 'user.settings.modal.confirmTitle', - defaultMessage: 'Discard Changes?' - }, - confirmMsg: { - id: 'user.settings.modal.confirmMsg', - defaultMessage: 'You have unsaved changes, are you sure you want to discard them?' - }, - confirmBtns: { - id: 'user.settings.modal.confirmBtns', - defaultMessage: 'Yes, Discard' - } -}); - -class UserSettingsModal extends React.Component { - constructor(props) { - super(props); - - this.handleShow = this.handleShow.bind(this); - this.handleHide = this.handleHide.bind(this); - this.handleHidden = this.handleHidden.bind(this); - this.handleCollapse = this.handleCollapse.bind(this); - this.handleConfirm = this.handleConfirm.bind(this); - this.handleCancelConfirmation = this.handleCancelConfirmation.bind(this); - - this.deactivateTab = this.deactivateTab.bind(this); - this.closeModal = this.closeModal.bind(this); - this.collapseModal = this.collapseModal.bind(this); - - this.updateTab = this.updateTab.bind(this); - this.updateSection = this.updateSection.bind(this); - this.onUserChanged = this.onUserChanged.bind(this); - - this.state = { - active_tab: 'general', - active_section: '', - showConfirmModal: false, - enforceFocus: true, - currentUser: UserStore.getCurrentUser() - }; - - this.requireConfirm = false; - } - - onUserChanged() { - this.setState({currentUser: UserStore.getCurrentUser()}); - } - - componentDidMount() { - if (this.props.show) { - this.handleShow(); - } - UserStore.addChangeListener(this.onUserChanged); - } - - componentDidUpdate(prevProps) { - if (this.props.show && !prevProps.show) { - this.handleShow(); - } - UserStore.removeChangeListener(this.onUserChanged); - } - - handleShow() { - if ($(window).width() > 768) { - $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar(); - $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200); - } else { - $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 50); - } - } - - // Called when the close button is pressed on the main modal - handleHide() { - if (this.requireConfirm) { - this.afterConfirm = () => this.handleHide(); - this.showConfirmModal(); - - return; - } - - this.resetTheme(); - this.deactivateTab(); - this.props.onModalDismissed(); - return; - } - - // called after the dialog is fully hidden and faded out - handleHidden() { - this.setState({ - active_tab: 'general', - active_section: '' - }); - } - - // Called to hide the settings pane when on mobile - handleCollapse() { - $(ReactDOM.findDOMNode(this.refs.modalBody)).closest('.modal-dialog').removeClass('display--content'); - - this.deactivateTab(); - - this.setState({ - active_tab: '', - active_section: '' - }); - } - - handleConfirm() { - this.setState({ - showConfirmModal: false, - enforceFocus: true - }); - - this.requireConfirm = false; - - if (this.afterConfirm) { - this.afterConfirm(); - this.afterConfirm = null; - } - } - - handleCancelConfirmation() { - this.setState({ - showConfirmModal: false, - enforceFocus: true - }); - - this.afterConfirm = null; - } - - showConfirmModal(afterConfirm) { - this.setState({ - showConfirmModal: true, - enforceFocus: false - }); - - if (afterConfirm) { - this.afterConfirm = afterConfirm; - } - } - - // Called to let settings tab perform cleanup before being closed - deactivateTab() { - const activeTab = this.refs.userSettings.getActiveTab(); - if (activeTab && activeTab.deactivate) { - activeTab.deactivate(); - } - } - - // Called by settings tabs when their close button is pressed - closeModal() { - if (this.requireConfirm) { - this.showConfirmModal(this.closeModal); - } else { - this.handleHide(); - } - } - - // Called by settings tabs when their back button is pressed - collapseModal() { - if (this.requireConfirm) { - this.showConfirmModal(this.collapseModal); - } else { - this.handleCollapse(); - } - } - - updateTab(tab, skipConfirm) { - if (!skipConfirm && this.requireConfirm) { - this.showConfirmModal(() => this.updateTab(tab, true)); - } else { - this.deactivateTab(); - - this.setState({ - active_tab: tab, - active_section: '' - }); - } - } - - updateSection(section, skipConfirm) { - if (!skipConfirm && this.requireConfirm) { - this.showConfirmModal(() => this.updateSection(section, true)); - } else { - if (this.state.active_section === 'theme' && section !== 'theme') { - this.resetTheme(); - } - this.setState({active_section: section}); - } - } - - resetTheme() { - const user = UserStore.getCurrentUser(); - if (user.theme_props == null) { - Utils.applyTheme(Constants.THEMES.default); - } else { - Utils.applyTheme(user.theme_props); - } - } - - render() { - const {formatMessage} = this.props.intl; - if (this.state.currentUser == null) { - return (<div/>); - } - var isAdmin = Utils.isAdmin(this.state.currentUser.roles); - var tabs = []; - - tabs.push({name: 'general', uiName: formatMessage(holders.general), icon: 'glyphicon glyphicon-cog'}); - tabs.push({name: 'security', uiName: formatMessage(holders.security), icon: 'glyphicon glyphicon-lock'}); - tabs.push({name: 'notifications', uiName: formatMessage(holders.notifications), icon: 'glyphicon glyphicon-exclamation-sign'}); - if (global.window.mm_config.EnableOAuthServiceProvider === 'true') { - tabs.push({name: 'developer', uiName: formatMessage(holders.developer), icon: 'glyphicon glyphicon-th'}); - } - - if (global.window.mm_config.EnableIncomingWebhooks === 'true' || global.window.mm_config.EnableOutgoingWebhooks === 'true' || global.window.mm_config.EnableCommands === 'true') { - var show = global.window.mm_config.EnableOnlyAdminIntegrations !== 'true'; - - if (global.window.mm_config.EnableOnlyAdminIntegrations === 'true' && isAdmin) { - show = true; - } - - if (show) { - tabs.push({name: 'integrations', uiName: formatMessage(holders.integrations), icon: 'glyphicon glyphicon-transfer'}); - } - } - - tabs.push({name: 'display', uiName: formatMessage(holders.display), icon: 'glyphicon glyphicon-eye-open'}); - tabs.push({name: 'advanced', uiName: formatMessage(holders.advanced), icon: 'glyphicon glyphicon-list-alt'}); - - return ( - <Modal - dialogClassName='settings-modal' - show={this.props.show} - onHide={this.handleHide} - onExited={this.handleHidden} - enforceFocus={this.state.enforceFocus} - > - <Modal.Header closeButton={true}> - <Modal.Title> - <FormattedMessage - id='user.settings.modal.title' - defaultMessage='Account Settings' - /> - </Modal.Title> - </Modal.Header> - <Modal.Body ref='modalBody'> - <div className='settings-table'> - <div className='settings-links'> - <SettingsSidebar - tabs={tabs} - activeTab={this.state.active_tab} - updateTab={this.updateTab} - /> - </div> - <div className='settings-content minimize-settings'> - <UserSettings - ref='userSettings' - activeTab={this.state.active_tab} - activeSection={this.state.active_section} - updateSection={this.updateSection} - updateTab={this.updateTab} - closeModal={this.closeModal} - collapseModal={this.collapseModal} - setEnforceFocus={(enforceFocus) => this.setState({enforceFocus})} - setRequireConfirm={ - (requireConfirm) => { - this.requireConfirm = requireConfirm; - return; - } - } - /> - </div> - </div> - </Modal.Body> - <ConfirmModal - title={formatMessage(holders.confirmTitle)} - message={formatMessage(holders.confirmMsg)} - confirmButton={formatMessage(holders.confirmBtns)} - show={this.state.showConfirmModal} - onConfirm={this.handleConfirm} - onCancel={this.handleCancelConfirmation} - /> - </Modal> - ); - } -} - -UserSettingsModal.propTypes = { - intl: intlShape.isRequired, - show: React.PropTypes.bool.isRequired, - onModalDismissed: React.PropTypes.func.isRequired -}; - -export default injectIntl(UserSettingsModal); diff --git a/web/react/components/user_settings/user_settings_notifications.jsx b/web/react/components/user_settings/user_settings_notifications.jsx deleted file mode 100644 index 3ef6435f1..000000000 --- a/web/react/components/user_settings/user_settings_notifications.jsx +++ /dev/null @@ -1,831 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import SettingItemMin from '../setting_item_min.jsx'; -import SettingItemMax from '../setting_item_max.jsx'; - -import UserStore from '../../stores/user_store.jsx'; - -import * as Client from '../../utils/client.jsx'; -import * as AsyncClient from '../../utils/async_client.jsx'; -import * as Utils from '../../utils/utils.jsx'; - -import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl'; - -function getNotificationsStateFromStores() { - var user = UserStore.getCurrentUser(); - var soundNeeded = !Utils.isBrowserFirefox(); - - var sound = 'true'; - if (user.notify_props && user.notify_props.desktop_sound) { - sound = user.notify_props.desktop_sound; - } - var desktop = 'default'; - if (user.notify_props && user.notify_props.desktop) { - desktop = user.notify_props.desktop; - } - var email = 'true'; - if (user.notify_props && user.notify_props.email) { - email = user.notify_props.email; - } - - var usernameKey = false; - var mentionKey = false; - var customKeys = ''; - var firstNameKey = false; - var allKey = false; - var channelKey = false; - - if (user.notify_props) { - if (user.notify_props.mention_keys) { - var keys = user.notify_props.mention_keys.split(','); - - if (keys.indexOf(user.username) === -1) { - usernameKey = false; - } else { - usernameKey = true; - keys.splice(keys.indexOf(user.username), 1); - } - - if (keys.indexOf('@' + user.username) === -1) { - mentionKey = false; - } else { - mentionKey = true; - keys.splice(keys.indexOf('@' + user.username), 1); - } - - customKeys = keys.join(','); - } - - if (user.notify_props.first_name) { - firstNameKey = user.notify_props.first_name === 'true'; - } - - if (user.notify_props.all) { - allKey = user.notify_props.all === 'true'; - } - - if (user.notify_props.channel) { - channelKey = user.notify_props.channel === 'true'; - } - } - - return {notifyLevel: desktop, enableEmail: email, soundNeeded: soundNeeded, enableSound: sound, - usernameKey: usernameKey, mentionKey: mentionKey, customKeys: customKeys, customKeysChecked: customKeys.length > 0, - firstNameKey: firstNameKey, allKey: allKey, channelKey: channelKey}; -} - -const holders = defineMessages({ - desktop: { - id: 'user.settings.notifications.desktop', - defaultMessage: 'Send desktop notifications' - }, - desktopSounds: { - id: 'user.settings.notifications.desktopSounds', - defaultMessage: 'Desktop notification sounds' - }, - emailNotifications: { - id: 'user.settings.notifications.emailNotifications', - defaultMessage: 'Email notifications' - }, - wordsTrigger: { - id: 'user.settings.notifications.wordsTrigger', - defaultMessage: 'Words that trigger mentions' - }, - close: { - id: 'user.settings.notifications.close', - defaultMessage: 'Close' - } -}); - -class NotificationsTab extends React.Component { - constructor(props) { - super(props); - - this.handleSubmit = this.handleSubmit.bind(this); - this.handleCancel = this.handleCancel.bind(this); - this.updateSection = this.updateSection.bind(this); - this.updateState = this.updateState.bind(this); - this.onListenerChange = this.onListenerChange.bind(this); - this.handleNotifyRadio = this.handleNotifyRadio.bind(this); - this.handleEmailRadio = this.handleEmailRadio.bind(this); - this.handleSoundRadio = this.handleSoundRadio.bind(this); - this.updateUsernameKey = this.updateUsernameKey.bind(this); - this.updateMentionKey = this.updateMentionKey.bind(this); - this.updateFirstNameKey = this.updateFirstNameKey.bind(this); - this.updateAllKey = this.updateAllKey.bind(this); - this.updateChannelKey = this.updateChannelKey.bind(this); - this.updateCustomMentionKeys = this.updateCustomMentionKeys.bind(this); - this.onCustomChange = this.onCustomChange.bind(this); - - this.state = getNotificationsStateFromStores(); - } - handleSubmit() { - var data = {}; - data.user_id = this.props.user.id; - data.email = this.state.enableEmail; - data.desktop_sound = this.state.enableSound; - data.desktop = this.state.notifyLevel; - - var mentionKeys = []; - if (this.state.usernameKey) { - mentionKeys.push(this.props.user.username); - } - if (this.state.mentionKey) { - mentionKeys.push('@' + this.props.user.username); - } - - var stringKeys = mentionKeys.join(','); - if (this.state.customKeys.length > 0 && this.state.customKeysChecked) { - stringKeys += ',' + this.state.customKeys; - } - - data.mention_keys = stringKeys; - data.first_name = this.state.firstNameKey.toString(); - data.all = this.state.allKey.toString(); - data.channel = this.state.channelKey.toString(); - - Client.updateUserNotifyProps(data, - function success() { - this.props.updateSection(''); - AsyncClient.getMe(); - }.bind(this), - function failure(err) { - this.setState({serverError: err.message}); - }.bind(this) - ); - } - handleCancel(e) { - this.updateState(); - this.props.updateSection(''); - e.preventDefault(); - } - updateSection(section) { - this.updateState(); - this.props.updateSection(section); - } - updateState() { - const newState = getNotificationsStateFromStores(); - if (!Utils.areObjectsEqual(newState, this.state)) { - this.setState(newState); - } - } - componentDidMount() { - UserStore.addChangeListener(this.onListenerChange); - } - componentWillUnmount() { - UserStore.removeChangeListener(this.onListenerChange); - } - onListenerChange() { - this.updateState(); - } - handleNotifyRadio(notifyLevel) { - this.setState({notifyLevel: notifyLevel}); - ReactDOM.findDOMNode(this.refs.wrapper).focus(); - } - handleEmailRadio(enableEmail) { - this.setState({enableEmail: enableEmail}); - ReactDOM.findDOMNode(this.refs.wrapper).focus(); - } - handleSoundRadio(enableSound) { - this.setState({enableSound: enableSound}); - ReactDOM.findDOMNode(this.refs.wrapper).focus(); - } - updateUsernameKey(val) { - this.setState({usernameKey: val}); - } - updateMentionKey(val) { - this.setState({mentionKey: val}); - } - updateFirstNameKey(val) { - this.setState({firstNameKey: val}); - } - updateAllKey(val) { - this.setState({allKey: val}); - } - updateChannelKey(val) { - this.setState({channelKey: val}); - } - updateCustomMentionKeys() { - var checked = ReactDOM.findDOMNode(this.refs.customcheck).checked; - - if (checked) { - var text = ReactDOM.findDOMNode(this.refs.custommentions).value; - - // remove all spaces and split string into individual keys - this.setState({customKeys: text.replace(/ /g, ''), customKeysChecked: true}); - } else { - this.setState({customKeys: '', customKeysChecked: false}); - } - } - onCustomChange() { - ReactDOM.findDOMNode(this.refs.customcheck).checked = true; - this.updateCustomMentionKeys(); - } - render() { - const {formatMessage} = this.props.intl; - var serverError = null; - if (this.state.serverError) { - serverError = this.state.serverError; - } - - var user = this.props.user; - - var desktopSection; - var handleUpdateDesktopSection; - if (this.props.activeSection === 'desktop') { - var notifyActive = [false, false, false]; - if (this.state.notifyLevel === 'mention') { - notifyActive[1] = true; - } else if (this.state.notifyLevel === 'none') { - notifyActive[2] = true; - } else { - notifyActive[0] = true; - } - - let inputs = []; - - inputs.push( - <div key='userNotificationLevelOption'> - <div className='radio'> - <label> - <input type='radio' - checked={notifyActive[0]} - onChange={this.handleNotifyRadio.bind(this, 'all')} - /> - <FormattedMessage - id='user.settings.notification.allActivity' - defaultMessage='For all activity' - /> - </label> - <br/> - </div> - <div className='radio'> - <label> - <input - type='radio' - checked={notifyActive[1]} - onChange={this.handleNotifyRadio.bind(this, 'mention')} - /> - <FormattedMessage - id='user.settings.notifications.onlyMentions' - defaultMessage='Only for mentions and direct messages' - /> - </label> - <br/> - </div> - <div className='radio'> - <label> - <input - type='radio' - checked={notifyActive[2]} - onChange={this.handleNotifyRadio.bind(this, 'none')} - /> - <FormattedMessage - id='user.settings.notifications.never' - defaultMessage='Never' - /> - </label> - </div> - </div> - ); - - const extraInfo = ( - <span> - <FormattedMessage - id='user.settings.notifications.info' - defaultMessage='Desktop notifications are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.' - /> - </span> - ); - - desktopSection = ( - <SettingItemMax - title={formatMessage(holders.desktop)} - extraInfo={extraInfo} - inputs={inputs} - submit={this.handleSubmit} - server_error={serverError} - updateSection={this.handleCancel} - /> - ); - } else { - let describe = ''; - if (this.state.notifyLevel === 'mention') { - describe = ( - <FormattedMessage - id='user.settings.notifications.onlyMentions' - defaultMessage='Only for mentions and direct messages' - /> - ); - } else if (this.state.notifyLevel === 'none') { - describe = ( - <FormattedMessage - id='user.settings.notifications.never' - defaultMessage='Never' - /> - ); - } else { - describe = ( - <FormattedMessage - id='user.settings.notification.allActivity' - defaultMessage='For all activity' - /> - ); - } - - handleUpdateDesktopSection = function updateDesktopSection() { - this.props.updateSection('desktop'); - }.bind(this); - - desktopSection = ( - <SettingItemMin - title={formatMessage(holders.desktop)} - describe={describe} - updateSection={handleUpdateDesktopSection} - /> - ); - } - - var soundSection; - var handleUpdateSoundSection; - if (this.props.activeSection === 'sound' && this.state.soundNeeded) { - var soundActive = [false, false]; - if (this.state.enableSound === 'false') { - soundActive[1] = true; - } else { - soundActive[0] = true; - } - - let inputs = []; - - inputs.push( - <div key='userNotificationSoundOptions'> - <div className='radio'> - <label> - <input - type='radio' - checked={soundActive[0]} - onChange={this.handleSoundRadio.bind(this, 'true')} - /> - <FormattedMessage - id='user.settings.notifications.on' - defaultMessage='On' - /> - </label> - <br/> - </div> - <div className='radio'> - <label> - <input - type='radio' - checked={soundActive[1]} - onChange={this.handleSoundRadio.bind(this, 'false')} - /> - <FormattedMessage - id='user.settings.notifications.off' - defaultMessage='Off' - /> - </label> - <br/> - </div> - </div> - ); - - const extraInfo = ( - <span> - <FormattedMessage - id='user.settings.notifications.sounds_info' - defaultMessage='Desktop notifications sounds are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.' - /> - </span> - ); - - soundSection = ( - <SettingItemMax - title={formatMessage(holders.desktopSounds)} - extraInfo={extraInfo} - inputs={inputs} - submit={this.handleSubmit} - server_error={serverError} - updateSection={this.handleCancel} - /> - ); - } else { - let describe = ''; - if (!this.state.soundNeeded) { - describe = ( - <FormattedMessage - id='user.settings.notification.soundConfig' - defaultMessage='Please configure notification sounds in your browser settings' - /> - ); - } else if (this.state.enableSound === 'false') { - describe = ( - <FormattedMessage - id='user.settings.notifications.off' - defaultMessage='Off' - /> - ); - } else { - describe = ( - <FormattedMessage - id='user.settings.notifications.on' - defaultMessage='On' - /> - ); - } - - handleUpdateSoundSection = function updateSoundSection() { - this.props.updateSection('sound'); - }.bind(this); - - soundSection = ( - <SettingItemMin - title={formatMessage(holders.desktopSounds)} - describe={describe} - updateSection={handleUpdateSoundSection} - disableOpen={!this.state.soundNeeded} - /> - ); - } - - var emailSection; - var handleUpdateEmailSection; - if (this.props.activeSection === 'email') { - var emailActive = [false, false]; - if (this.state.enableEmail === 'false') { - emailActive[1] = true; - } else { - emailActive[0] = true; - } - - let inputs = []; - - inputs.push( - <div key='userNotificationEmailOptions'> - <div className='radio'> - <label> - <input - type='radio' - checked={emailActive[0]} - onChange={this.handleEmailRadio.bind(this, 'true')} - /> - <FormattedMessage - id='user.settings.notifications.on' - defaultMessage='On' - /> - </label> - <br/> - </div> - <div className='radio'> - <label> - <input - type='radio' - checked={emailActive[1]} - onChange={this.handleEmailRadio.bind(this, 'false')} - /> - <FormattedMessage - id='user.settings.notifications.off' - defaultMessage='Off' - /> - </label> - <br/> - </div> - <div><br/> - <FormattedMessage - id='user.settings.notifications.emailInfo' - defaultMessage='Email notifications are sent for mentions and direct messages after you’ve been offline for more than 60 seconds or away from {siteName} for more than 5 minutes.' - values={{ - siteName: global.window.mm_config.SiteName - }} - /> - </div> - </div> - ); - - emailSection = ( - <SettingItemMax - title={formatMessage(holders.emailNotifications)} - inputs={inputs} - submit={this.handleSubmit} - server_error={serverError} - updateSection={this.handleCancel} - /> - ); - } else { - let describe = ''; - if (this.state.enableEmail === 'false') { - describe = ( - <FormattedMessage - id='user.settings.notifications.off' - defaultMessage='Off' - /> - ); - } else { - describe = ( - <FormattedMessage - id='user.settings.notifications.on' - defaultMessage='On' - /> - ); - } - - handleUpdateEmailSection = function updateEmailSection() { - this.props.updateSection('email'); - }.bind(this); - - emailSection = ( - <SettingItemMin - title={formatMessage(holders.emailNotifications)} - describe={describe} - updateSection={handleUpdateEmailSection} - /> - ); - } - - var keysSection; - var handleUpdateKeysSection; - if (this.props.activeSection === 'keys') { - let inputs = []; - - let handleUpdateFirstNameKey; - let handleUpdateUsernameKey; - let handleUpdateMentionKey; - let handleUpdateAllKey; - let handleUpdateChannelKey; - - if (user.first_name) { - handleUpdateFirstNameKey = function handleFirstNameKeyChange(e) { - this.updateFirstNameKey(e.target.checked); - }.bind(this); - inputs.push( - <div key='userNotificationFirstNameOption'> - <div className='checkbox'> - <label> - <input - type='checkbox' - checked={this.state.firstNameKey} - onChange={handleUpdateFirstNameKey} - /> - <FormattedMessage - id='user.settings.notifications.sensitiveName' - defaultMessage='Your case sensitive first name "{first_name}"' - values={{ - first_name: user.first_name - }} - /> - </label> - </div> - </div> - ); - } - - handleUpdateUsernameKey = function handleUsernameKeyChange(e) { - this.updateUsernameKey(e.target.checked); - }.bind(this); - inputs.push( - <div key='userNotificationUsernameOption'> - <div className='checkbox'> - <label> - <input - type='checkbox' - checked={this.state.usernameKey} - onChange={handleUpdateUsernameKey} - /> - <FormattedMessage - id='user.settings.notifications.sensitiveUsername' - defaultMessage='Your non-case sensitive username "{username}"' - values={{ - username: user.username - }} - /> - </label> - </div> - </div> - ); - - handleUpdateMentionKey = function handleMentionKeyChange(e) { - this.updateMentionKey(e.target.checked); - }.bind(this); - inputs.push( - <div key='userNotificationMentionOption'> - <div className='checkbox'> - <label> - <input - type='checkbox' - checked={this.state.mentionKey} - onChange={handleUpdateMentionKey} - /> - <FormattedMessage - id='user.settings.notifications.usernameMention' - defaultMessage='Your username mentioned "@{username}"' - values={{ - username: user.username - }} - /> - </label> - </div> - </div> - ); - - handleUpdateAllKey = function handleAllKeyChange(e) { - this.updateAllKey(e.target.checked); - }.bind(this); - inputs.push( - <div key='userNotificationAllOption'> - <div className='checkbox hidden'> - <label> - <input - type='checkbox' - checked={this.state.allKey} - onChange={handleUpdateAllKey} - /> - <FormattedMessage - id='user.settings.notifications.teamWide' - defaultMessage='Team-wide mentions "@all"' - /> - </label> - </div> - </div> - ); - - handleUpdateChannelKey = function handleChannelKeyChange(e) { - this.updateChannelKey(e.target.checked); - }.bind(this); - inputs.push( - <div key='userNotificationChannelOption'> - <div className='checkbox'> - <label> - <input - type='checkbox' - checked={this.state.channelKey} - onChange={handleUpdateChannelKey} - /> - <FormattedMessage - id='user.settings.notifications.channelWide' - defaultMessage='Channel-wide mentions "@channel"' - /> - </label> - </div> - </div> - ); - - inputs.push( - <div key='userNotificationCustomOption'> - <div className='checkbox'> - <label> - <input - ref='customcheck' - type='checkbox' - checked={this.state.customKeysChecked} - onChange={this.updateCustomMentionKeys} - /> - <FormattedMessage - id='user.settings.notifications.sensitiveWords' - defaultMessage='Other non-case sensitive words, separated by commas:' - /> - </label> - </div> - <input - ref='custommentions' - className='form-control mentions-input' - type='text' - defaultValue={this.state.customKeys} - onChange={this.onCustomChange} - /> - </div> - ); - - keysSection = ( - <SettingItemMax - title={formatMessage(holders.wordsTrigger)} - inputs={inputs} - submit={this.handleSubmit} - server_error={serverError} - updateSection={this.handleCancel} - /> - ); - } else { - let keys = []; - if (this.state.firstNameKey) { - keys.push(user.first_name); - } - if (this.state.usernameKey) { - keys.push(user.username); - } - if (this.state.mentionKey) { - keys.push('@' + user.username); - } - - // if (this.state.allKey) { - // keys.push('@all'); - // } - - if (this.state.channelKey) { - keys.push('@channel'); - } - if (this.state.customKeys.length > 0) { - keys = keys.concat(this.state.customKeys.split(',')); - } - - let describe = ''; - for (var i = 0; i < keys.length; i++) { - describe += '"' + keys[i] + '", '; - } - - if (describe.length > 0) { - describe = describe.substring(0, describe.length - 2); - } else { - describe = ( - <FormattedMessage - id='user.settings.notifications.noWords' - defaultMessage='No words configured' - /> - ); - } - - handleUpdateKeysSection = function updateKeysSection() { - this.props.updateSection('keys'); - }.bind(this); - - keysSection = ( - <SettingItemMin - title={formatMessage(holders.wordsTrigger)} - describe={describe} - updateSection={handleUpdateKeysSection} - /> - ); - } - - return ( - <div> - <div className='modal-header'> - <button - type='button' - className='close' - data-dismiss='modal' - aria-label={formatMessage(holders.close)} - onClick={this.props.closeModal} - > - <span aria-hidden='true'>{'×'}</span> - </button> - <h4 - className='modal-title' - ref='title' - > - <div className='modal-back'> - <i - className='fa fa-angle-left' - onClick={this.props.collapseModal} - /> - </div> - <FormattedMessage - id='user.settings.notifications.title' - defaultMessage='Notification Settings' - /> - </h4> - </div> - <div - ref='wrapper' - className='user-settings' - > - <h3 className='tab-header'> - <FormattedMessage - id='user.settings.notifications.header' - defaultMessage='Notifications' - /> - </h3> - <div className='divider-dark first'/> - {desktopSection} - <div className='divider-light'/> - {soundSection} - <div className='divider-light'/> - {emailSection} - <div className='divider-light'/> - {keysSection} - <div className='divider-dark'/> - </div> - </div> - - ); - } -} - -NotificationsTab.defaultProps = { - user: null, - activeSection: '', - activeTab: '' -}; -NotificationsTab.propTypes = { - intl: intlShape.isRequired, - user: React.PropTypes.object, - updateSection: React.PropTypes.func, - updateTab: React.PropTypes.func, - activeSection: React.PropTypes.string, - activeTab: React.PropTypes.string, - closeModal: React.PropTypes.func.isRequired, - collapseModal: React.PropTypes.func.isRequired -}; - -export default injectIntl(NotificationsTab); diff --git a/web/react/components/user_settings/user_settings_security.jsx b/web/react/components/user_settings/user_settings_security.jsx deleted file mode 100644 index 0b6b6c398..000000000 --- a/web/react/components/user_settings/user_settings_security.jsx +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import SettingItemMin from '../setting_item_min.jsx'; -import SettingItemMax from '../setting_item_max.jsx'; -import AccessHistoryModal from '../access_history_modal.jsx'; -import ActivityLogModal from '../activity_log_modal.jsx'; -import ToggleModalButton from '../toggle_modal_button.jsx'; - -import TeamStore from '../../stores/team_store.jsx'; - -import * as Client from '../../utils/client.jsx'; -import * as AsyncClient from '../../utils/async_client.jsx'; -import * as Utils from '../../utils/utils.jsx'; -import Constants from '../../utils/constants.jsx'; - -import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedTime, FormattedDate} from 'mm-intl'; - -const holders = defineMessages({ - currentPasswordError: { - id: 'user.settings.security.currentPasswordError', - defaultMessage: 'Please enter your current password' - }, - passwordLengthError: { - id: 'user.settings.security.passwordLengthError', - defaultMessage: 'New passwords must be at least {chars} characters' - }, - passwordMatchError: { - id: 'user.settings.security.passwordMatchError', - defaultMessage: 'The new passwords you entered do not match' - }, - password: { - id: 'user.settings.security.password', - defaultMessage: 'Password' - }, - lastUpdated: { - id: 'user.settings.security.lastUpdated', - defaultMessage: 'Last updated {date} at {time}' - }, - method: { - id: 'user.settings.security.method', - defaultMessage: 'Sign-in Method' - }, - close: { - id: 'user.settings.security.close', - defaultMessage: 'Close' - } -}); - -class SecurityTab extends React.Component { - constructor(props) { - super(props); - - this.submitPassword = this.submitPassword.bind(this); - this.updateCurrentPassword = this.updateCurrentPassword.bind(this); - this.updateNewPassword = this.updateNewPassword.bind(this); - this.updateConfirmPassword = this.updateConfirmPassword.bind(this); - this.getDefaultState = this.getDefaultState.bind(this); - this.createPasswordSection = this.createPasswordSection.bind(this); - this.createSignInSection = this.createSignInSection.bind(this); - - this.state = this.getDefaultState(); - } - getDefaultState() { - return { - currentPassword: '', - newPassword: '', - confirmPassword: '', - authService: this.props.user.auth_service - }; - } - submitPassword(e) { - e.preventDefault(); - - var user = this.props.user; - var currentPassword = this.state.currentPassword; - var newPassword = this.state.newPassword; - var confirmPassword = this.state.confirmPassword; - - const {formatMessage} = this.props.intl; - if (currentPassword === '') { - this.setState({passwordError: formatMessage(holders.currentPasswordError), serverError: ''}); - return; - } - - if (newPassword.length < Constants.MIN_PASSWORD_LENGTH) { - this.setState({passwordError: formatMessage(holders.passwordLengthError, {chars: Constants.MIN_PASSWORD_LENGTH}), serverError: ''}); - return; - } - - if (newPassword !== confirmPassword) { - var defaultState = Object.assign(this.getDefaultState(), {passwordError: formatMessage(holders.passwordMatchError), serverError: ''}); - this.setState(defaultState); - return; - } - - var data = {}; - data.user_id = user.id; - data.current_password = currentPassword; - data.new_password = newPassword; - - Client.updatePassword(data, - () => { - this.props.updateSection(''); - AsyncClient.getMe(); - this.setState(this.getDefaultState()); - }, - (err) => { - var state = this.getDefaultState(); - if (err.message) { - state.serverError = err.message; - } else { - state.serverError = err; - } - state.passwordError = ''; - this.setState(state); - } - ); - } - updateCurrentPassword(e) { - this.setState({currentPassword: e.target.value}); - } - updateNewPassword(e) { - this.setState({newPassword: e.target.value}); - } - updateConfirmPassword(e) { - this.setState({confirmPassword: e.target.value}); - } - createPasswordSection() { - let updateSectionStatus; - const {formatMessage} = this.props.intl; - - if (this.props.activeSection === 'password' && this.props.user.auth_service === '') { - const inputs = []; - - inputs.push( - <div - key='currentPasswordUpdateForm' - className='form-group' - > - <label className='col-sm-5 control-label'> - <FormattedMessage - id='user.settings.security.currentPassword' - defaultMessage='Current Password' - /> - </label> - <div className='col-sm-7'> - <input - className='form-control' - type='password' - onChange={this.updateCurrentPassword} - value={this.state.currentPassword} - /> - </div> - </div> - ); - inputs.push( - <div - key='newPasswordUpdateForm' - className='form-group' - > - <label className='col-sm-5 control-label'> - <FormattedMessage - id='user.settings.security.newPassword' - defaultMessage='New Password' - /> - </label> - <div className='col-sm-7'> - <input - className='form-control' - type='password' - onChange={this.updateNewPassword} - value={this.state.newPassword} - /> - </div> - </div> - ); - inputs.push( - <div - key='retypeNewPasswordUpdateForm' - className='form-group' - > - <label className='col-sm-5 control-label'> - <FormattedMessage - id='user.settings.security.retypePassword' - defaultMessage='Retype New Password' - /> - </label> - <div className='col-sm-7'> - <input - className='form-control' - type='password' - onChange={this.updateConfirmPassword} - value={this.state.confirmPassword} - /> - </div> - </div> - ); - - updateSectionStatus = function resetSection(e) { - this.props.updateSection(''); - this.setState({currentPassword: '', newPassword: '', confirmPassword: '', serverError: null, passwordError: null}); - e.preventDefault(); - }.bind(this); - - return ( - <SettingItemMax - title={formatMessage(holders.password)} - inputs={inputs} - submit={this.submitPassword} - server_error={this.state.serverError} - client_error={this.state.passwordError} - updateSection={updateSectionStatus} - /> - ); - } - - var describe; - var d = new Date(this.props.user.last_password_update); - - const hours12 = !Utils.isMilitaryTime(); - describe = formatMessage(holders.lastUpdated, { - date: ( - <FormattedDate - value={d} - day='2-digit' - month='short' - year='numeric' - /> - ), - time: ( - <FormattedTime - value={d} - hour12={hours12} - hour='2-digit' - minute='2-digit' - /> - ) - }); - - updateSectionStatus = function updateSection() { - this.props.updateSection('password'); - }.bind(this); - - return ( - <SettingItemMin - title={formatMessage(holders.password)} - describe={describe} - updateSection={updateSectionStatus} - /> - ); - } - createSignInSection() { - let updateSectionStatus; - const user = this.props.user; - - if (this.props.activeSection === 'signin') { - const inputs = []; - const teamName = TeamStore.getCurrent().name; - - let emailOption; - if (global.window.mm_config.EnableSignUpWithEmail === 'true' && user.auth_service !== '') { - emailOption = ( - <div> - <a - className='btn btn-primary' - href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service} - > - <FormattedMessage - id='user.settings.security.switchEmail' - defaultMessage='Switch to using email and password' - /> - </a> - <br/> - </div> - ); - } - - let gitlabOption; - if (global.window.mm_config.EnableSignUpWithGitLab === 'true' && user.auth_service === '') { - gitlabOption = ( - <div> - <a - className='btn btn-primary' - href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.GITLAB_SERVICE} - > - <FormattedMessage - id='user.settings.security.switchGitlab' - defaultMessage='Switch to using GitLab SSO' - /> - </a> - <br/> - </div> - ); - } - - let googleOption; - if (global.window.mm_config.EnableSignUpWithGoogle === 'true' && user.auth_service === '') { - googleOption = ( - <div> - <a - className='btn btn-primary' - href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.GOOGLE_SERVICE} - > - <FormattedMessage - id='user.settings.security.switchGoogle' - defaultMessage='Switch to using Google SSO' - /> - </a> - <br/> - </div> - ); - } - - inputs.push( - <div key='userSignInOption'> - {emailOption} - {gitlabOption} - <br/> - {googleOption} - </div> - ); - - updateSectionStatus = function updateSection(e) { - this.props.updateSection(''); - this.setState({serverError: null}); - e.preventDefault(); - }.bind(this); - - const extraInfo = ( - <span> - <FormattedMessage - id='user.settings.security.oneSignin' - defaultMessage='You may only have one sign-in method at a time. Switching sign-in method will send an email notifying you if the change was successful.' - /> - </span> - ); - - return ( - <SettingItemMax - title={this.props.intl.formatMessage(holders.method)} - extraInfo={extraInfo} - inputs={inputs} - server_error={this.state.serverError} - updateSection={updateSectionStatus} - /> - ); - } - - updateSectionStatus = function updateSection() { - this.props.updateSection('signin'); - }.bind(this); - - let describe = ( - <FormattedMessage - id='user.settings.security.emailPwd' - defaultMessage='Email and Password' - /> - ); - if (this.props.user.auth_service === Constants.GITLAB_SERVICE) { - describe = ( - <FormattedMessage - id='user.settings.security.gitlab' - defaultMessage='GitLab SSO' - /> - ); - } - - return ( - <SettingItemMin - title={this.props.intl.formatMessage(holders.method)} - describe={describe} - updateSection={updateSectionStatus} - /> - ); - } - render() { - const passwordSection = this.createPasswordSection(); - let signInSection; - - let numMethods = 0; - numMethods = global.window.mm_config.EnableSignUpWithGitLab === 'true' ? numMethods + 1 : numMethods; - numMethods = global.window.mm_config.EnableSignUpWithGoogle === 'true' ? numMethods + 1 : numMethods; - - if (global.window.mm_config.EnableSignUpWithEmail && numMethods > 0) { - signInSection = this.createSignInSection(); - } - - return ( - <div> - <div className='modal-header'> - <button - type='button' - className='close' - data-dismiss='modal' - aria-label={this.props.intl.formatMessage(holders.close)} - onClick={this.props.closeModal} - > - <span aria-hidden='true'>{'×'}</span> - </button> - <h4 - className='modal-title' - ref='title' - > - <div className='modal-back'> - <i - className='fa fa-angle-left' - onClick={this.props.collapseModal} - /> - </div> - <FormattedMessage - id='user.settings.security.title' - defaultMessage='Security Settings' - /> - </h4> - </div> - <div className='user-settings'> - <h3 className='tab-header'> - <FormattedMessage - id='user.settings.security.title' - defaultMessage='Security Settings' - /> - </h3> - <div className='divider-dark first'/> - {passwordSection} - <div className='divider-light'/> - {signInSection} - <div className='divider-dark'/> - <br></br> - <ToggleModalButton - className='security-links theme' - dialogType={AccessHistoryModal} - > - <i className='fa fa-clock-o'></i> - <FormattedMessage - id='user.settings.security.viewHistory' - defaultMessage='View Access History' - /> - </ToggleModalButton> - <b> </b> - <ToggleModalButton - className='security-links theme' - dialogType={ActivityLogModal} - > - <i className='fa fa-clock-o'></i> - <FormattedMessage - id='user.settings.security.logoutActiveSessions' - defaultMessage='View and Logout of Active Sessions' - /> - </ToggleModalButton> - </div> - </div> - ); - } -} - -SecurityTab.defaultProps = { - user: {}, - activeSection: '' -}; -SecurityTab.propTypes = { - intl: intlShape.isRequired, - user: React.PropTypes.object, - activeSection: React.PropTypes.string, - updateSection: React.PropTypes.func, - updateTab: React.PropTypes.func, - closeModal: React.PropTypes.func.isRequired, - collapseModal: React.PropTypes.func.isRequired, - setEnforceFocus: React.PropTypes.func.isRequired -}; - -export default injectIntl(SecurityTab); diff --git a/web/react/components/user_settings/user_settings_theme.jsx b/web/react/components/user_settings/user_settings_theme.jsx deleted file mode 100644 index 74975d115..000000000 --- a/web/react/components/user_settings/user_settings_theme.jsx +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import CustomThemeChooser from './custom_theme_chooser.jsx'; -import PremadeThemeChooser from './premade_theme_chooser.jsx'; -import SettingItemMin from '../setting_item_min.jsx'; -import SettingItemMax from '../setting_item_max.jsx'; - -import UserStore from '../../stores/user_store.jsx'; - -import AppDispatcher from '../../dispatcher/app_dispatcher.jsx'; -import * as Client from '../../utils/client.jsx'; -import * as Utils from '../../utils/utils.jsx'; - -import Constants from '../../utils/constants.jsx'; - -import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl'; - -const ActionTypes = Constants.ActionTypes; - -const holders = defineMessages({ - themeTitle: { - id: 'user.settings.display.theme.title', - defaultMessage: 'Theme' - }, - themeDescribe: { - id: 'user.settings.display.theme.describe', - defaultMessage: 'Open to manage your theme' - } -}); - -export default class ThemeSetting extends React.Component { - constructor(props) { - super(props); - - this.onChange = this.onChange.bind(this); - this.submitTheme = this.submitTheme.bind(this); - this.updateTheme = this.updateTheme.bind(this); - this.deactivate = this.deactivate.bind(this); - this.resetFields = this.resetFields.bind(this); - this.handleImportModal = this.handleImportModal.bind(this); - - this.state = this.getStateFromStores(); - - this.originalTheme = Object.assign({}, this.state.theme); - } - componentDidMount() { - UserStore.addChangeListener(this.onChange); - - if (this.props.selected) { - $(ReactDOM.findDOMNode(this.refs[this.state.theme])).addClass('active-border'); - } - } - componentDidUpdate() { - if (this.props.selected) { - $('.color-btn').removeClass('active-border'); - $(ReactDOM.findDOMNode(this.refs[this.state.theme])).addClass('active-border'); - } - } - componentWillReceiveProps(nextProps) { - if (!this.props.selected && nextProps.selected) { - this.resetFields(); - } - } - componentWillUnmount() { - UserStore.removeChangeListener(this.onChange); - } - getStateFromStores() { - const user = UserStore.getCurrentUser(); - let theme = null; - - if ($.isPlainObject(user.theme_props) && !$.isEmptyObject(user.theme_props)) { - theme = Object.assign({}, user.theme_props); - } else { - theme = $.extend(true, {}, Constants.THEMES.default); - } - - let type = 'premade'; - if (theme.type === 'custom') { - type = 'custom'; - } - - if (!theme.codeTheme) { - theme.codeTheme = Constants.DEFAULT_CODE_THEME; - } - - return {theme, type}; - } - onChange() { - const newState = this.getStateFromStores(); - - if (!Utils.areObjectsEqual(this.state, newState)) { - this.setState(newState); - } - - this.props.setEnforceFocus(true); - } - scrollToTop() { - $('.ps-container.modal-body').scrollTop(0); - $('.ps-container.modal-body').perfectScrollbar('update'); - } - submitTheme(e) { - e.preventDefault(); - var user = UserStore.getCurrentUser(); - user.theme_props = this.state.theme; - - Client.updateUser(user, - (data) => { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_ME, - me: data - }); - - this.props.setRequireConfirm(false); - this.originalTheme = Object.assign({}, this.state.theme); - this.scrollToTop(); - this.props.updateSection(''); - }, - (err) => { - var state = this.getStateFromStores(); - state.serverError = err; - this.setState(state); - } - ); - } - updateTheme(theme) { - let themeChanged = this.state.theme.length === theme.length; - if (!themeChanged) { - for (const field in theme) { - if (theme.hasOwnProperty(field)) { - if (this.state.theme[field] !== theme[field]) { - themeChanged = true; - break; - } - } - } - } - - this.props.setRequireConfirm(themeChanged); - - this.setState({theme}); - Utils.applyTheme(theme); - } - updateType(type) { - this.setState({type}); - } - deactivate() { - const state = this.getStateFromStores(); - - Utils.applyTheme(state.theme); - } - resetFields() { - const state = this.getStateFromStores(); - state.serverError = null; - this.setState(state); - this.scrollToTop(); - - Utils.applyTheme(state.theme); - - this.props.setRequireConfirm(false); - } - handleImportModal() { - AppDispatcher.handleViewAction({ - type: ActionTypes.TOGGLE_IMPORT_THEME_MODAL, - value: true - }); - - this.props.setEnforceFocus(false); - } - render() { - const {formatMessage} = this.props.intl; - - var serverError; - if (this.state.serverError) { - serverError = this.state.serverError; - } - - const displayCustom = this.state.type === 'custom'; - - let custom; - let premade; - if (displayCustom) { - custom = ( - <div key='customThemeChooser'> - <CustomThemeChooser - theme={this.state.theme} - updateTheme={this.updateTheme} - /> - </div> - ); - } else { - premade = ( - <div key='premadeThemeChooser'> - <br/> - <PremadeThemeChooser - theme={this.state.theme} - updateTheme={this.updateTheme} - /> - </div> - ); - } - - let themeUI; - if (this.props.selected) { - let inputs = []; - - inputs.push( - <div - className='radio' - key='premadeThemeColorLabel' - > - <label> - <input type='radio' - checked={!displayCustom} - onChange={this.updateType.bind(this, 'premade')} - /> - <FormattedMessage - id='user.settings.display.theme.themeColors' - defaultMessage='Theme Colors' - /> - </label> - <br/> - </div> - ); - - inputs.push(premade); - - inputs.push( - <div - className='radio' - key='customThemeColorLabel' - > - <label> - <input type='radio' - checked={displayCustom} - onChange={this.updateType.bind(this, 'custom')} - /> - <FormattedMessage - id='user.settings.display.theme.customTheme' - defaultMessage='Custom Theme' - /> - </label> - </div> - ); - - inputs.push(custom); - - inputs.push( - <div key='importSlackThemeButton'> - <br/> - <a - className='theme' - onClick={this.handleImportModal} - > - <FormattedMessage - id='user.settings.display.theme.import' - defaultMessage='Import theme colors from Slack' - /> - </a> - </div> - ); - - themeUI = ( - <SettingItemMax - inputs={inputs} - submit={this.submitTheme} - server_error={serverError} - width='full' - updateSection={(e) => { - this.props.updateSection(''); - e.preventDefault(); - }} - /> - ); - } else { - themeUI = ( - <SettingItemMin - title={formatMessage(holders.themeTitle)} - describe={formatMessage(holders.themeDescribe)} - updateSection={() => { - this.props.updateSection('theme'); - }} - /> - ); - } - - return themeUI; - } -} - -ThemeSetting.propTypes = { - intl: intlShape.isRequired, - selected: React.PropTypes.bool.isRequired, - updateSection: React.PropTypes.func.isRequired, - setRequireConfirm: React.PropTypes.func.isRequired, - setEnforceFocus: React.PropTypes.func.isRequired -}; - -export default injectIntl(ThemeSetting); |