summaryrefslogtreecommitdiffstats
path: root/web/react/components
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-03-14 08:50:46 -0400
committerChristopher Speller <crspeller@gmail.com>2016-03-16 18:02:55 -0400
commit12896bd23eeba79884245c1c29fdc568cf21a7fa (patch)
tree4e7f83d3e2564b9b89d669e9f7905ff11768b11a /web/react/components
parent29fe6a3d13c9c7aa490fffebbe5d1b5fdf1e3090 (diff)
downloadchat-12896bd23eeba79884245c1c29fdc568cf21a7fa.tar.gz
chat-12896bd23eeba79884245c1c29fdc568cf21a7fa.tar.bz2
chat-12896bd23eeba79884245c1c29fdc568cf21a7fa.zip
Converting to Webpack. Stage 1.
Diffstat (limited to 'web/react/components')
-rw-r--r--web/react/components/about_build_modal.jsx135
-rw-r--r--web/react/components/access_history_modal.jsx110
-rw-r--r--web/react/components/activity_log_modal.jsx296
-rw-r--r--web/react/components/admin_console/admin_controller.jsx215
-rw-r--r--web/react/components/admin_console/admin_navbar_dropdown.jsx110
-rw-r--r--web/react/components/admin_console/admin_sidebar.jsx492
-rw-r--r--web/react/components/admin_console/admin_sidebar_header.jsx67
-rw-r--r--web/react/components/admin_console/audits.jsx95
-rw-r--r--web/react/components/admin_console/email_settings.jsx957
-rw-r--r--web/react/components/admin_console/gitlab_settings.jsx379
-rw-r--r--web/react/components/admin_console/image_settings.jsx688
-rw-r--r--web/react/components/admin_console/ldap_settings.jsx584
-rw-r--r--web/react/components/admin_console/legal_and_support_settings.jsx290
-rw-r--r--web/react/components/admin_console/license_settings.jsx291
-rw-r--r--web/react/components/admin_console/log_settings.jsx414
-rw-r--r--web/react/components/admin_console/logs.jsx100
-rw-r--r--web/react/components/admin_console/privacy_settings.jsx211
-rw-r--r--web/react/components/admin_console/rate_settings.jsx367
-rw-r--r--web/react/components/admin_console/reset_password_modal.jsx159
-rw-r--r--web/react/components/admin_console/select_team_modal.jsx112
-rw-r--r--web/react/components/admin_console/service_settings.jsx980
-rw-r--r--web/react/components/admin_console/sql_settings.jsx386
-rw-r--r--web/react/components/admin_console/team_settings.jsx416
-rw-r--r--web/react/components/admin_console/team_users.jsx186
-rw-r--r--web/react/components/admin_console/user_item.jsx421
-rw-r--r--web/react/components/analytics/doughnut_chart.jsx77
-rw-r--r--web/react/components/analytics/line_chart.jsx90
-rw-r--r--web/react/components/analytics/statistic_count.jsx33
-rw-r--r--web/react/components/analytics/system_analytics.jsx346
-rw-r--r--web/react/components/analytics/table_chart.jsx60
-rw-r--r--web/react/components/analytics/team_analytics.jsx235
-rw-r--r--web/react/components/audio_video_preview.jsx116
-rw-r--r--web/react/components/audit_table.jsx624
-rw-r--r--web/react/components/authorize.jsx115
-rw-r--r--web/react/components/center_panel.jsx139
-rw-r--r--web/react/components/change_url_modal.jsx219
-rw-r--r--web/react/components/channel_header.jsx517
-rw-r--r--web/react/components/channel_info_modal.jsx102
-rw-r--r--web/react/components/channel_invite_modal.jsx214
-rw-r--r--web/react/components/channel_members_modal.jsx223
-rw-r--r--web/react/components/channel_notifications_modal.jsx415
-rw-r--r--web/react/components/channel_view.jsx18
-rw-r--r--web/react/components/claim/claim_account.jsx113
-rw-r--r--web/react/components/claim/email_to_sso.jsx151
-rw-r--r--web/react/components/claim/sso_to_email.jsx165
-rw-r--r--web/react/components/confirm_modal.jsx67
-rw-r--r--web/react/components/create_comment.jsx448
-rw-r--r--web/react/components/create_post.jsx507
-rw-r--r--web/react/components/delete_channel_modal.jsx109
-rw-r--r--web/react/components/delete_post_modal.jsx174
-rw-r--r--web/react/components/do_verify_email.jsx82
-rw-r--r--web/react/components/edit_channel_header_modal.jsx173
-rw-r--r--web/react/components/edit_channel_purpose_modal.jsx184
-rw-r--r--web/react/components/edit_post_modal.jsx220
-rw-r--r--web/react/components/error_bar.jsx98
-rw-r--r--web/react/components/file_attachment.jsx225
-rw-r--r--web/react/components/file_attachment_list.jsx62
-rw-r--r--web/react/components/file_info_preview.jsx56
-rw-r--r--web/react/components/file_preview.jsx116
-rw-r--r--web/react/components/file_upload.jsx335
-rw-r--r--web/react/components/file_upload_overlay.jsx45
-rw-r--r--web/react/components/filtered_user_list.jsx132
-rw-r--r--web/react/components/get_link_modal.jsx135
-rw-r--r--web/react/components/get_post_link_modal.jsx76
-rw-r--r--web/react/components/get_team_invite_link_modal.jsx76
-rw-r--r--web/react/components/invite_member_modal.jsx515
-rw-r--r--web/react/components/loading_screen.jsx38
-rw-r--r--web/react/components/logged_in.jsx224
-rw-r--r--web/react/components/login.jsx302
-rw-r--r--web/react/components/login_email.jsx165
-rw-r--r--web/react/components/login_ldap.jsx142
-rw-r--r--web/react/components/login_username.jsx181
-rw-r--r--web/react/components/member_list_team.jsx60
-rw-r--r--web/react/components/message_wrapper.jsx26
-rw-r--r--web/react/components/more_channels.jsx227
-rw-r--r--web/react/components/more_direct_channels.jsx153
-rw-r--r--web/react/components/msg_typing.jsx135
-rw-r--r--web/react/components/navbar.jsx570
-rw-r--r--web/react/components/navbar_dropdown.jsx277
-rw-r--r--web/react/components/needs_team.jsx20
-rw-r--r--web/react/components/new_channel_flow.jsx248
-rw-r--r--web/react/components/new_channel_modal.jsx284
-rw-r--r--web/react/components/not_logged_in.jsx70
-rw-r--r--web/react/components/notify_counts.jsx52
-rw-r--r--web/react/components/password_reset_form.jsx129
-rw-r--r--web/react/components/password_reset_send_link.jsx149
-rw-r--r--web/react/components/popover_list_members.jsx200
-rw-r--r--web/react/components/post.jsx252
-rw-r--r--web/react/components/post_attachment.jsx312
-rw-r--r--web/react/components/post_attachment_list.jsx28
-rw-r--r--web/react/components/post_attachment_oembed.jsx104
-rw-r--r--web/react/components/post_body.jsx219
-rw-r--r--web/react/components/post_body_additional_content.jsx147
-rw-r--r--web/react/components/post_deleted_modal.jsx89
-rw-r--r--web/react/components/post_focus_view.jsx133
-rw-r--r--web/react/components/post_header.jsx78
-rw-r--r--web/react/components/post_image.jsx81
-rw-r--r--web/react/components/post_info.jsx249
-rw-r--r--web/react/components/posts_view.jsx604
-rw-r--r--web/react/components/posts_view_container.jsx223
-rw-r--r--web/react/components/providers.json376
-rw-r--r--web/react/components/register_app_modal.jsx409
-rw-r--r--web/react/components/removed_from_channel_modal.jsx132
-rw-r--r--web/react/components/rename_channel_modal.jsx321
-rw-r--r--web/react/components/rhs_comment.jsx306
-rw-r--r--web/react/components/rhs_header_post.jsx91
-rw-r--r--web/react/components/rhs_root_post.jsx287
-rw-r--r--web/react/components/rhs_thread.jsx226
-rw-r--r--web/react/components/root.jsx90
-rw-r--r--web/react/components/search_bar.jsx206
-rw-r--r--web/react/components/search_results.jsx193
-rw-r--r--web/react/components/search_results_header.jsx75
-rw-r--r--web/react/components/search_results_item.jsx143
-rw-r--r--web/react/components/setting_item_max.jsx95
-rw-r--r--web/react/components/setting_item_min.jsx45
-rw-r--r--web/react/components/setting_picture.jsx156
-rw-r--r--web/react/components/setting_upload.jsx123
-rw-r--r--web/react/components/settings_sidebar.jsx64
-rw-r--r--web/react/components/should_verify_email.jsx111
-rw-r--r--web/react/components/sidebar.jsx714
-rw-r--r--web/react/components/sidebar_header.jsx141
-rw-r--r--web/react/components/sidebar_right.jsx140
-rw-r--r--web/react/components/sidebar_right_menu.jsx220
-rw-r--r--web/react/components/signup_team.jsx205
-rw-r--r--web/react/components/signup_team_complete/components/signup_team_complete.jsx79
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_display_name_page.jsx136
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_email_item.jsx86
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_finished.jsx15
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_password_page.jsx215
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_send_invites_page.jsx210
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_url_page.jsx205
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_username_page.jsx164
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_welcome_page.jsx234
-rw-r--r--web/react/components/signup_team_confirm.jsx46
-rw-r--r--web/react/components/signup_user_complete.jsx491
-rw-r--r--web/react/components/suggestion/at_mention_provider.jsx118
-rw-r--r--web/react/components/suggestion/command_provider.jsx43
-rw-r--r--web/react/components/suggestion/emoticon_provider.jsx91
-rw-r--r--web/react/components/suggestion/search_channel_provider.jsx69
-rw-r--r--web/react/components/suggestion/search_suggestion_list.jsx97
-rw-r--r--web/react/components/suggestion/search_user_provider.jsx62
-rw-r--r--web/react/components/suggestion/suggestion_box.jsx163
-rw-r--r--web/react/components/suggestion/suggestion_list.jsx125
-rw-r--r--web/react/components/team_export_tab.jsx124
-rw-r--r--web/react/components/team_general_tab.jsx655
-rw-r--r--web/react/components/team_import_tab.jsx168
-rw-r--r--web/react/components/team_members_dropdown.jsx332
-rw-r--r--web/react/components/team_members_modal.jsx87
-rw-r--r--web/react/components/team_settings.jsx84
-rw-r--r--web/react/components/team_settings_modal.jsx123
-rw-r--r--web/react/components/team_signup_choose_auth.jsx138
-rw-r--r--web/react/components/team_signup_with_email.jsx121
-rw-r--r--web/react/components/team_signup_with_ldap.jsx234
-rw-r--r--web/react/components/team_signup_with_sso.jsx178
-rw-r--r--web/react/components/textbox.jsx263
-rw-r--r--web/react/components/time_since.jsx66
-rw-r--r--web/react/components/toggle_modal_button.jsx73
-rw-r--r--web/react/components/tutorial/tutorial_intro_screens.jsx242
-rw-r--r--web/react/components/tutorial/tutorial_tip.jsx158
-rw-r--r--web/react/components/unread_channel_indicator.jsx32
-rw-r--r--web/react/components/user_list.jsx49
-rw-r--r--web/react/components/user_list_row.jsx64
-rw-r--r--web/react/components/user_profile.jsx126
-rw-r--r--web/react/components/user_settings/custom_theme_chooser.jsx387
-rw-r--r--web/react/components/user_settings/import_theme_modal.jsx215
-rw-r--r--web/react/components/user_settings/manage_command_hooks.jsx679
-rw-r--r--web/react/components/user_settings/manage_incoming_hooks.jsx223
-rw-r--r--web/react/components/user_settings/manage_languages.jsx118
-rw-r--r--web/react/components/user_settings/manage_outgoing_hooks.jsx395
-rw-r--r--web/react/components/user_settings/premade_theme_chooser.jsx58
-rw-r--r--web/react/components/user_settings/user_settings.jsx158
-rw-r--r--web/react/components/user_settings/user_settings_advanced.jsx343
-rw-r--r--web/react/components/user_settings/user_settings_developer.jsx136
-rw-r--r--web/react/components/user_settings/user_settings_display.jsx496
-rw-r--r--web/react/components/user_settings/user_settings_general.jsx815
-rw-r--r--web/react/components/user_settings/user_settings_integrations.jsx208
-rw-r--r--web/react/components/user_settings/user_settings_modal.jsx338
-rw-r--r--web/react/components/user_settings/user_settings_notifications.jsx831
-rw-r--r--web/react/components/user_settings/user_settings_security.jsx472
-rw-r--r--web/react/components/user_settings/user_settings_theme.jsx299
-rw-r--r--web/react/components/view_image.jsx422
-rw-r--r--web/react/components/view_image_popover_bar.jsx81
-rw-r--r--web/react/components/youtube_video.jsx176
183 files changed, 0 insertions, 40124 deletions
diff --git a/web/react/components/about_build_modal.jsx b/web/react/components/about_build_modal.jsx
deleted file mode 100644
index 34b1fdccf..000000000
--- a/web/react/components/about_build_modal.jsx
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-var Modal = ReactBootstrap.Modal;
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class AboutBuildModal extends React.Component {
- constructor(props) {
- super(props);
- this.doHide = this.doHide.bind(this);
- }
-
- doHide() {
- this.props.onModalDismissed();
- }
-
- render() {
- const config = global.window.mm_config;
- const license = global.window.mm_license;
-
- let title = (
- <FormattedMessage
- id='about.teamEditiont0'
- defaultMessage='Team Edition T0'
- />
- );
-
- let licensee;
- if (config.BuildEnterpriseReady === 'true') {
- title = (
- <FormattedMessage
- id='about.teamEditiont1'
- defaultMessage='Team Edition T1'
- />
- );
- if (license.IsLicensed === 'true') {
- title = (
- <FormattedMessage
- id='about.enterpriseEditione1'
- defaultMessage='Enterprise Edition E1'
- />
- );
- licensee = (
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='about.licensed'
- defaultMessage='Licensed by:'
- />
- </div>
- <div className='col-sm-9'>{license.Company}</div>
- </div>
- );
- }
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.doHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='about.title'
- defaultMessage='About Mattermost'
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <h4>{'Mattermost'} {title}</h4>
- {licensee}
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='about.version'
- defaultMessage='Version:'
- />
- </div>
- <div className='col-sm-9'>{config.Version}</div>
- </div>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='about.number'
- defaultMessage='Build Number:'
- />
- </div>
- <div className='col-sm-9'>{config.BuildNumber}</div>
- </div>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='about.date'
- defaultMessage='Build Date:'
- />
- </div>
- <div className='col-sm-9'>{config.BuildDate}</div>
- </div>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='about.hash'
- defaultMessage='Build Hash:'
- />
- </div>
- <div className='col-sm-9'>{config.BuildHash}</div>
- </div>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.doHide}
- >
- <FormattedMessage
- id='about.close'
- defaultMessage='Close'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-AboutBuildModal.defaultProps = {
- show: false
-};
-
-AboutBuildModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onModalDismissed: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/access_history_modal.jsx b/web/react/components/access_history_modal.jsx
deleted file mode 100644
index cd1db32ec..000000000
--- a/web/react/components/access_history_modal.jsx
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-var Modal = ReactBootstrap.Modal;
-import LoadingScreen from './loading_screen.jsx';
-import AuditTable from './audit_table.jsx';
-
-import UserStore from '../stores/user_store.jsx';
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {intlShape, injectIntl, FormattedMessage} from 'mm-intl';
-
-class AccessHistoryModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.onAuditChange = this.onAuditChange.bind(this);
- this.onShow = this.onShow.bind(this);
- this.onHide = this.onHide.bind(this);
-
- const state = this.getStateFromStoresForAudits();
- state.moreInfo = [];
-
- this.state = state;
- }
- getStateFromStoresForAudits() {
- return {
- audits: UserStore.getAudits()
- };
- }
- onShow() {
- AsyncClient.getAudits();
-
- 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() - 150);
- }
- }
- onHide() {
- this.setState({moreInfo: []});
- this.props.onHide();
- }
- componentDidMount() {
- UserStore.addAuditsChangeListener(this.onAuditChange);
-
- if (this.props.show) {
- this.onShow();
- }
- }
- componentDidUpdate(prevProps) {
- if (this.props.show && !prevProps.show) {
- this.onShow();
- }
- }
- componentWillUnmount() {
- UserStore.removeAuditsChangeListener(this.onAuditChange);
- }
- onAuditChange() {
- var newState = this.getStateFromStoresForAudits();
- if (!Utils.areObjectsEqual(newState.audits, this.state.audits)) {
- this.setState(newState);
- }
- }
- render() {
- var content;
- if (this.state.audits.loading) {
- content = (<LoadingScreen/>);
- } else {
- content = (
- <AuditTable
- audits={this.state.audits}
- showIp={true}
- showSession={true}
- />
- );
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.onHide}
- bsSize='large'
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='access_history.title'
- defaultMessage='Access History'
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body ref='modalBody'>
- {content}
- </Modal.Body>
- </Modal>
- );
- }
-}
-
-AccessHistoryModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(AccessHistoryModal);
diff --git a/web/react/components/activity_log_modal.jsx b/web/react/components/activity_log_modal.jsx
deleted file mode 100644
index db366f8ed..000000000
--- a/web/react/components/activity_log_modal.jsx
+++ /dev/null
@@ -1,296 +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 Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-const Modal = ReactBootstrap.Modal;
-import LoadingScreen from './loading_screen.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage, FormattedTime, FormattedDate} from 'mm-intl';
-
-export default class ActivityLogModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitRevoke = this.submitRevoke.bind(this);
- this.onListenerChange = this.onListenerChange.bind(this);
- this.handleMoreInfo = this.handleMoreInfo.bind(this);
- this.onHide = this.onHide.bind(this);
- this.onShow = this.onShow.bind(this);
-
- let state = this.getStateFromStores();
- state.moreInfo = [];
-
- this.state = state;
- }
- getStateFromStores() {
- return {
- sessions: UserStore.getSessions(),
- serverError: null,
- clientError: null
- };
- }
- submitRevoke(altId, e) {
- e.preventDefault();
- var modalContent = $(e.target).closest('.modal-content');
- modalContent.addClass('animation--highlight');
- setTimeout(() => {
- modalContent.removeClass('animation--highlight');
- }, 1500);
- Client.revokeSession(altId,
- function handleRevokeSuccess() {
- AsyncClient.getSessions();
- },
- function handleRevokeError(err) {
- let state = this.getStateFromStores();
- state.serverError = err;
- this.setState(state);
- }.bind(this)
- );
- }
- onShow() {
- AsyncClient.getSessions();
-
- 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() - 150);
- }
- }
- onHide() {
- this.setState({moreInfo: []});
- this.props.onHide();
- }
- componentDidMount() {
- UserStore.addSessionsChangeListener(this.onListenerChange);
-
- if (this.props.show) {
- this.onShow();
- }
- }
- componentDidUpdate(prevProps) {
- if (this.props.show && !prevProps.show) {
- this.onShow();
- }
- }
- componentWillUnmount() {
- UserStore.removeSessionsChangeListener(this.onListenerChange);
- }
- onListenerChange() {
- const newState = this.getStateFromStores();
- if (!Utils.areObjectsEqual(newState.sessions, this.state.sessions)) {
- this.setState(newState);
- }
- }
- handleMoreInfo(index) {
- let newMoreInfo = this.state.moreInfo;
- newMoreInfo[index] = true;
- this.setState({moreInfo: newMoreInfo});
- }
- render() {
- let activityList = [];
-
- for (let i = 0; i < this.state.sessions.length; i++) {
- const currentSession = this.state.sessions[i];
- const lastAccessTime = new Date(currentSession.last_activity_at);
- const firstAccessTime = new Date(currentSession.create_at);
- let devicePlatform = currentSession.props.platform;
- let devicePicture = '';
-
- if (currentSession.props.platform === 'Windows') {
- devicePicture = 'fa fa-windows';
- } else if (currentSession.device_id && currentSession.device_id.indexOf('apple:') === 0) {
- devicePicture = 'fa fa-apple';
- devicePlatform = (
- <FormattedMessage
- id='activity_log_modal.iphoneNativeApp'
- defaultMessage='iPhone Native App'
- />
- );
- } else if (currentSession.device_id && currentSession.device_id.indexOf('android:') === 0) {
- devicePlatform = (
- <FormattedMessage
- id='activity_log_modal.androidNativeApp'
- defaultMessage='Android Native App'
- />
- );
- devicePicture = 'fa fa-android';
- } else if (currentSession.props.platform === 'Macintosh' ||
- currentSession.props.platform === 'iPhone') {
- devicePicture = 'fa fa-apple';
- } else if (currentSession.props.platform === 'Linux') {
- if (currentSession.props.os.indexOf('Android') >= 0) {
- devicePlatform = (
- <FormattedMessage
- id='activity_log_modal.android'
- defaultMessage='Android'
- />
- );
- devicePicture = 'fa fa-android';
- } else {
- devicePicture = 'fa fa-linux';
- }
- }
-
- let moreInfo;
- if (this.state.moreInfo[i]) {
- moreInfo = (
- <div>
- <div>
- <FormattedMessage
- id='activity_log.firstTime'
- defaultMessage='First time active: {date}, {time}'
- values={{
- date: (
- <FormattedDate
- value={firstAccessTime}
- day='2-digit'
- month='long'
- year='numeric'
- />
- ),
- time: (
- <FormattedTime
- value={firstAccessTime}
- hour='2-digit'
- minute='2-digit'
- />
- )
- }}
- />
- </div>
- <div>
- <FormattedMessage
- id='activity_log.os'
- defaultMessage='OS: {os}'
- values={{
- os: currentSession.props.os
- }}
- />
- </div>
- <div>
- <FormattedMessage
- id='activity_log.browser'
- defaultMessage='Browser: {browser}'
- values={{
- browser: currentSession.props.browser
- }}
- />
- </div>
- <div>
- <FormattedMessage
- id='activity_log.sessionId'
- defaultMessage='Session ID: {id}'
- values={{
- id: currentSession.id
- }}
- />
- </div>
- </div>
- );
- } else {
- moreInfo = (
- <a
- className='theme'
- href='#'
- onClick={this.handleMoreInfo.bind(this, i)}
- >
- <FormattedMessage
- id='activity_log.moreInfo'
- defaultMessage='More info'
- />
- </a>
- );
- }
-
- activityList[i] = (
- <div
- key={'activityLogEntryKey' + i}
- className='activity-log__table'
- >
- <div className='activity-log__report'>
- <div className='report__platform'><i className={devicePicture}/>{devicePlatform}</div>
- <div className='report__info'>
- <div>
- <FormattedMessage
- id='activity_log.lastActivity'
- defaultMessage='Last activity: {date}, {time}'
- values={{
- date: (
- <FormattedDate
- value={lastAccessTime}
- day='2-digit'
- month='long'
- year='numeric'
- />
- ),
- time: (
- <FormattedTime
- value={lastAccessTime}
- hour='2-digit'
- minute='2-digit'
- />
- )
- }}
- />
- </div>
- {moreInfo}
- </div>
- </div>
- <div className='activity-log__action'>
- <button
- onClick={this.submitRevoke.bind(this, currentSession.id)}
- className='btn btn-primary'
- >
- <FormattedMessage
- id='activity_log.logout'
- defaultMessage='Logout'
- />
- </button>
- </div>
- </div>
- );
- }
-
- let content;
- if (this.state.sessions.loading) {
- content = <LoadingScreen/>;
- } else {
- content = <form role='form'>{activityList}</form>;
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.onHide}
- bsSize='large'
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='activity_log.activeSessions'
- defaultMessage='Active Sessions'
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body ref='modalBody'>
- <p className='session-help-text'>
- <FormattedMessage
- id='activity_log.sessionsDescription'
- defaultMessage="Sessions are created when you log in to a new browser on a device. Sessions let you use Mattermost without having to log in again for a time period specified by the System Admin. If you want to log out sooner, use the 'Logout' button below to end a session."
- />
- </p>
- {content}
- </Modal.Body>
- </Modal>
- );
- }
-}
-
-ActivityLogModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/admin_console/admin_controller.jsx b/web/react/components/admin_console/admin_controller.jsx
deleted file mode 100644
index 6dca391d0..000000000
--- a/web/react/components/admin_console/admin_controller.jsx
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AdminSidebar from './admin_sidebar.jsx';
-import AdminStore from '../../stores/admin_store.jsx';
-import TeamStore from '../../stores/team_store.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-import LoadingScreen from '../loading_screen.jsx';
-
-import EmailSettingsTab from './email_settings.jsx';
-import LogSettingsTab from './log_settings.jsx';
-import LogsTab from './logs.jsx';
-import AuditsTab from './audits.jsx';
-import FileSettingsTab from './image_settings.jsx';
-import PrivacySettingsTab from './privacy_settings.jsx';
-import RateSettingsTab from './rate_settings.jsx';
-import GitLabSettingsTab from './gitlab_settings.jsx';
-import SqlSettingsTab from './sql_settings.jsx';
-import TeamSettingsTab from './team_settings.jsx';
-import ServiceSettingsTab from './service_settings.jsx';
-import LegalAndSupportSettingsTab from './legal_and_support_settings.jsx';
-import TeamUsersTab from './team_users.jsx';
-import TeamAnalyticsTab from '../analytics/team_analytics.jsx';
-import LdapSettingsTab from './ldap_settings.jsx';
-import LicenseSettingsTab from './license_settings.jsx';
-import SystemAnalyticsTab from '../analytics/system_analytics.jsx';
-
-export default class AdminController extends React.Component {
- constructor(props) {
- super(props);
-
- this.selectTab = this.selectTab.bind(this);
- this.removeSelectedTeam = this.removeSelectedTeam.bind(this);
- this.addSelectedTeam = this.addSelectedTeam.bind(this);
- this.onConfigListenerChange = this.onConfigListenerChange.bind(this);
- this.onAllTeamsListenerChange = this.onAllTeamsListenerChange.bind(this);
-
- var selectedTeams = AdminStore.getSelectedTeams();
- if (selectedTeams == null) {
- selectedTeams = {};
- selectedTeams[TeamStore.getCurrentId()] = 'true';
- AdminStore.saveSelectedTeams(selectedTeams);
- }
-
- this.state = {
- config: AdminStore.getConfig(),
- teams: AdminStore.getAllTeams(),
- selectedTeams,
- selected: props.tab || 'system_analytics',
- selectedTeam: props.teamId || null
- };
- }
-
- componentDidMount() {
- AdminStore.addConfigChangeListener(this.onConfigListenerChange);
- AsyncClient.getConfig();
-
- AdminStore.addAllTeamsChangeListener(this.onAllTeamsListenerChange);
- AsyncClient.getAllTeams();
-
- $('[data-toggle="tooltip"]').tooltip();
- $('[data-toggle="popover"]').popover();
- }
-
- componentWillUnmount() {
- AdminStore.removeConfigChangeListener(this.onConfigListenerChange);
- AdminStore.removeAllTeamsChangeListener(this.onAllTeamsListenerChange);
- }
-
- onConfigListenerChange() {
- this.setState({
- config: AdminStore.getConfig(),
- teams: AdminStore.getAllTeams(),
- selectedTeams: AdminStore.getSelectedTeams(),
- selected: this.state.selected,
- selectedTeam: this.state.selectedTeam
- });
- }
-
- onAllTeamsListenerChange() {
- this.setState({
- config: AdminStore.getConfig(),
- teams: AdminStore.getAllTeams(),
- selectedTeams: AdminStore.getSelectedTeams(),
- selected: this.state.selected,
- selectedTeam: this.state.selectedTeam
-
- });
- }
-
- selectTab(tab, teamId) {
- this.setState({
- config: AdminStore.getConfig(),
- teams: AdminStore.getAllTeams(),
- selectedTeams: AdminStore.getSelectedTeams(),
- selected: tab,
- selectedTeam: teamId
- });
- }
-
- removeSelectedTeam(teamId) {
- var selectedTeams = AdminStore.getSelectedTeams();
- Reflect.deleteProperty(selectedTeams, teamId);
- AdminStore.saveSelectedTeams(selectedTeams);
-
- this.setState({
- config: AdminStore.getConfig(),
- teams: AdminStore.getAllTeams(),
- selectedTeams: AdminStore.getSelectedTeams(),
- selected: this.state.selected,
- selectedTeam: this.state.selectedTeam
- });
- }
-
- addSelectedTeam(teamId) {
- var selectedTeams = AdminStore.getSelectedTeams();
- selectedTeams[teamId] = 'true';
- AdminStore.saveSelectedTeams(selectedTeams);
-
- this.setState({
- config: AdminStore.getConfig(),
- teams: AdminStore.getAllTeams(),
- selectedTeams: AdminStore.getSelectedTeams(),
- selected: this.state.selected,
- selectedTeam: this.state.selectedTeam
- });
- }
-
- render() {
- var tab = <LoadingScreen/>;
-
- if (this.state.config != null) {
- if (this.state.selected === 'email_settings') {
- tab = <EmailSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'log_settings') {
- tab = <LogSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'logs') {
- tab = <LogsTab/>;
- } else if (this.state.selected === 'audits') {
- tab = <AuditsTab/>;
- } else if (this.state.selected === 'image_settings') {
- tab = <FileSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'privacy_settings') {
- tab = <PrivacySettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'rate_settings') {
- tab = <RateSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'gitlab_settings') {
- tab = <GitLabSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'sql_settings') {
- tab = <SqlSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'team_settings') {
- tab = <TeamSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'service_settings') {
- tab = <ServiceSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'legal_and_support_settings') {
- tab = <LegalAndSupportSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'ldap_settings') {
- tab = <LdapSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'license') {
- tab = <LicenseSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'team_users') {
- if (this.state.teams) {
- tab = <TeamUsersTab team={this.state.teams[this.state.selectedTeam]}/>;
- }
- } else if (this.state.selected === 'team_analytics') {
- if (this.state.teams) {
- tab = <TeamAnalyticsTab team={this.state.teams[this.state.selectedTeam]}/>;
- }
- } else if (this.state.selected === 'system_analytics') {
- tab = <SystemAnalyticsTab/>;
- }
- }
-
- return (
- <div
- id='admin_controller'
- className='admin-controller'
- >
- <div
- className='sidebar--menu'
- id='sidebar-menu'
- />
- <AdminSidebar
- selected={this.state.selected}
- selectedTeam={this.state.selectedTeam}
- selectTab={this.selectTab}
- teams={this.state.teams}
- selectedTeams={this.state.selectedTeams}
- removeSelectedTeam={this.removeSelectedTeam}
- addSelectedTeam={this.addSelectedTeam}
- />
- <div className='inner-wrap channel__wrap'>
- <div className='row header'>
- </div>
- <div className='row main'>
- <div
- id='app-content'
- className='app__content admin'
- >
- {tab}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-AdminController.defaultProps = {
-};
-
-AdminController.propTypes = {
- tab: React.PropTypes.string,
- teamId: React.PropTypes.string
-};
diff --git a/web/react/components/admin_console/admin_navbar_dropdown.jsx b/web/react/components/admin_console/admin_navbar_dropdown.jsx
deleted file mode 100644
index ae95f5a3a..000000000
--- a/web/react/components/admin_console/admin_navbar_dropdown.jsx
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../utils/utils.jsx';
-import TeamStore from '../../stores/team_store.jsx';
-
-import Constants from '../../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-import {Link} from 'react-router';
-
-function getStateFromStores() {
- return {currentTeam: TeamStore.getCurrent()};
-}
-
-export default class AdminNavbarDropdown extends React.Component {
- constructor(props) {
- super(props);
- this.blockToggle = false;
-
- this.state = getStateFromStores();
- }
-
- componentDidMount() {
- $(ReactDOM.findDOMNode(this.refs.dropdown)).on('hide.bs.dropdown', () => {
- this.blockToggle = true;
- setTimeout(() => {
- this.blockToggle = false;
- }, 100);
- });
- }
-
- componentWillUnmount() {
- $(ReactDOM.findDOMNode(this.refs.dropdown)).off('hide.bs.dropdown');
- }
-
- render() {
- return (
- <ul className='nav navbar-nav navbar-right'>
- <li
- ref='dropdown'
- className='dropdown'
- >
- <a
- href='#'
- className='dropdown-toggle'
- data-toggle='dropdown'
- role='button'
- aria-expanded='false'
- >
- <span
- className='dropdown__icon'
- dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}}
- />
- </a>
- <ul
- className='dropdown-menu'
- role='menu'
- >
- <li>
- <a
- href={Utils.getWindowLocationOrigin() + '/' + this.state.currentTeam.name}
- >
- <FormattedMessage
- id='admin.nav.switch'
- defaultMessage='Switch to {display_name}'
- values={{
- display_name: this.state.currentTeam.display_name
- }}
- />
- </a>
- </li>
- <li>
- <Link to={Utils.getTeamURLFromAddressBar() + '/logout'}>
- <FormattedMessage
- id='admin.nav.logout'
- defaultMessage='Logout'
- />
- </Link>
- </li>
- <li className='divider'></li>
- <li>
- <a
- target='_blank'
- href='/static/help/help.html'
- >
- <FormattedMessage
- id='admin.nav.help'
- defaultMessage='Help'
- />
- </a>
- </li>
- <li>
- <a
- target='_blank'
- href='/static/help/report_problem.html'
- >
- <FormattedMessage
- id='admin.nav.report'
- defaultMessage='Report a Problem'
- />
- </a>
- </li>
- </ul>
- </li>
- </ul>
- );
- }
-}
diff --git a/web/react/components/admin_console/admin_sidebar.jsx b/web/react/components/admin_console/admin_sidebar.jsx
deleted file mode 100644
index c2f31f569..000000000
--- a/web/react/components/admin_console/admin_sidebar.jsx
+++ /dev/null
@@ -1,492 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AdminSidebarHeader from './admin_sidebar_header.jsx';
-import SelectTeamModal from './select_team_modal.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const Tooltip = ReactBootstrap.Tooltip;
-const OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-export default class AdminSidebar extends React.Component {
- constructor(props) {
- super(props);
-
- this.isSelected = this.isSelected.bind(this);
- this.handleClick = this.handleClick.bind(this);
- this.removeTeam = this.removeTeam.bind(this);
-
- this.showTeamSelect = this.showTeamSelect.bind(this);
- this.teamSelectedModal = this.teamSelectedModal.bind(this);
- this.teamSelectedModalDismissed = this.teamSelectedModalDismissed.bind(this);
-
- this.state = {
- showSelectModal: false
- };
- }
-
- handleClick(name, teamId, e) {
- e.preventDefault();
- this.props.selectTab(name, teamId);
- }
-
- isSelected(name, teamId) {
- if (this.props.selected === name) {
- if (name === 'team_users' || name === 'team_analytics') {
- if (this.props.selectedTeam != null && this.props.selectedTeam === teamId) {
- return 'active';
- }
- } else {
- return 'active';
- }
- }
-
- return '';
- }
-
- removeTeam(teamId, e) {
- e.preventDefault();
- e.stopPropagation();
- Reflect.deleteProperty(this.props.selectedTeams, teamId);
- this.props.removeSelectedTeam(teamId);
-
- if (this.props.selected === 'team_users') {
- if (this.props.selectedTeam != null && this.props.selectedTeam === teamId) {
- this.props.selectTab('service_settings', null);
- }
- }
- }
-
- componentDidMount() {
- if ($(window).width() > 768) {
- $('.nav-pills__container').perfectScrollbar();
- }
- }
-
- showTeamSelect(e) {
- e.preventDefault();
- this.setState({showSelectModal: true});
- }
-
- teamSelectedModal(teamId) {
- this.setState({showSelectModal: false});
- this.props.addSelectedTeam(teamId);
- this.forceUpdate();
- }
-
- teamSelectedModalDismissed() {
- this.setState({showSelectModal: false});
- }
-
- render() {
- var count = '*';
- var teams = (
- <FormattedMessage
- id='admin.sidebar.loading'
- defaultMessage='Loading'
- />
- );
- const removeTooltip = (
- <Tooltip id='remove-team-tooltip'>
- <FormattedMessage
- id='admin.sidebar.rmTeamSidebar'
- defaultMessage='Remove team from sidebar menu'
- />
- </Tooltip>
- );
- const addTeamTooltip = (
- <Tooltip id='add-team-tooltip'>
- <FormattedMessage
- id='admin.sidebar.addTeamSidebar'
- defaultMessage='Add team from sidebar menu'
- />
- </Tooltip>
- );
-
- if (this.props.teams != null) {
- count = '' + Object.keys(this.props.teams).length;
-
- teams = [];
- for (var key in this.props.selectedTeams) {
- if (this.props.selectedTeams.hasOwnProperty(key)) {
- var team = this.props.teams[key];
-
- if (team != null) {
- teams.push(
- <ul
- key={'team_' + team.id}
- className='nav nav__sub-menu'
- >
- <li>
- <a
- href='#'
- onClick={this.handleClick.bind(this, 'team_users', team.id)}
- className={'nav__sub-menu-item ' + this.isSelected('team_users', team.id) + ' ' + this.isSelected('team_analytics', team.id)}
- >
- {team.name}
- <OverlayTrigger
- delayShow={1000}
- placement='top'
- overlay={removeTooltip}
- >
- <span
- className='menu-icon--right menu__close'
- onClick={this.removeTeam.bind(this, team.id)}
- style={{cursor: 'pointer'}}
- >
- {'×'}
- </span>
- </OverlayTrigger>
- </a>
- </li>
- <li>
- <ul className='nav nav__inner-menu'>
- <li>
- <a
- href='#'
- className={this.isSelected('team_users', team.id)}
- onClick={this.handleClick.bind(this, 'team_users', team.id)}
- >
- <FormattedMessage
- id='admin.sidebar.users'
- defaultMessage='- Users'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('team_analytics', team.id)}
- onClick={this.handleClick.bind(this, 'team_analytics', team.id)}
- >
- <FormattedMessage
- id='admin.sidebar.statistics'
- defaultMessage='- Statistics'
- />
- </a>
- </li>
- </ul>
- </li>
- </ul>
- );
- }
- }
- }
- }
-
- let ldapSettings;
- let licenseSettings;
- if (global.window.mm_config.BuildEnterpriseReady === 'true') {
- if (global.window.mm_license.IsLicensed === 'true') {
- ldapSettings = (
- <li>
- <a
- href='#'
- className={this.isSelected('ldap_settings')}
- onClick={this.handleClick.bind(this, 'ldap_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.ldap'
- defaultMessage='LDAP Settings'
- />
- </a>
- </li>
- );
- }
-
- licenseSettings = (
- <li>
- <a
- href='#'
- className={this.isSelected('license')}
- onClick={this.handleClick.bind(this, 'license', null)}
- >
- <FormattedMessage
- id='admin.sidebar.license'
- defaultMessage='Edition and License'
- />
- </a>
- </li>
- );
- }
-
- let audits;
- if (global.window.mm_license.IsLicensed === 'true') {
- audits = (
- <li>
- <a
- href='#'
- className={this.isSelected('audits')}
- onClick={this.handleClick.bind(this, 'audits', null)}
- >
- <FormattedMessage
- id='admin.sidebar.audits'
- defaultMessage='Compliance and Auditing'
- />
- </a>
- </li>
- );
- }
-
- return (
- <div className='sidebar--left sidebar--collapsable'>
- <div>
- <AdminSidebarHeader/>
- <div className='nav-pills__container'>
- <ul className='nav nav-pills nav-stacked'>
- <li>
- <ul className='nav nav__sub-menu'>
- <li>
- <h4>
- <span className='icon fa fa-gear'></span>
- <span>
- <FormattedMessage
- id='admin.sidebar.reports'
- defaultMessage='SITE REPORTS'
- />
- </span>
- </h4>
- </li>
- </ul>
- <ul className='nav nav__sub-menu padded'>
- <li>
- <a
- href='#'
- className={this.isSelected('system_analytics')}
- onClick={this.handleClick.bind(this, 'system_analytics', null)}
- >
- <FormattedMessage
- id='admin.sidebar.view_statistics'
- defaultMessage='View Statistics'
- />
- </a>
- </li>
- </ul>
- <ul className='nav nav__sub-menu'>
- <li>
- <h4>
- <span className='icon fa fa-gear'></span>
- <span>
- <FormattedMessage
- id='admin.sidebar.settings'
- defaultMessage='SETTINGS'
- />
- </span>
- </h4>
- </li>
- </ul>
- <ul className='nav nav__sub-menu padded'>
- <li>
- <a
- href='#'
- className={this.isSelected('service_settings')}
- onClick={this.handleClick.bind(this, 'service_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.service'
- defaultMessage='Service Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('team_settings')}
- onClick={this.handleClick.bind(this, 'team_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.team'
- defaultMessage='Team Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('sql_settings')}
- onClick={this.handleClick.bind(this, 'sql_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.sql'
- defaultMessage='SQL Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('email_settings')}
- onClick={this.handleClick.bind(this, 'email_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.email'
- defaultMessage='Email Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('image_settings')}
- onClick={this.handleClick.bind(this, 'image_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.file'
- defaultMessage='File Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('log_settings')}
- onClick={this.handleClick.bind(this, 'log_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.log'
- defaultMessage='Log Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('rate_settings')}
- onClick={this.handleClick.bind(this, 'rate_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.rate_limit'
- defaultMessage='Rate Limit Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('privacy_settings')}
- onClick={this.handleClick.bind(this, 'privacy_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.privacy'
- defaultMessage='Privacy Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('gitlab_settings')}
- onClick={this.handleClick.bind(this, 'gitlab_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.gitlab'
- defaultMessage='GitLab Settings'
- />
- </a>
- </li>
- {ldapSettings}
- <li>
- <a
- href='#'
- className={this.isSelected('legal_and_support_settings')}
- onClick={this.handleClick.bind(this, 'legal_and_support_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.support'
- defaultMessage='Legal and Support Settings'
- />
- </a>
- </li>
- </ul>
- <ul className='nav nav__sub-menu'>
- <li>
- <h4>
- <span className='icon fa fa-gear'></span>
- <span>
- <FormattedMessage
- id='admin.sidebar.teams'
- defaultMessage='TEAMS ({count})'
- values={{
- count: count
- }}
- />
- </span>
- <span className='menu-icon--right'>
- <OverlayTrigger
- delayShow={1000}
- placement='top'
- overlay={addTeamTooltip}
- >
- <a
- href='#'
- onClick={this.showTeamSelect}
- >
- <i
- className='fa fa-plus'
- ></i>
- </a>
- </OverlayTrigger>
- </span>
- </h4>
- </li>
- </ul>
- <ul className='nav nav__sub-menu padded'>
- <li>
- {teams}
- </li>
- </ul>
- <ul className='nav nav__sub-menu'>
- <li>
- <h4>
- <span className='icon fa fa-gear'></span>
- <span>
- <FormattedMessage
- id='admin.sidebar.other'
- defaultMessage='OTHER'
- />
- </span>
- </h4>
- </li>
- </ul>
- <ul className='nav nav__sub-menu padded'>
- {licenseSettings}
- {audits}
- <li>
- <a
- href='#'
- className={this.isSelected('logs')}
- onClick={this.handleClick.bind(this, 'logs', null)}
- >
- <FormattedMessage
- id='admin.sidebar.logs'
- defaultMessage='Logs'
- />
- </a>
- </li>
- </ul>
- </li>
- </ul>
- </div>
- </div>
-
- <SelectTeamModal
- teams={this.props.teams}
- show={this.state.showSelectModal}
- onModalSubmit={this.teamSelectedModal}
- onModalDismissed={this.teamSelectedModalDismissed}
- />
- </div>
- );
- }
-}
-
-AdminSidebar.propTypes = {
- teams: React.PropTypes.object,
- selectedTeams: React.PropTypes.object,
- removeSelectedTeam: React.PropTypes.func,
- addSelectedTeam: React.PropTypes.func,
- selected: React.PropTypes.string,
- selectedTeam: React.PropTypes.string,
- selectTab: React.PropTypes.func
-};
diff --git a/web/react/components/admin_console/admin_sidebar_header.jsx b/web/react/components/admin_console/admin_sidebar_header.jsx
deleted file mode 100644
index f1281c6ee..000000000
--- a/web/react/components/admin_console/admin_sidebar_header.jsx
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AdminNavbarDropdown from './admin_navbar_dropdown.jsx';
-import UserStore from '../../stores/user_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class SidebarHeader extends React.Component {
- constructor(props) {
- super(props);
-
- this.toggleDropdown = this.toggleDropdown.bind(this);
-
- this.state = {};
- }
-
- toggleDropdown(e) {
- e.preventDefault();
-
- if (this.refs.dropdown.blockToggle) {
- this.refs.dropdown.blockToggle = false;
- return;
- }
-
- $('.team__header').find('.dropdown-toggle').dropdown('toggle');
- }
-
- render() {
- var me = UserStore.getCurrentUser();
- var profilePicture = null;
-
- if (!me) {
- return null;
- }
-
- if (me.last_picture_update) {
- profilePicture = (
- <img
- className='user__picture'
- src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at}
- />
- );
- }
-
- return (
- <div className='team__header theme'>
- <a
- href='#'
- onClick={this.toggleDropdown}
- >
- {profilePicture}
- <div className='header__info'>
- <div className='user__name'>{'@' + me.username}</div>
- <div className='team__name'>
- <FormattedMessage
- id='admin.sidebarHeader.systemConsole'
- defaultMessage='System Console'
- />
- </div>
- </div>
- </a>
- <AdminNavbarDropdown ref='dropdown'/>
- </div>
- );
- }
-}
diff --git a/web/react/components/admin_console/audits.jsx b/web/react/components/admin_console/audits.jsx
deleted file mode 100644
index c519e0a23..000000000
--- a/web/react/components/admin_console/audits.jsx
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import LoadingScreen from '../loading_screen.jsx';
-import AuditTable from '../audit_table.jsx';
-
-import AdminStore from '../../stores/admin_store.jsx';
-
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class Audits extends React.Component {
- constructor(props) {
- super(props);
-
- this.onAuditListenerChange = this.onAuditListenerChange.bind(this);
- this.reload = this.reload.bind(this);
-
- this.state = {
- audits: AdminStore.getAudits()
- };
- }
-
- componentDidMount() {
- AdminStore.addAuditChangeListener(this.onAuditListenerChange);
- AsyncClient.getServerAudits();
- }
-
- componentWillUnmount() {
- AdminStore.removeAuditChangeListener(this.onAuditListenerChange);
- }
-
- onAuditListenerChange() {
- this.setState({
- audits: AdminStore.getAudits()
- });
- }
-
- reload() {
- AdminStore.saveAudits(null);
- this.setState({
- audits: null
- });
-
- AsyncClient.getServerAudits();
- }
-
- render() {
- var content = null;
-
- if (global.window.mm_license.IsLicensed !== 'true') {
- return <div/>;
- }
-
- if (this.state.audits === null) {
- content = <LoadingScreen/>;
- } else {
- content = (
- <div style={{margin: '10px'}}>
- <AuditTable
- audits={this.state.audits}
- showUserId={true}
- showIp={true}
- showSession={true}
- />
- </div>
- );
- }
-
- return (
- <div className='panel'>
- <h3>
- <FormattedMessage
- id='admin.audits.title'
- defaultMessage='User Activity'
- />
- </h3>
- <button
- type='submit'
- className='btn btn-primary'
- onClick={this.reload}
- >
- <FormattedMessage
- id='admin.audits.reload'
- defaultMessage='Reload'
- />
- </button>
- <div className='log__panel'>
- {content}
- </div>
- </div>
- );
- }
-}
diff --git a/web/react/components/admin_console/email_settings.jsx b/web/react/components/admin_console/email_settings.jsx
deleted file mode 100644
index 1d8f9c1dc..000000000
--- a/web/react/components/admin_console/email_settings.jsx
+++ /dev/null
@@ -1,957 +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 AsyncClient from '../../utils/async_client.jsx';
-import crypto from 'crypto';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-var holders = defineMessages({
- notificationDisplayExample: {
- id: 'admin.email.notificationDisplayExample',
- defaultMessage: 'Ex: "Mattermost Notification", "System", "No-Reply"'
- },
- notificationEmailExample: {
- id: 'admin.email.notificationEmailExample',
- defaultMessage: 'Ex: "mattermost@yourcompany.com", "admin@yourcompany.com"'
- },
- smtpUsernameExample: {
- id: 'admin.email.smtpUsernameExample',
- defaultMessage: 'Ex: "admin@yourcompany.com", "AKIADTOVBGERKLCBV"'
- },
- smtpPasswordExample: {
- id: 'admin.email.smtpPasswordExample',
- defaultMessage: 'Ex: "yourpassword", "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
- },
- smtpServerExample: {
- id: 'admin.email.smtpServerExample',
- defaultMessage: 'Ex: "smtp.yourcompany.com", "email-smtp.us-east-1.amazonaws.com"'
- },
- smtpPortExample: {
- id: 'admin.email.smtpPortExample',
- defaultMessage: 'Ex: "25", "465"'
- },
- connectionSecurityNone: {
- id: 'admin.email.connectionSecurityNone',
- defaultMessage: 'None'
- },
- connectionSecurityTls: {
- id: 'admin.email.connectionSecurityTls',
- defaultMessage: 'TLS (Recommended)'
- },
- connectionSecurityStart: {
- id: 'admin.email.connectionSecurityStart',
- defaultMessage: 'STARTTLS'
- },
- inviteSaltExample: {
- id: 'admin.email.inviteSaltExample',
- defaultMessage: 'Ex "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo"'
- },
- passwordSaltExample: {
- id: 'admin.email.passwordSaltExample',
- defaultMessage: 'Ex "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo"'
- },
- pushServerEx: {
- id: 'admin.email.pushServerEx',
- defaultMessage: 'E.g.: "http://push-test.mattermost.com"'
- },
- testing: {
- id: 'admin.email.testing',
- defaultMessage: 'Testing...'
- },
- saving: {
- id: 'admin.email.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class EmailSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleTestConnection = this.handleTestConnection.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.buildConfig = this.buildConfig.bind(this);
- this.handleGenerateInvite = this.handleGenerateInvite.bind(this);
- this.handleGenerateReset = this.handleGenerateReset.bind(this);
-
- this.state = {
- sendEmailNotifications: this.props.config.EmailSettings.SendEmailNotifications,
- sendPushNotifications: this.props.config.EmailSettings.SendPushNotifications,
- saveNeeded: false,
- serverError: null,
- emailSuccess: null,
- emailFail: null
- };
- }
-
- handleChange(action) {
- var s = {saveNeeded: true, serverError: this.state.serverError};
-
- if (action === 'sendEmailNotifications_true') {
- s.sendEmailNotifications = true;
- }
-
- if (action === 'sendEmailNotifications_false') {
- s.sendEmailNotifications = false;
- }
-
- if (action === 'sendPushNotifications_true') {
- s.sendPushNotifications = true;
- }
-
- if (action === 'sendPushNotifications_false') {
- s.sendPushNotifications = false;
- }
-
- this.setState(s);
- }
-
- buildConfig() {
- var config = this.props.config;
- config.EmailSettings.EnableSignUpWithEmail = ReactDOM.findDOMNode(this.refs.allowSignUpWithEmail).checked;
- config.EmailSettings.EnableSignInWithEmail = ReactDOM.findDOMNode(this.refs.allowSignInWithEmail).checked;
- config.EmailSettings.EnableSignInWithUsername = ReactDOM.findDOMNode(this.refs.allowSignInWithUsername).checked;
- config.EmailSettings.SendEmailNotifications = ReactDOM.findDOMNode(this.refs.sendEmailNotifications).checked;
- config.EmailSettings.SendPushNotifications = ReactDOM.findDOMNode(this.refs.sendPushNotifications).checked;
- config.EmailSettings.RequireEmailVerification = ReactDOM.findDOMNode(this.refs.requireEmailVerification).checked;
- config.EmailSettings.FeedbackName = ReactDOM.findDOMNode(this.refs.feedbackName).value.trim();
- config.EmailSettings.FeedbackEmail = ReactDOM.findDOMNode(this.refs.feedbackEmail).value.trim();
- config.EmailSettings.SMTPServer = ReactDOM.findDOMNode(this.refs.SMTPServer).value.trim();
- config.EmailSettings.PushNotificationServer = ReactDOM.findDOMNode(this.refs.PushNotificationServer).value.trim();
- config.EmailSettings.SMTPPort = ReactDOM.findDOMNode(this.refs.SMTPPort).value.trim();
- config.EmailSettings.SMTPUsername = ReactDOM.findDOMNode(this.refs.SMTPUsername).value.trim();
- config.EmailSettings.SMTPPassword = ReactDOM.findDOMNode(this.refs.SMTPPassword).value.trim();
- config.EmailSettings.ConnectionSecurity = ReactDOM.findDOMNode(this.refs.ConnectionSecurity).value.trim();
-
- config.EmailSettings.InviteSalt = ReactDOM.findDOMNode(this.refs.InviteSalt).value.trim();
- if (config.EmailSettings.InviteSalt === '') {
- config.EmailSettings.InviteSalt = crypto.randomBytes(256).toString('base64').substring(0, 32);
- ReactDOM.findDOMNode(this.refs.InviteSalt).value = config.EmailSettings.InviteSalt;
- }
-
- config.EmailSettings.PasswordResetSalt = ReactDOM.findDOMNode(this.refs.PasswordResetSalt).value.trim();
- if (config.EmailSettings.PasswordResetSalt === '') {
- config.EmailSettings.PasswordResetSalt = crypto.randomBytes(256).toString('base64').substring(0, 32);
- ReactDOM.findDOMNode(this.refs.PasswordResetSalt).value = config.EmailSettings.PasswordResetSalt;
- }
-
- return config;
- }
-
- handleGenerateInvite(e) {
- e.preventDefault();
- ReactDOM.findDOMNode(this.refs.InviteSalt).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleGenerateReset(e) {
- e.preventDefault();
- ReactDOM.findDOMNode(this.refs.PasswordResetSalt).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleTestConnection(e) {
- e.preventDefault();
- $('#connection-button').button('loading');
-
- var config = this.buildConfig();
-
- Client.testEmail(
- config,
- () => {
- this.setState({
- sendEmailNotifications: config.EmailSettings.SendEmailNotifications,
- serverError: null,
- saveNeeded: true,
- emailSuccess: true,
- emailFail: null
- });
- $('#connection-button').button('reset');
- },
- (err) => {
- this.setState({
- sendEmailNotifications: config.EmailSettings.SendEmailNotifications,
- serverError: null,
- saveNeeded: true,
- emailSuccess: null,
- emailFail: err.message + ' - ' + err.detailed_error
- });
- $('#connection-button').button('reset');
- }
- );
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.buildConfig();
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- sendEmailNotifications: config.EmailSettings.SendEmailNotifications,
- serverError: null,
- saveNeeded: false,
- emailSuccess: null,
- emailFail: null
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- sendEmailNotifications: config.EmailSettings.SendEmailNotifications,
- serverError: err.message,
- saveNeeded: true,
- emailSuccess: null,
- emailFail: null
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- var emailSuccess = '';
- if (this.state.emailSuccess) {
- emailSuccess = (
- <div className='alert alert-success'>
- <i className='fa fa-check'></i>
- <FormattedMessage
- id='admin.email.emailSuccess'
- defaultMessage='No errors were reported while sending an email. Please check your inbox to make sure.'
- />
- </div>
- );
- }
-
- var emailFail = '';
- if (this.state.emailFail) {
- emailSuccess = (
- <div className='alert alert-warning'>
- <i className='fa fa-warning'></i>
- <FormattedMessage
- id='admin.email.emailFail'
- defaultMessage='Connection unsuccessful: {error}'
- values={{
- error: this.state.emailFail
- }}
- />
- </div>
- );
- }
-
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.email.emailSettings'
- defaultMessage='Email Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='allowSignUpWithEmail'
- >
- <FormattedMessage
- id='admin.email.allowSignupTitle'
- defaultMessage='Allow Sign Up With Email: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='allowSignUpWithEmail'
- value='true'
- ref='allowSignUpWithEmail'
- defaultChecked={this.props.config.EmailSettings.EnableSignUpWithEmail}
- onChange={this.handleChange.bind(this, 'allowSignUpWithEmail_true')}
- />
- <FormattedMessage
- id='admin.email.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='allowSignUpWithEmail'
- value='false'
- defaultChecked={!this.props.config.EmailSettings.EnableSignUpWithEmail}
- onChange={this.handleChange.bind(this, 'allowSignUpWithEmail_false')}
- />
- <FormattedMessage
- id='admin.email.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.allowSignupDescription'
- defaultMessage='When true, Mattermost allows team creation and account signup using email and password. This value should be false only when you want to limit signup to a single-sign-on service like OAuth or LDAP.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='allowSignInWithEmail'
- >
- <FormattedMessage
- id='admin.email.allowEmailSignInTitle'
- defaultMessage='Allow Sign In With Email: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='allowSignInWithEmail'
- value='true'
- ref='allowSignInWithEmail'
- defaultChecked={this.props.config.EmailSettings.EnableSignInWithEmail}
- onChange={this.handleChange.bind(this, 'allowSignInWithEmail_true')}
- />
- <FormattedMessage
- id='admin.email.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='allowSignInWithEmail'
- value='false'
- defaultChecked={!this.props.config.EmailSettings.EnableSignInWithEmail}
- onChange={this.handleChange.bind(this, 'allowSignInWithEmail_false')}
- />
- <FormattedMessage
- id='admin.email.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.allowEmailSignInDescription'
- defaultMessage='When true, Mattermost allows users to sign in using their email and password.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='allowSignInWithUsername'
- >
- <FormattedMessage
- id='admin.email.allowUsernameSignInTitle'
- defaultMessage='Allow Sign In With Username: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='allowSignInWithUsername'
- value='true'
- ref='allowSignInWithUsername'
- defaultChecked={this.props.config.EmailSettings.EnableSignInWithUsername}
- onChange={this.handleChange.bind(this, 'allowSignInWithUsername_true')}
- />
- <FormattedMessage
- id='admin.email.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='allowSignInWithUsername'
- value='false'
- defaultChecked={!this.props.config.EmailSettings.EnableSignInWithUsername}
- onChange={this.handleChange.bind(this, 'allowSignInWithUsername_false')}
- />
- <FormattedMessage
- id='admin.email.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.allowUsernameSignInDescription'
- defaultMessage='When true, Mattermost allows users to sign in using their username and password. This setting is typically only used when email verification is disabled.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='sendEmailNotifications'
- >
- <FormattedMessage
- id='admin.email.notificationsTitle'
- defaultMessage='Send Email Notifications: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='sendEmailNotifications'
- value='true'
- ref='sendEmailNotifications'
- defaultChecked={this.props.config.EmailSettings.SendEmailNotifications}
- onChange={this.handleChange.bind(this, 'sendEmailNotifications_true')}
- />
- <FormattedMessage
- id='admin.email.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='sendEmailNotifications'
- value='false'
- defaultChecked={!this.props.config.EmailSettings.SendEmailNotifications}
- onChange={this.handleChange.bind(this, 'sendEmailNotifications_false')}
- />
- <FormattedMessage
- id='admin.email.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedHTMLMessage
- id='admin.email.notificationsDescription'
- defaultMessage='Typically set to true in production. When true, Mattermost attempts to send email notifications. Developers may set this field to false to skip email setup for faster development.<br />Setting this to true removes the Preview Mode banner (requires logging out and logging back in after setting is changed).'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='requireEmailVerification'
- >
- <FormattedMessage
- id='admin.email.requireVerificationTitle'
- defaultMessage='Require Email Verification: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='requireEmailVerification'
- value='true'
- ref='requireEmailVerification'
- defaultChecked={this.props.config.EmailSettings.RequireEmailVerification}
- onChange={this.handleChange.bind(this, 'requireEmailVerification_true')}
- disabled={!this.state.sendEmailNotifications}
- />
- <FormattedMessage
- id='admin.email.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='requireEmailVerification'
- value='false'
- defaultChecked={!this.props.config.EmailSettings.RequireEmailVerification}
- onChange={this.handleChange.bind(this, 'requireEmailVerification_false')}
- disabled={!this.state.sendEmailNotifications}
- />
- <FormattedMessage
- id='admin.email.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.requireVerificationDescription'
- defaultMessage='Typically set to true in production. When true, Mattermost requires email verification after account creation prior to allowing login. Developers may set this field to false so skip sending verification emails for faster development.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='feedbackName'
- >
- <FormattedMessage
- id='admin.email.notificationDisplayTitle'
- defaultMessage='Notification Display Name:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='feedbackName'
- ref='feedbackName'
- placeholder={formatMessage(holders.notificationDisplayExample)}
- defaultValue={this.props.config.EmailSettings.FeedbackName}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.notificationDisplayDescription'
- defaultMessage='Display name on email account used when sending notification emails from Mattermost.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='feedbackEmail'
- >
- <FormattedMessage
- id='admin.email.notificationEmailTitle'
- defaultMessage='Notification Email Address:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='email'
- className='form-control'
- id='feedbackEmail'
- ref='feedbackEmail'
- placeholder={formatMessage(holders.notificationEmailExample)}
- defaultValue={this.props.config.EmailSettings.FeedbackEmail}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.notificationEmailDescription'
- defaultMessage='Email address displayed on email account used when sending notification emails from Mattermost.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SMTPUsername'
- >
- <FormattedMessage
- id='admin.email.smtpUsernameTitle'
- defaultMessage='SMTP Username:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SMTPUsername'
- ref='SMTPUsername'
- placeholder={formatMessage(holders.smtpUsernameExample)}
- defaultValue={this.props.config.EmailSettings.SMTPUsername}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.smtpUsernameDescription'
- defaultMessage=' Obtain this credential from administrator setting up your email server.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SMTPPassword'
- >
- <FormattedMessage
- id='admin.email.smtpPasswordTitle'
- defaultMessage='SMTP Password:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SMTPPassword'
- ref='SMTPPassword'
- placeholder={formatMessage(holders.smtpPasswordExample)}
- defaultValue={this.props.config.EmailSettings.SMTPPassword}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.smtpPasswordDescription'
- defaultMessage=' Obtain this credential from administrator setting up your email server.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SMTPServer'
- >
- <FormattedMessage
- id='admin.email.smtpServerTitle'
- defaultMessage='SMTP Server:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SMTPServer'
- ref='SMTPServer'
- placeholder={formatMessage(holders.smtpServerExample)}
- defaultValue={this.props.config.EmailSettings.SMTPServer}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.smtpServerDescription'
- defaultMessage='Location of SMTP email server.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SMTPPort'
- >
- <FormattedMessage
- id='admin.email.smtpPortTitle'
- defaultMessage='SMTP Port:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SMTPPort'
- ref='SMTPPort'
- placeholder={formatMessage(holders.smtpPortExample)}
- defaultValue={this.props.config.EmailSettings.SMTPPort}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.smtpPortDescription'
- defaultMessage='Port of SMTP email server.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ConnectionSecurity'
- >
- <FormattedMessage
- id='admin.email.connectionSecurityTitle'
- defaultMessage='Connection Security:'
- />
- </label>
- <div className='col-sm-8'>
- <select
- className='form-control'
- id='ConnectionSecurity'
- ref='ConnectionSecurity'
- defaultValue={this.props.config.EmailSettings.ConnectionSecurity}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- >
- <option value=''>{formatMessage(holders.connectionSecurityNone)}</option>
- <option value='TLS'>{formatMessage(holders.connectionSecurityTls)}</option>
- <option value='STARTTLS'>{formatMessage(holders.connectionSecurityStart)}</option>
- </select>
- <div className='help-text'>
- <table
- className='table table-bordered'
- cellPadding='5'
- >
- <tbody>
- <tr><td className='help-text'>
- <FormattedMessage
- id='admin.email.connectionSecurityNone'
- defaultMessage='None'
- />
- </td><td className='help-text'>
- <FormattedMessage
- id='admin.email.connectionSecurityNoneDescription'
- defaultMessage='Mattermost will send email over an unsecure connection.'
- />
- </td></tr>
- <tr><td className='help-text'>{'TLS'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.email.connectionSecurityTlsDescription'
- defaultMessage='Encrypts the communication between Mattermost and your email server.'
- />
- </td></tr>
- <tr><td className='help-text'>{'STARTTLS'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.email.connectionSecurityStartDescription'
- defaultMessage='Takes an existing insecure connection and attempts to upgrade it to a secure connection using TLS.'
- />
- </td></tr>
- </tbody>
- </table>
- </div>
- <div className='help-text'>
- <button
- className='btn btn-default'
- onClick={this.handleTestConnection}
- disabled={!this.state.sendEmailNotifications}
- id='connection-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.testing)}
- >
- <FormattedMessage
- id='admin.email.connectionSecurityTest'
- defaultMessage='Test Connection'
- />
- </button>
- {emailSuccess}
- {emailFail}
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='InviteSalt'
- >
- <FormattedMessage
- id='admin.email.inviteSaltTitle'
- defaultMessage='Invite Salt:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='InviteSalt'
- ref='InviteSalt'
- placeholder={formatMessage(holders.inviteSaltExample)}
- defaultValue={this.props.config.EmailSettings.InviteSalt}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.inviteSaltDescription'
- defaultMessage='32-character salt added to signing of email invites. Randomly generated on install. Click "Re-Generate" to create new salt.'
- />
- </p>
- <div className='help-text'>
- <button
- className='btn btn-default'
- onClick={this.handleGenerateInvite}
- disabled={!this.state.sendEmailNotifications}
- >
- <FormattedMessage
- id='admin.email.regenerate'
- defaultMessage='Re-Generate'
- />
- </button>
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PasswordResetSalt'
- >
- <FormattedMessage
- id='admin.email.passwordSaltTitle'
- defaultMessage='Password Reset Salt:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PasswordResetSalt'
- ref='PasswordResetSalt'
- placeholder={formatMessage(holders.passwordSaltExample)}
- defaultValue={this.props.config.EmailSettings.PasswordResetSalt}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.passwordSaltDescription'
- defaultMessage='32-character salt added to signing of password reset emails. Randomly generated on install. Click "Re-Generate" to create new salt.'
- />
- </p>
- <div className='help-text'>
- <button
- className='btn btn-default'
- onClick={this.handleGenerateReset}
- disabled={!this.state.sendEmailNotifications}
- >
- <FormattedMessage
- id='admin.email.regenerate'
- defaultMessage='Re-Generate'
- />
- </button>
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='sendPushNotifications'
- >
- <FormattedMessage
- id='admin.email.pushTitle'
- defaultMessage='Send Push Notifications: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='sendPushNotifications'
- value='true'
- ref='sendPushNotifications'
- defaultChecked={this.props.config.EmailSettings.SendPushNotifications}
- onChange={this.handleChange.bind(this, 'sendPushNotifications_true')}
- />
- <FormattedMessage
- id='admin.email.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='sendPushNotifications'
- value='false'
- defaultChecked={!this.props.config.EmailSettings.SendPushNotifications}
- onChange={this.handleChange.bind(this, 'sendPushNotifications_false')}
- />
- <FormattedMessage
- id='admin.email.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.pushDesc'
- defaultMessage='Typically set to true in production. When true, Mattermost attempts to send iOS and Android push notifications through the push notification server.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PushNotificationServer'
- >
- <FormattedMessage
- id='admin.email.pushServerTitle'
- defaultMessage='Push Notification Server:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PushNotificationServer'
- ref='PushNotificationServer'
- placeholder={formatMessage(holders.pushServerEx)}
- defaultValue={this.props.config.EmailSettings.PushNotificationServer}
- onChange={this.handleChange}
- disabled={!this.state.sendPushNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.pushServerDesc'
- defaultMessage='Location of Mattermost push notification service you can set up behind your firewall using https://github.com/mattermost/push-proxy. For testing you can use http://push-test.mattermost.com, which connects to the sample Mattermost iOS app in the public Apple AppStore. Please do not use test service for production deployments.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.email.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-EmailSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(EmailSettings);
diff --git a/web/react/components/admin_console/gitlab_settings.jsx b/web/react/components/admin_console/gitlab_settings.jsx
deleted file mode 100644
index ad9950eea..000000000
--- a/web/react/components/admin_console/gitlab_settings.jsx
+++ /dev/null
@@ -1,379 +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 AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const holders = defineMessages({
- clientIdExample: {
- id: 'admin.gitlab.clientIdExample',
- defaultMessage: 'Ex "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
- },
- clientSecretExample: {
- id: 'admin.gitlab.clientSecretExample',
- defaultMessage: 'Ex "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
- },
- authExample: {
- id: 'admin.gitlab.authExample',
- defaultMessage: 'Ex ""'
- },
- tokenExample: {
- id: 'admin.gitlab.tokenExample',
- defaultMessage: 'Ex ""'
- },
- userExample: {
- id: 'admin.gitlab.userExample',
- defaultMessage: 'Ex ""'
- },
- saving: {
- id: 'admin.gitlab.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class GitLabSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- Enable: this.props.config.GitLabSettings.Enable,
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange(action) {
- var s = {saveNeeded: true, serverError: this.state.serverError};
-
- if (action === 'EnableTrue') {
- s.Enable = true;
- }
-
- if (action === 'EnableFalse') {
- s.Enable = false;
- }
-
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.GitLabSettings.Enable = ReactDOM.findDOMNode(this.refs.Enable).checked;
- config.GitLabSettings.Secret = ReactDOM.findDOMNode(this.refs.Secret).value.trim();
- config.GitLabSettings.Id = ReactDOM.findDOMNode(this.refs.Id).value.trim();
- config.GitLabSettings.AuthEndpoint = ReactDOM.findDOMNode(this.refs.AuthEndpoint).value.trim();
- config.GitLabSettings.TokenEndpoint = ReactDOM.findDOMNode(this.refs.TokenEndpoint).value.trim();
- config.GitLabSettings.UserApiEndpoint = ReactDOM.findDOMNode(this.refs.UserApiEndpoint).value.trim();
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
-
- <h3>
- <FormattedMessage
- id='admin.gitlab.settingsTitle'
- defaultMessage='GitLab Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='Enable'
- >
- <FormattedMessage
- id='admin.gitlab.enableTitle'
- defaultMessage='Enable Sign Up With GitLab: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='Enable'
- value='true'
- ref='Enable'
- defaultChecked={this.props.config.GitLabSettings.Enable}
- onChange={this.handleChange.bind(this, 'EnableTrue')}
- />
- <FormattedMessage
- id='admin.gitlab.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='Enable'
- value='false'
- defaultChecked={!this.props.config.GitLabSettings.Enable}
- onChange={this.handleChange.bind(this, 'EnableFalse')}
- />
- <FormattedMessage
- id='admin.gitlab.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.gitlab.enableDescription'
- defaultMessage='When true, Mattermost allows team creation and account signup using GitLab OAuth.'
- />
- <br/>
- </p>
- <div className='help-text'>
- <FormattedHTMLMessage
- id='admin.gitlab.EnableHtmlDesc'
- defaultMessage='<ol><li>Log in to your GitLab account and go to Profile Settings -> Applications.</li><li>Enter Redirect URIs "<your-mattermost-url>/login/gitlab/complete" (example: http://localhost:8065/login/gitlab/complete) and "<your-mattermost-url>/signup/gitlab/complete". </li><li>Then use "Secret" and "Id" fields from GitLab to complete the options below.</li><li>Complete the Endpoint URLs below. </li></ol>'
- />
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='Id'
- >
- <FormattedMessage
- id='admin.gitlab.clientIdTitle'
- defaultMessage='Id:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='Id'
- ref='Id'
- placeholder={formatMessage(holders.clientIdExample)}
- defaultValue={this.props.config.GitLabSettings.Id}
- onChange={this.handleChange}
- disabled={!this.state.Enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.gitlab.clientIdDescription'
- defaultMessage='Obtain this value via the instructions above for logging into GitLab'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='Secret'
- >
- <FormattedMessage
- id='admin.gitlab.clientSecretTitle'
- defaultMessage='Secret:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='Secret'
- ref='Secret'
- placeholder={formatMessage(holders.clientSecretExample)}
- defaultValue={this.props.config.GitLabSettings.Secret}
- onChange={this.handleChange}
- disabled={!this.state.Enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.gitab.clientSecretDescription'
- defaultMessage='Obtain this value via the instructions above for logging into GitLab.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AuthEndpoint'
- >
- <FormattedMessage
- id='admin.gitlab.authTitle'
- defaultMessage='Auth Endpoint:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AuthEndpoint'
- ref='AuthEndpoint'
- placeholder={formatMessage(holders.authExample)}
- defaultValue={this.props.config.GitLabSettings.AuthEndpoint}
- onChange={this.handleChange}
- disabled={!this.state.Enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.gitlab.authDescription'
- defaultMessage='Enter https://<your-gitlab-url>/oauth/authorize (example https://example.com:3000/oauth/authorize). Make sure you use HTTP or HTTPS in your URL depending on your server configuration.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='TokenEndpoint'
- >
- <FormattedMessage
- id='admin.gitlab.tokenTitle'
- defaultMessage='Token Endpoint:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='TokenEndpoint'
- ref='TokenEndpoint'
- placeholder={formatMessage(holders.tokenExample)}
- defaultValue={this.props.config.GitLabSettings.TokenEndpoint}
- onChange={this.handleChange}
- disabled={!this.state.Enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.gitlab.tokenDescription'
- defaultMessage='Enter https://<your-gitlab-url>/oauth/token. Make sure you use HTTP or HTTPS in your URL depending on your server configuration.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='UserApiEndpoint'
- >
- <FormattedMessage
- id='admin.gitlab.userTitle'
- defaultMessage='User API Endpoint:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='UserApiEndpoint'
- ref='UserApiEndpoint'
- placeholder={formatMessage(holders.userExample)}
- defaultValue={this.props.config.GitLabSettings.UserApiEndpoint}
- onChange={this.handleChange}
- disabled={!this.state.Enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.gitlab.userDescription'
- defaultMessage='Enter https://<your-gitlab-url>/api/v3/user. Make sure you use HTTP or HTTPS in your URL depending on your server configuration.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.gitlab.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-//config.GitLabSettings.Scope = ReactDOM.findDOMNode(this.refs.Scope).value.trim();
-// <div className='form-group'>
-// <label
-// className='control-label col-sm-4'
-// htmlFor='Scope'
-// >
-// {'Scope:'}
-// </label>
-// <div className='col-sm-8'>
-// <input
-// type='text'
-// className='form-control'
-// id='Scope'
-// ref='Scope'
-// placeholder='Not currently used by GitLab. Please leave blank'
-// defaultValue={this.props.config.GitLabSettings.Scope}
-// onChange={this.handleChange}
-// disabled={!this.state.Allow}
-// />
-// <p className='help-text'>{'This field is not yet used by GitLab OAuth. Other OAuth providers may use this field to specify the scope of account data from OAuth provider that is sent to Mattermost.'}</p>
-// </div>
-// </div>
-
-GitLabSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(GitLabSettings);
diff --git a/web/react/components/admin_console/image_settings.jsx b/web/react/components/admin_console/image_settings.jsx
deleted file mode 100644
index 86f78e093..000000000
--- a/web/react/components/admin_console/image_settings.jsx
+++ /dev/null
@@ -1,688 +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 AsyncClient from '../../utils/async_client.jsx';
-import crypto from 'crypto';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- storeLocal: {
- id: 'admin.image.storeLocal',
- defaultMessage: 'Local File System'
- },
- storeAmazonS3: {
- id: 'admin.image.storeAmazonS3',
- defaultMessage: 'Amazon S3'
- },
- localExample: {
- id: 'admin.image.localExample',
- defaultMessage: 'Ex "./data/"'
- },
- amazonS3IdExample: {
- id: 'admin.image.amazonS3IdExample',
- defaultMessage: 'Ex "AKIADTOVBGERKLCBV"'
- },
- amazonS3SecretExample: {
- id: 'admin.image.amazonS3SecretExample',
- defaultMessage: 'Ex "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
- },
- amazonS3BucketExample: {
- id: 'admin.image.amazonS3BucketExample',
- defaultMessage: 'Ex "mattermost-media"'
- },
- amazonS3RegionExample: {
- id: 'admin.image.amazonS3RegionExample',
- defaultMessage: 'Ex "us-east-1"'
- },
- thumbWidthExample: {
- id: 'admin.image.thumbWidthExample',
- defaultMessage: 'Ex "120"'
- },
- thumbHeightExample: {
- id: 'admin.image.thumbHeightExample',
- defaultMessage: 'Ex "100"'
- },
- previewWidthExample: {
- id: 'admin.image.previewWidthExample',
- defaultMessage: 'Ex "1024"'
- },
- previewHeightExample: {
- id: 'admin.image.previewHeightExample',
- defaultMessage: 'Ex "0"'
- },
- profileWidthExample: {
- id: 'admin.image.profileWidthExample',
- defaultMessage: 'Ex "1024"'
- },
- profileHeightExample: {
- id: 'admin.image.profileHeightExample',
- defaultMessage: 'Ex "0"'
- },
- publicLinkExample: {
- id: 'admin.image.publicLinkExample',
- defaultMessage: 'Ex "gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6"'
- },
- saving: {
- id: 'admin.image.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class FileSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleGenerate = this.handleGenerate.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null,
- DriverName: this.props.config.FileSettings.DriverName
- };
- }
-
- handleChange(action) {
- var s = {saveNeeded: true, serverError: this.state.serverError};
-
- if (action === 'DriverName') {
- s.DriverName = ReactDOM.findDOMNode(this.refs.DriverName).value;
- }
-
- this.setState(s);
- }
-
- handleGenerate(e) {
- e.preventDefault();
- ReactDOM.findDOMNode(this.refs.PublicLinkSalt).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.FileSettings.DriverName = ReactDOM.findDOMNode(this.refs.DriverName).value;
- config.FileSettings.Directory = ReactDOM.findDOMNode(this.refs.Directory).value;
- config.FileSettings.AmazonS3AccessKeyId = ReactDOM.findDOMNode(this.refs.AmazonS3AccessKeyId).value;
- config.FileSettings.AmazonS3SecretAccessKey = ReactDOM.findDOMNode(this.refs.AmazonS3SecretAccessKey).value;
- config.FileSettings.AmazonS3Bucket = ReactDOM.findDOMNode(this.refs.AmazonS3Bucket).value;
- config.FileSettings.AmazonS3Region = ReactDOM.findDOMNode(this.refs.AmazonS3Region).value;
- config.FileSettings.EnablePublicLink = ReactDOM.findDOMNode(this.refs.EnablePublicLink).checked;
-
- config.FileSettings.PublicLinkSalt = ReactDOM.findDOMNode(this.refs.PublicLinkSalt).value.trim();
-
- if (config.FileSettings.PublicLinkSalt === '') {
- config.FileSettings.PublicLinkSalt = crypto.randomBytes(256).toString('base64').substring(0, 32);
- ReactDOM.findDOMNode(this.refs.PublicLinkSalt).value = config.FileSettings.PublicLinkSalt;
- }
-
- var thumbnailWidth = 120;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.ThumbnailWidth).value, 10))) {
- thumbnailWidth = parseInt(ReactDOM.findDOMNode(this.refs.ThumbnailWidth).value, 10);
- }
- config.FileSettings.ThumbnailWidth = thumbnailWidth;
- ReactDOM.findDOMNode(this.refs.ThumbnailWidth).value = thumbnailWidth;
-
- var thumbnailHeight = 100;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.ThumbnailHeight).value, 10))) {
- thumbnailHeight = parseInt(ReactDOM.findDOMNode(this.refs.ThumbnailHeight).value, 10);
- }
- config.FileSettings.ThumbnailHeight = thumbnailHeight;
- ReactDOM.findDOMNode(this.refs.ThumbnailHeight).value = thumbnailHeight;
-
- var previewWidth = 1024;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.PreviewWidth).value, 10))) {
- previewWidth = parseInt(ReactDOM.findDOMNode(this.refs.PreviewWidth).value, 10);
- }
- config.FileSettings.PreviewWidth = previewWidth;
- ReactDOM.findDOMNode(this.refs.PreviewWidth).value = previewWidth;
-
- var previewHeight = 0;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.PreviewHeight).value, 10))) {
- previewHeight = parseInt(ReactDOM.findDOMNode(this.refs.PreviewHeight).value, 10);
- }
- config.FileSettings.PreviewHeight = previewHeight;
- ReactDOM.findDOMNode(this.refs.PreviewHeight).value = previewHeight;
-
- var profileWidth = 128;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.ProfileWidth).value, 10))) {
- profileWidth = parseInt(ReactDOM.findDOMNode(this.refs.ProfileWidth).value, 10);
- }
- config.FileSettings.ProfileWidth = profileWidth;
- ReactDOM.findDOMNode(this.refs.ProfileWidth).value = profileWidth;
-
- var profileHeight = 128;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.ProfileHeight).value, 10))) {
- profileHeight = parseInt(ReactDOM.findDOMNode(this.refs.ProfileHeight).value, 10);
- }
- config.FileSettings.ProfileHeight = profileHeight;
- ReactDOM.findDOMNode(this.refs.ProfileHeight).value = profileHeight;
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- var enableFile = false;
- var enableS3 = false;
-
- if (this.state.DriverName === 'local') {
- enableFile = true;
- }
-
- if (this.state.DriverName === 'amazons3') {
- enableS3 = true;
- }
-
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.image.fileSettings'
- defaultMessage='File Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='DriverName'
- >
- <FormattedMessage
- id='admin.image.storeTitle'
- defaultMessage='Store Files In:'
- />
- </label>
- <div className='col-sm-8'>
- <select
- className='form-control'
- id='DriverName'
- ref='DriverName'
- defaultValue={this.props.config.FileSettings.DriverName}
- onChange={this.handleChange.bind(this, 'DriverName')}
- >
- <option value='local'>{formatMessage(holders.storeLocal)}</option>
- <option value='amazons3'>{formatMessage(holders.storeAmazonS3)}</option>
- </select>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='Directory'
- >
- <FormattedMessage
- id='admin.image.localTitle'
- defaultMessage='Local Directory Location:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='Directory'
- ref='Directory'
- placeholder={formatMessage(holders.localExample)}
- defaultValue={this.props.config.FileSettings.Directory}
- onChange={this.handleChange}
- disabled={!enableFile}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.localDescription'
- defaultMessage='Directory to which image files are written. If blank, will be set to ./data/.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AmazonS3AccessKeyId'
- >
- <FormattedMessage
- id='admin.image.amazonS3IdTitle'
- defaultMessage='Amazon S3 Access Key Id:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AmazonS3AccessKeyId'
- ref='AmazonS3AccessKeyId'
- placeholder={formatMessage(holders.amazonS3IdExample)}
- defaultValue={this.props.config.FileSettings.AmazonS3AccessKeyId}
- onChange={this.handleChange}
- disabled={!enableS3}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.amazonS3IdDescription'
- defaultMessage='Obtain this credential from your Amazon EC2 administrator.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AmazonS3SecretAccessKey'
- >
- <FormattedMessage
- id='admin.image.amazonS3SecretTitle'
- defaultMessage='Amazon S3 Secret Access Key:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AmazonS3SecretAccessKey'
- ref='AmazonS3SecretAccessKey'
- placeholder={formatMessage(holders.amazonS3SecretExample)}
- defaultValue={this.props.config.FileSettings.AmazonS3SecretAccessKey}
- onChange={this.handleChange}
- disabled={!enableS3}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.amazonS3SecretDescription'
- defaultMessage='Obtain this credential from your Amazon EC2 administrator.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AmazonS3Bucket'
- >
- <FormattedMessage
- id='admin.image.amazonS3BucketTitle'
- defaultMessage='Amazon S3 Bucket:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AmazonS3Bucket'
- ref='AmazonS3Bucket'
- placeholder={formatMessage(holders.amazonS3BucketExample)}
- defaultValue={this.props.config.FileSettings.AmazonS3Bucket}
- onChange={this.handleChange}
- disabled={!enableS3}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.amazonS3BucketDescription'
- defaultMessage='Name you selected for your S3 bucket in AWS.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AmazonS3Region'
- >
- <FormattedMessage
- id='admin.image.amazonS3RegionTitle'
- defaultMessage='Amazon S3 Region:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AmazonS3Region'
- ref='AmazonS3Region'
- placeholder={formatMessage(holders.amazonS3RegionExample)}
- defaultValue={this.props.config.FileSettings.AmazonS3Region}
- onChange={this.handleChange}
- disabled={!enableS3}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.amazonS3RegionDescription'
- defaultMessage='AWS region you selected for creating your S3 bucket.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ThumbnailWidth'
- >
- <FormattedMessage
- id='admin.image.thumbWidthTitle'
- defaultMessage='Thumbnail Width:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='ThumbnailWidth'
- ref='ThumbnailWidth'
- placeholder={formatMessage(holders.thumbWidthExample)}
- defaultValue={this.props.config.FileSettings.ThumbnailWidth}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.thumbWidthDescription'
- defaultMessage='Width of thumbnails generated from uploaded images. Updating this value changes how thumbnail images render in future, but does not change images created in the past.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ThumbnailHeight'
- >
- <FormattedMessage
- id='admin.image.thumbHeightTitle'
- defaultMessage='Thumbnail Height:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='ThumbnailHeight'
- ref='ThumbnailHeight'
- placeholder={formatMessage(holders.thumbHeightExample)}
- defaultValue={this.props.config.FileSettings.ThumbnailHeight}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.thumbHeightDescription'
- defaultMessage='Height of thumbnails generated from uploaded images. Updating this value changes how thumbnail images render in future, but does not change images created in the past.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PreviewWidth'
- >
- <FormattedMessage
- id='admin.image.previewWidthTitle'
- defaultMessage='Preview Width:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PreviewWidth'
- ref='PreviewWidth'
- placeholder={formatMessage(holders.previewWidthExample)}
- defaultValue={this.props.config.FileSettings.PreviewWidth}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.previewWidthDescription'
- defaultMessage='Maximum width of preview image. Updating this value changes how preview images render in future, but does not change images created in the past.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PreviewHeight'
- >
- <FormattedMessage
- id='admin.image.previewHeightTitle'
- defaultMessage='Preview Height:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PreviewHeight'
- ref='PreviewHeight'
- placeholder={formatMessage(holders.previewHeightExample)}
- defaultValue={this.props.config.FileSettings.PreviewHeight}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.previewHeightDescription'
- defaultMessage='Maximum height of preview image ("0": Sets to auto-size). Updating this value changes how preview images render in future, but does not change images created in the past.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ProfileWidth'
- >
- <FormattedMessage
- id='admin.image.profileWidthTitle'
- defaultMessage='Profile Width:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='ProfileWidth'
- ref='ProfileWidth'
- placeholder={formatMessage(holders.profileWidthExample)}
- defaultValue={this.props.config.FileSettings.ProfileWidth}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.profileWidthDescription'
- defaultMessage='Width of profile picture.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ProfileHeight'
- >
- <FormattedMessage
- id='admin.image.profileHeightTitle'
- defaultMessage='Profile Height:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='ProfileHeight'
- ref='ProfileHeight'
- placeholder={formatMessage(holders.profileHeightExample)}
- defaultValue={this.props.config.FileSettings.ProfileHeight}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.profileHeightDescription'
- defaultMessage='Height of profile picture.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnablePublicLink'
- >
- <FormattedMessage
- id='admin.image.shareTitle'
- defaultMessage='Share Public File Link: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnablePublicLink'
- value='true'
- ref='EnablePublicLink'
- defaultChecked={this.props.config.FileSettings.EnablePublicLink}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.image.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnablePublicLink'
- value='false'
- defaultChecked={!this.props.config.FileSettings.EnablePublicLink}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.image.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.shareDescription'
- defaultMessage='Allow users to share public links to files and images.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PublicLinkSalt'
- >
- <FormattedMessage
- id='admin.image.publicLinkTitle'
- defaultMessage='Public Link Salt:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PublicLinkSalt'
- ref='PublicLinkSalt'
- placeholder={formatMessage(holders.publicLinkExample)}
- defaultValue={this.props.config.FileSettings.PublicLinkSalt}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.publicLinkDescription'
- defaultMessage='32-character salt added to signing of public image links. Randomly generated on install. Click "Re-Generate" to create new salt.'
- />
- </p>
- <div className='help-text'>
- <button
- className='btn btn-default'
- onClick={this.handleGenerate}
- >
- <FormattedMessage
- id='admin.image.regenerate'
- defaultMessage='Re-Generate'
- />
- </button>
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.image.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-FileSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(FileSettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/ldap_settings.jsx b/web/react/components/admin_console/ldap_settings.jsx
deleted file mode 100644
index 4cd19c886..000000000
--- a/web/react/components/admin_console/ldap_settings.jsx
+++ /dev/null
@@ -1,584 +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 AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const DEFAULT_LDAP_PORT = 389;
-const DEFAULT_QUERY_TIMEOUT = 60;
-
-var holders = defineMessages({
- serverEx: {
- id: 'admin.ldap.serverEx',
- defaultMessage: 'Ex "10.0.0.23"'
- },
- portEx: {
- id: 'admin.ldap.portEx',
- defaultMessage: 'Ex "389"'
- },
- baseEx: {
- id: 'admin.ldap.baseEx',
- defaultMessage: 'Ex "ou=Unit Name,dc=corp,dc=example,dc=com"'
- },
- firstnameAttrEx: {
- id: 'admin.ldap.firstnameAttrEx',
- defaultMessage: 'Ex "givenName"'
- },
- lastnameAttrEx: {
- id: 'admin.ldap.lastnameAttrEx',
- defaultMessage: 'Ex "sn"'
- },
- emailAttrEx: {
- id: 'admin.ldap.emailAttrEx',
- defaultMessage: 'Ex "mail" or "userPrincipalName"'
- },
- usernameAttrEx: {
- id: 'admin.ldap.usernameAttrEx',
- defaultMessage: 'Ex "sAMAccountName"'
- },
- idAttrEx: {
- id: 'admin.ldap.idAttrEx',
- defaultMessage: 'Ex "sAMAccountName"'
- },
- queryEx: {
- id: 'admin.ldap.queryEx',
- defaultMessage: 'Ex "60"'
- },
- saving: {
- id: 'admin.ldap.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class LdapSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleChange = this.handleChange.bind(this);
- this.handleEnable = this.handleEnable.bind(this);
- this.handleDisable = this.handleDisable.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null,
- enable: this.props.config.LdapSettings.Enable
- };
- }
- handleChange() {
- this.setState({saveNeeded: true});
- }
- handleEnable() {
- this.setState({saveNeeded: true, enable: true});
- }
- handleDisable() {
- this.setState({saveNeeded: true, enable: false});
- }
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- const config = this.props.config;
- config.LdapSettings.Enable = this.refs.Enable.checked;
- config.LdapSettings.LdapServer = this.refs.LdapServer.value.trim();
-
- let LdapPort = DEFAULT_LDAP_PORT;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.LdapPort).value, 10))) {
- LdapPort = parseInt(ReactDOM.findDOMNode(this.refs.LdapPort).value, 10);
- }
- config.LdapSettings.LdapPort = LdapPort;
-
- config.LdapSettings.BaseDN = this.refs.BaseDN.value.trim();
- config.LdapSettings.BindUsername = this.refs.BindUsername.value.trim();
- config.LdapSettings.BindPassword = this.refs.BindPassword.value.trim();
- config.LdapSettings.FirstNameAttribute = this.refs.FirstNameAttribute.value.trim();
- config.LdapSettings.LastNameAttribute = this.refs.LastNameAttribute.value.trim();
- config.LdapSettings.EmailAttribute = this.refs.EmailAttribute.value.trim();
- config.LdapSettings.UsernameAttribute = this.refs.UsernameAttribute.value.trim();
- config.LdapSettings.IdAttribute = this.refs.IdAttribute.value.trim();
-
- let QueryTimeout = DEFAULT_QUERY_TIMEOUT;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.QueryTimeout).value, 10))) {
- QueryTimeout = parseInt(ReactDOM.findDOMNode(this.refs.QueryTimeout).value, 10);
- }
- config.LdapSettings.QueryTimeout = QueryTimeout;
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
- render() {
- const {formatMessage} = this.props.intl;
- let serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- let saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- const licenseEnabled = global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.LDAP === 'true';
-
- let bannerContent;
- if (licenseEnabled) {
- bannerContent = (
- <div className='banner'>
- <div className='banner__content'>
- <h4 className='banner__heading'>
- <FormattedMessage
- id='admin.ldap.bannerHeading'
- defaultMessage='Note:'
- />
- </h4>
- <p>
- <FormattedMessage
- id='admin.ldap.bannerDesc'
- defaultMessage='If a user attribute changes on the LDAP server it will be updated the next time the user enters their credentials to log in to Mattermost. This includes if a user is made inactive or removed from an LDAP server. Synchronization with LDAP servers is planned in a future release.'
- />
- </p>
- </div>
- </div>
- );
- } else {
- bannerContent = (
- <div className='banner warning'>
- <div className='banner__content'>
- <FormattedHTMLMessage
- id='admin.ldap.noLicense'
- defaultMessage='<h4 class="banner__heading">Note:</h4><p>LDAP is an enterprise feature. Your current license does not support LDAP. Click <a href="http://mattermost.com"target="_blank">here</a> for information and pricing on enterprise licenses.</p>'
- />
- </div>
- </div>
- );
- }
-
- return (
- <div className='wrapper--fixed'>
- {bannerContent}
- <h3>
- <FormattedMessage
- id='admin.ldap.title'
- defaultMessage='LDAP Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='Enable'
- >
- <FormattedMessage
- id='admin.ldap.enableTitle'
- defaultMessage='Enable Login With LDAP:'
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='Enable'
- value='true'
- ref='Enable'
- defaultChecked={this.props.config.LdapSettings.Enable}
- onChange={this.handleEnable}
- disabled={!licenseEnabled}
- />
- <FormattedMessage
- id='admin.ldap.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='Enable'
- value='false'
- defaultChecked={!this.props.config.LdapSettings.Enable}
- onChange={this.handleDisable}
- />
- <FormattedMessage
- id='admin.ldap.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.enableDesc'
- defaultMessage='When true, Mattermost allows login using LDAP'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='LdapServer'
- >
- <FormattedMessage
- id='admin.ldap.serverTitle'
- defaultMessage='LDAP Server:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='LdapServer'
- ref='LdapServer'
- placeholder={formatMessage(holders.serverEx)}
- defaultValue={this.props.config.LdapSettings.LdapServer}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.serverDesc'
- defaultMessage='The domain or IP address of LDAP server.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='LdapPort'
- >
- <FormattedMessage
- id='admin.ldap.portTitle'
- defaultMessage='LDAP Port:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='number'
- className='form-control'
- id='LdapPort'
- ref='LdapPort'
- placeholder={formatMessage(holders.portEx)}
- defaultValue={this.props.config.LdapSettings.LdapPort}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.portDesc'
- defaultMessage='The port Mattermost will use to connect to the LDAP server. Default is 389.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='BaseDN'
- >
- <FormattedMessage
- id='admin.ldap.baseTitle'
- defaultMessage='BaseDN:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='BaseDN'
- ref='BaseDN'
- placeholder={formatMessage(holders.baseEx)}
- defaultValue={this.props.config.LdapSettings.BaseDN}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.baseDesc'
- defaultMessage='The Base DN is the Distinguished Name of the location where Mattermost should start its search for users in the LDAP tree.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='BindUsername'
- >
- <FormattedMessage
- id='admin.ldap.bindUserTitle'
- defaultMessage='Bind Username:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='BindUsername'
- ref='BindUsername'
- placeholder=''
- defaultValue={this.props.config.LdapSettings.BindUsername}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.bindUserDesc'
- defaultMessage='The username used to perform the LDAP search. This should typically be an account created specifically for use with Mattermost. It should have access limited to read the portion of the LDAP tree specified in the BaseDN field.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='BindPassword'
- >
- <FormattedMessage
- id='admin.ldap.bindPwdTitle'
- defaultMessage='Bind Password:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='password'
- className='form-control'
- id='BindPassword'
- ref='BindPassword'
- placeholder=''
- defaultValue={this.props.config.LdapSettings.BindPassword}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.bindPwdDesc'
- defaultMessage='Password of the user given in "Bind Username".'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='FirstNameAttribute'
- >
- <FormattedMessage
- id='admin.ldap.firstnameAttrTitle'
- defaultMessage='First Name Attrubute'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='FirstNameAttribute'
- ref='FirstNameAttribute'
- placeholder={formatMessage(holders.firstnameAttrEx)}
- defaultValue={this.props.config.LdapSettings.FirstNameAttribute}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.firstnameAttrDesc'
- defaultMessage='The attribute in the LDAP server that will be used to populate the first name of users in Mattermost.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='LastNameAttribute'
- >
- <FormattedMessage
- id='admin.ldap.lastnameAttrTitle'
- defaultMessage='Last Name Attribute:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='LastNameAttribute'
- ref='LastNameAttribute'
- placeholder={formatMessage(holders.lastnameAttrEx)}
- defaultValue={this.props.config.LdapSettings.LastNameAttribute}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.lastnameAttrDesc'
- defaultMessage='The attribute in the LDAP server that will be used to populate the last name of users in Mattermost.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EmailAttribute'
- >
- <FormattedMessage
- id='admin.ldap.emailAttrTitle'
- defaultMessage='Email Attribute:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='EmailAttribute'
- ref='EmailAttribute'
- placeholder={formatMessage(holders.emailAttrEx)}
- defaultValue={this.props.config.LdapSettings.EmailAttribute}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.emailAttrDesc'
- defaultMessage='The attribute in the LDAP server that will be used to populate the email addresses of users in Mattermost.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='UsernameAttribute'
- >
- <FormattedMessage
- id='admin.ldap.usernameAttrTitle'
- defaultMessage='Username Attribute:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='UsernameAttribute'
- ref='UsernameAttribute'
- placeholder={formatMessage(holders.usernameAttrEx)}
- defaultValue={this.props.config.LdapSettings.UsernameAttribute}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.uernameAttrDesc'
- defaultMessage='The attribute in the LDAP server that will be used to populate the username field in Mattermost. This may be the same as the ID Attribute.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='IdAttribute'
- >
- <FormattedMessage
- id='admin.ldap.idAttrTitle'
- defaultMessage='Id Attribute: '
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='IdAttribute'
- ref='IdAttribute'
- placeholder={formatMessage(holders.idAttrEx)}
- defaultValue={this.props.config.LdapSettings.IdAttribute}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.idAttrDesc'
- defaultMessage='The attribute in the LDAP server that will be used as a unique identifier in Mattermost. It should be an LDAP attribute with a value that does not change, such as username or uid. If a user’s Id Attribute changes, it will create a new Mattermost account unassociated with their old one. This is the value used to log in to Mattermost in the "LDAP Username" field on the sign in page. Normally this attribute is the same as the “Username Attribute” field above. If your team typically uses domain\\username to sign in to other services with LDAP, you may choose to put domain\\username in this field to maintain consistency between sites.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='QueryTimeout'
- >
- <FormattedMessage
- id='admin.ldap.queryTitle'
- defaultMessage='Query Timeout (seconds):'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='number'
- className='form-control'
- id='QueryTimeout'
- ref='QueryTimeout'
- placeholder={formatMessage(holders.queryEx)}
- defaultValue={this.props.config.LdapSettings.QueryTimeout}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.queryDesc'
- defaultMessage='The timeout value for queries to the LDAP server. Increase if you are getting timeout errors caused by a slow LDAP server.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.ldap.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
- </form>
- </div>
- );
- }
-}
-LdapSettings.defaultProps = {
-};
-
-LdapSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(LdapSettings);
diff --git a/web/react/components/admin_console/legal_and_support_settings.jsx b/web/react/components/admin_console/legal_and_support_settings.jsx
deleted file mode 100644
index a6c6a0626..000000000
--- a/web/react/components/admin_console/legal_and_support_settings.jsx
+++ /dev/null
@@ -1,290 +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 AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-var holders = defineMessages({
- saving: {
- id: 'admin.support.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class LegalAndSupportSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange() {
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
-
- config.SupportSettings.TermsOfServiceLink = ReactDOM.findDOMNode(this.refs.TermsOfServiceLink).value.trim();
- config.SupportSettings.PrivacyPolicyLink = ReactDOM.findDOMNode(this.refs.PrivacyPolicyLink).value.trim();
- config.SupportSettings.AboutLink = ReactDOM.findDOMNode(this.refs.AboutLink).value.trim();
- config.SupportSettings.HelpLink = ReactDOM.findDOMNode(this.refs.HelpLink).value.trim();
- config.SupportSettings.ReportAProblemLink = ReactDOM.findDOMNode(this.refs.ReportAProblemLink).value.trim();
- config.SupportSettings.SupportEmail = ReactDOM.findDOMNode(this.refs.SupportEmail).value.trim();
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
-
- <h3>
- <FormattedMessage
- id='admin.support.title'
- defaultMessage='Legal and Support Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='TermsOfServiceLink'
- >
- <FormattedMessage
- id='admin.support.termsTitle'
- defaultMessage='Terms of Service link:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='TermsOfServiceLink'
- ref='TermsOfServiceLink'
- defaultValue={this.props.config.SupportSettings.TermsOfServiceLink}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.support.termsDesc'
- defaultMessage='Link to Terms of Service available to users on desktop and on mobile. Leaving this blank will hide the option to display a notice.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PrivacyPolicyLink'
- >
- <FormattedMessage
- id='admin.support.privacyTitle'
- defaultMessage='Privacy Policy link:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PrivacyPolicyLink'
- ref='PrivacyPolicyLink'
- defaultValue={this.props.config.SupportSettings.PrivacyPolicyLink}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.support.privacyDesc'
- defaultMessage='Link to Privacy Policy available to users on desktop and on mobile. Leaving this blank will hide the option to display a notice.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AboutLink'
- >
- <FormattedMessage
- id='admin.support.aboutTitle'
- defaultMessage='About link:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AboutLink'
- ref='AboutLink'
- defaultValue={this.props.config.SupportSettings.AboutLink}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.support.aboutDesc'
- defaultMessage='Link to About page for more information on your Mattermost deployment, for example its purpose and audience within your organization. Defaults to Mattermost information page.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='HelpLink'
- >
- <FormattedMessage
- id='admin.support.helpTitle'
- defaultMessage='Help link:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='HelpLink'
- ref='HelpLink'
- defaultValue={this.props.config.SupportSettings.HelpLink}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.support.helpDesc'
- defaultMessage='Link to help documentation from team site main menu. Typically not changed unless your organization chooses to create custom documentation.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ReportAProblemLink'
- >
- <FormattedMessage
- id='admin.support.problemTitle'
- defaultMessage='Report a Problem link:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='ReportAProblemLink'
- ref='ReportAProblemLink'
- defaultValue={this.props.config.SupportSettings.ReportAProblemLink}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.support.problemDesc'
- defaultMessage='Link to help documentation from team site main menu. By default this points to the peer-to-peer troubleshooting forum where users can search for, find and request help with technical issues.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SupportEmail'
- >
- <FormattedMessage
- id='admin.support.emailTitle'
- defaultMessage='Support email:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SupportEmail'
- ref='SupportEmail'
- defaultValue={this.props.config.SupportSettings.SupportEmail}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.support.emailHelp'
- defaultMessage='Email shown during tutorial for end users to ask support questions.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.support.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-LegalAndSupportSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(LegalAndSupportSettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/license_settings.jsx b/web/react/components/admin_console/license_settings.jsx
deleted file mode 100644
index 9d2ec8030..000000000
--- a/web/react/components/admin_console/license_settings.jsx
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../utils/utils.jsx';
-import * as Client from '../../utils/client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const holders = defineMessages({
- removing: {
- id: 'admin.license.removing',
- defaultMessage: 'Removing License...'
- },
- uploading: {
- id: 'admin.license.uploading',
- defaultMessage: 'Uploading License...'
- }
-});
-
-class LicenseSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleRemove = this.handleRemove.bind(this);
-
- this.state = {
- fileSelected: false,
- fileName: null,
- serverError: null
- };
- }
-
- handleChange() {
- const element = $(ReactDOM.findDOMNode(this.refs.fileInput));
- if (element.prop('files').length > 0) {
- this.setState({fileSelected: true, fileName: element.prop('files')[0].name});
- }
- }
-
- handleSubmit(e) {
- e.preventDefault();
-
- const element = $(ReactDOM.findDOMNode(this.refs.fileInput));
- if (element.prop('files').length === 0) {
- return;
- }
- const file = element.prop('files')[0];
-
- $('#upload-button').button('loading');
-
- const formData = new FormData();
- formData.append('license', file, file.name);
-
- Client.uploadLicenseFile(formData,
- () => {
- Utils.clearFileInput(element[0]);
- $('#upload-button').button('reset');
- this.setState({fileSelected: false, fileName: null, serverError: null});
- window.location.reload(true);
- },
- (error) => {
- Utils.clearFileInput(element[0]);
- $('#upload-button').button('reset');
- this.setState({fileSelected: false, fileName: null, serverError: error.message});
- }
- );
- }
-
- handleRemove(e) {
- e.preventDefault();
-
- $('#remove-button').button('loading');
-
- Client.removeLicenseFile(
- () => {
- $('#remove-button').button('reset');
- this.setState({fileSelected: false, fileName: null, serverError: null});
- window.location.reload(true);
- },
- (error) => {
- $('#remove-button').button('reset');
- this.setState({fileSelected: false, fileName: null, serverError: error.message});
- }
- );
- }
-
- render() {
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var btnClass = 'btn';
- if (this.state.fileSelected) {
- btnClass = 'btn btn-primary';
- }
-
- let edition;
- let licenseType;
- let licenseKey;
-
- if (global.window.mm_license.IsLicensed === 'true') {
- edition = (
- <FormattedMessage
- id='admin.license.enterpriseEdition'
- defaultMessage='Mattermost Enterprise Edition. Designed for enterprise-scale communication.'
- />
- );
- licenseType = (
- <FormattedHTMLMessage
- id='admin.license.enterpriseType'
- values={{
- terms: global.window.mm_config.TermsOfServiceLink,
- name: global.window.mm_license.Name,
- company: global.window.mm_license.Company,
- users: global.window.mm_license.Users,
- issued: Utils.displayDate(parseInt(global.window.mm_license.IssuedAt, 10)) + ' ' + Utils.displayTime(parseInt(global.window.mm_license.IssuedAt, 10), true),
- start: Utils.displayDate(parseInt(global.window.mm_license.StartsAt, 10)),
- expires: Utils.displayDate(parseInt(global.window.mm_license.ExpiresAt, 10)),
- ldap: global.window.mm_license.LDAP
- }}
- defaultMessage='<div><p>This compiled release of Mattermost platform is provided under a <a href="http://mattermost.com" target="_blank">commercial license</a> from Mattermost, Inc. based on your subscription level and is subject to the <a href="{terms}" target="_blank">Terms of Service.</a></p>
- <p>Your subscription details are as follows:</p>
- Name: {name}<br />
- Company or organization name: {company}<br/>
- Number of users: {users}<br/>
- License issued: {issued}<br/>
- Start date of license: {start}<br/>
- Expiry date of license: {expires}<br/>
- LDAP: {ldap}<br/></div>'
- />
- );
-
- licenseKey = (
- <div className='col-sm-8'>
- <button
- disabled={this.props.config.LdapSettings.Enable}
- className='btn btn-danger'
- onClick={this.handleRemove}
- id='remove-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.removing)}
- >
- <FormattedMessage
- id='admin.license.keyRemove'
- defaultMessage='Remove Enterprise License and Downgrade Server'
- />
- </button>
- <br/>
- <br/>
- <p className='help-text'>
- <FormattedHTMLMessage
- id='admin.licence.keyMigration'
- defaultMessage='If you’re migrating servers you may need to remove your license key from this server in order to install it on a new server. To start, <a href="http://mattermost.com" target="_blank">disable all Enterprise Edition features on this server</a>. This will enable the ability to remove the license key and downgrade this server from Enterprise Edition to Team Edition.'
- />
- </p>
- </div>
- );
- } else {
- edition = (
- <FormattedMessage
- id='admin.license.teamEdition'
- defaultMessage='Mattermost Team Edition. Designed for teams from 5 to 50 users.'
- />
- );
-
- licenseType = (
- <FormattedHTMLMessage
- id='admin.license.teamType'
- defaultMessage='<span><p>This compiled release of Mattermost platform is offered under an MIT license.</p>
- <p>See MIT-COMPILED-LICENSE.txt in your root install directory for details. See NOTICES.txt for information about open source software used in this system.</p></span>'
- />
- );
-
- let fileName;
- if (this.state.fileName) {
- fileName = this.state.fileName;
- } else {
- fileName = (
- <FormattedMessage
- id='admin.license.noFile'
- defaultMessage='No file uploaded'
- />
- );
- }
-
- licenseKey = (
- <div className='col-sm-8'>
- <div className='file__upload'>
- <button className='btn btn-default'>
- <FormattedMessage
- id='admin.license.choose'
- defaultMessage='Choose File'
- />
- </button>
- <input
- ref='fileInput'
- type='file'
- accept='.mattermost-license'
- onChange={this.handleChange}
- />
- </div>
- <button
- className={btnClass}
- disabled={!this.state.fileSelected}
- onClick={this.handleSubmit}
- id='upload-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.uploading)}
- >
- <FormattedMessage
- id='admin.license.upload'
- defaultMessage='Upload'
- />
- </button>
- <div className='help-text no-margin'>
- {fileName}
- </div>
- <br/>
- {serverError}
- <p className='help-text no-margin'>
- <FormattedHTMLMessage
- id='admin.license.uploadDesc'
- defaultMessage='Upload a license key for Mattermost Enterprise Edition to upgrade this server. <a href="http://mattermost.com" target="_blank">Visit us online</a> to learn more about the benefits of Enterprise Edition or to purchase a key.'
- />
- </p>
- </div>
- );
- }
-
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.license.title'
- defaultMessage='Edition and License'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- >
- <FormattedMessage
- id='admin.license.edition'
- defaultMessage='Edition: '
- />
- </label>
- <div className='col-sm-8'>
- {edition}
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- >
- <FormattedMessage
- id='admin.license.type'
- defaultMessage='License: '
- />
- </label>
- <div className='col-sm-8'>
- {licenseType}
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- >
- <FormattedMessage
- id='admin.license.key'
- defaultMessage='License Key: '
- />
- </label>
- {licenseKey}
- </div>
- </form>
- </div>
- );
- }
-}
-
-LicenseSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(LicenseSettings);
diff --git a/web/react/components/admin_console/log_settings.jsx b/web/react/components/admin_console/log_settings.jsx
deleted file mode 100644
index cefe6afba..000000000
--- a/web/react/components/admin_console/log_settings.jsx
+++ /dev/null
@@ -1,414 +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 AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- locationPlaceholder: {
- id: 'admin.log.locationPlaceholder',
- defaultMessage: 'Enter your file location'
- },
- formatPlaceholder: {
- id: 'admin.log.formatPlaceholder',
- defaultMessage: 'Enter your file format'
- },
- saving: {
- id: 'admin.log.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class LogSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- consoleEnable: this.props.config.LogSettings.EnableConsole,
- fileEnable: this.props.config.LogSettings.EnableFile,
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange(action) {
- var s = {saveNeeded: true, serverError: this.state.serverError};
-
- if (action === 'console_true') {
- s.consoleEnable = true;
- }
-
- if (action === 'console_false') {
- s.consoleEnable = false;
- }
-
- if (action === 'file_true') {
- s.fileEnable = true;
- }
-
- if (action === 'file_false') {
- s.fileEnable = false;
- }
-
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.LogSettings.EnableConsole = ReactDOM.findDOMNode(this.refs.consoleEnable).checked;
- config.LogSettings.ConsoleLevel = ReactDOM.findDOMNode(this.refs.consoleLevel).value;
- config.LogSettings.EnableFile = ReactDOM.findDOMNode(this.refs.fileEnable).checked;
- config.LogSettings.FileLevel = ReactDOM.findDOMNode(this.refs.fileLevel).value;
- config.LogSettings.FileLocation = ReactDOM.findDOMNode(this.refs.fileLocation).value.trim();
- config.LogSettings.FileFormat = ReactDOM.findDOMNode(this.refs.fileFormat).value.trim();
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- consoleEnable: config.LogSettings.EnableConsole,
- fileEnable: config.LogSettings.EnableFile,
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- consoleEnable: config.LogSettings.EnableConsole,
- fileEnable: config.LogSettings.EnableFile,
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.log.logSettings'
- defaultMessage='Log Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='consoleEnable'
- >
- <FormattedMessage
- id='admin.log.consoleTitle'
- defaultMessage='Log To The Console: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='consoleEnable'
- value='true'
- ref='consoleEnable'
- defaultChecked={this.props.config.LogSettings.EnableConsole}
- onChange={this.handleChange.bind(this, 'console_true')}
- />
- <FormattedMessage
- id='admin.log.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='consoleEnable'
- value='false'
- defaultChecked={!this.props.config.LogSettings.EnableConsole}
- onChange={this.handleChange.bind(this, 'console_false')}
- />
- <FormattedMessage
- id='admin.log.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.log.consoleDescription'
- defaultMessage='Typically set to false in production. Developers may set this field to true to output log messages to console based on the console level option. If true, server writes messages to the standard output stream (stdout).'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='consoleLevel'
- >
- <FormattedMessage
- id='admin.log.levelTitle'
- defaultMessage='Console Log Level:'
- />
- </label>
- <div className='col-sm-8'>
- <select
- className='form-control'
- id='consoleLevel'
- ref='consoleLevel'
- defaultValue={this.props.config.LogSettings.consoleLevel}
- onChange={this.handleChange}
- disabled={!this.state.consoleEnable}
- >
- <option value='DEBUG'>{'DEBUG'}</option>
- <option value='INFO'>{'INFO'}</option>
- <option value='ERROR'>{'ERROR'}</option>
- </select>
- <p className='help-text'>
- <FormattedMessage
- id='admin.log.levelDescription'
- defaultMessage='This setting determines the level of detail at which log events are written to the console. ERROR: Outputs only error messages. INFO: Outputs error messages and information around startup and initialization. DEBUG: Prints high detail for developers working on debugging issues.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- >
- <FormattedMessage
- id='admin.log.fileTitle'
- defaultMessage='Log To File: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='fileEnable'
- ref='fileEnable'
- value='true'
- defaultChecked={this.props.config.LogSettings.EnableFile}
- onChange={this.handleChange.bind(this, 'file_true')}
- />
- <FormattedMessage
- id='admin.log.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='fileEnable'
- value='false'
- defaultChecked={!this.props.config.LogSettings.EnableFile}
- onChange={this.handleChange.bind(this, 'file_false')}
- />
- <FormattedMessage
- id='admin.log.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.log.fileDescription'
- defaultMessage='Typically set to true in production. When true, log files are written to the log file specified in file location field below.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='fileLevel'
- >
- <FormattedMessage
- id='admin.log.fileLevelTitle'
- defaultMessage='File Log Level:'
- />
- </label>
- <div className='col-sm-8'>
- <select
- className='form-control'
- id='fileLevel'
- ref='fileLevel'
- defaultValue={this.props.config.LogSettings.FileLevel}
- onChange={this.handleChange}
- disabled={!this.state.fileEnable}
- >
- <option value='DEBUG'>{'DEBUG'}</option>
- <option value='INFO'>{'INFO'}</option>
- <option value='ERROR'>{'ERROR'}</option>
- </select>
- <p className='help-text'>
- <FormattedMessage
- id='admin.log.fileLevelDescription'
- defaultMessage='This setting determines the level of detail at which log events are written to the log file. ERROR: Outputs only error messages. INFO: Outputs error messages and information around startup and initialization. DEBUG: Prints high detail for developers working on debugging issues.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='fileLocation'
- >
- <FormattedMessage
- id='admin.log.locationTitle'
- defaultMessage='File Location:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='fileLocation'
- ref='fileLocation'
- placeholder={formatMessage(holders.locationPlaceholder)}
- defaultValue={this.props.config.LogSettings.FileLocation}
- onChange={this.handleChange}
- disabled={!this.state.fileEnable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.log.locationDescription'
- defaultMessage='File to which log files are written. If blank, will be set to ./logs/mattermost, which writes logs to mattermost.log. Log rotation is enabled and every 10,000 lines of log information is written to new files stored in the same directory, for example mattermost.2015-09-23.001, mattermost.2015-09-23.002, and so forth.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='fileFormat'
- >
- <FormattedMessage
- id='admin.log.formatTitle'
- defaultMessage='File Format:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='fileFormat'
- ref='fileFormat'
- placeholder={formatMessage(holders.formatPlaceholder)}
- defaultValue={this.props.config.LogSettings.FileFormat}
- onChange={this.handleChange}
- disabled={!this.state.fileEnable}
- />
- <div className='help-text'>
- <FormattedMessage
- id='admin.log.formatDescription'
- defaultMessage='Format of log message output. If blank will be set to "[%D %T] [%L] %M", where:'
- />
- <div className='help-text'>
- <table
- className='table table-bordered'
- cellPadding='5'
- >
- <tbody>
- <tr><td className='help-text'>{'%T'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.log.formatTime'
- defaultMessage='Time (15:04:05 MST)'
- />
- </td></tr>
- <tr><td className='help-text'>{'%D'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.log.formatDateLong'
- defaultMessage='Date (2006/01/02)'
- />
- </td></tr>
- <tr><td className='help-text'>{'%d'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.log.formatDateShort'
- defaultMessage='Date (01/02/06)'
- />
- </td></tr>
- <tr><td className='help-text'>{'%L'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.log.formatLevel'
- defaultMessage='Level (DEBG, INFO, EROR)'
- />
- </td></tr>
- <tr><td className='help-text'>{'%S'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.log.formatSource'
- defaultMessage='Source'
- />
- </td></tr>
- <tr><td className='help-text'>{'%M'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.log.formatMessage'
- defaultMessage='Message'
- />
- </td></tr>
- </tbody>
- </table>
- </div>
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.log.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-LogSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(LogSettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/logs.jsx b/web/react/components/admin_console/logs.jsx
deleted file mode 100644
index 8457999f5..000000000
--- a/web/react/components/admin_console/logs.jsx
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AdminStore from '../../stores/admin_store.jsx';
-import LoadingScreen from '../loading_screen.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class Logs extends React.Component {
- constructor(props) {
- super(props);
-
- this.onLogListenerChange = this.onLogListenerChange.bind(this);
- this.reload = this.reload.bind(this);
-
- this.state = {
- logs: AdminStore.getLogs()
- };
- }
-
- componentDidMount() {
- AdminStore.addLogChangeListener(this.onLogListenerChange);
- AsyncClient.getLogs();
- }
-
- componentWillUnmount() {
- AdminStore.removeLogChangeListener(this.onLogListenerChange);
- }
-
- onLogListenerChange() {
- this.setState({
- logs: AdminStore.getLogs()
- });
- }
-
- reload() {
- AdminStore.saveLogs(null);
- this.setState({
- logs: null
- });
-
- AsyncClient.getLogs();
- }
-
- render() {
- var content = null;
-
- if (this.state.logs === null) {
- content = <LoadingScreen/>;
- } else {
- content = [];
-
- for (var i = 0; i < this.state.logs.length; i++) {
- var style = {
- whiteSpace: 'nowrap',
- fontFamily: 'monospace'
- };
-
- if (this.state.logs[i].indexOf('[EROR]') > 0) {
- style.color = 'red';
- }
-
- content.push(<br key={'br_' + i}/>);
- content.push(
- <span
- key={'log_' + i}
- style={style}
- >
- {this.state.logs[i]}
- </span>
- );
- }
- }
-
- return (
- <div className='panel'>
- <h3>
- <FormattedMessage
- id='admin.logs.title'
- defaultMessage='Server Logs'
- />
- </h3>
- <button
- type='submit'
- className='btn btn-primary'
- onClick={this.reload}
- >
- <FormattedMessage
- id='admin.logs.reload'
- defaultMessage='Reload'
- />
- </button>
- <div className='log__panel'>
- {content}
- </div>
- </div>
- );
- }
-} \ No newline at end of file
diff --git a/web/react/components/admin_console/privacy_settings.jsx b/web/react/components/admin_console/privacy_settings.jsx
deleted file mode 100644
index 1ab625049..000000000
--- a/web/react/components/admin_console/privacy_settings.jsx
+++ /dev/null
@@ -1,211 +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 AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- saving: {
- id: 'admin.privacy.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class PrivacySettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange() {
- var s = {saveNeeded: true, serverError: this.state.serverError};
-
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.PrivacySettings.ShowEmailAddress = ReactDOM.findDOMNode(this.refs.ShowEmailAddress).checked;
- config.PrivacySettings.ShowFullName = ReactDOM.findDOMNode(this.refs.ShowFullName).checked;
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.privacy.title'
- defaultMessage='Privacy Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ShowEmailAddress'
- >
- <FormattedMessage
- id='admin.privacy.showEmailTitle'
- defaultMessage='Show Email Address: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='ShowEmailAddress'
- value='true'
- ref='ShowEmailAddress'
- defaultChecked={this.props.config.PrivacySettings.ShowEmailAddress}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.privacy.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='ShowEmailAddress'
- value='false'
- defaultChecked={!this.props.config.PrivacySettings.ShowEmailAddress}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.privacy.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.privacy.showEmailDescription'
- defaultMessage='When false, hides email address of users from other users in the user interface, including team owners and team administrators. Used when system is set up for managing teams where some users choose to keep their contact information private.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ShowFullName'
- >
- <FormattedMessage
- id='admin.privacy.showFullNameTitle'
- defaultMessage='Show Full Name: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='ShowFullName'
- value='true'
- ref='ShowFullName'
- defaultChecked={this.props.config.PrivacySettings.ShowFullName}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.privacy.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='ShowFullName'
- value='false'
- defaultChecked={!this.props.config.PrivacySettings.ShowFullName}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.privacy.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.privacy.showFullNameDescription'
- defaultMessage='When false, hides full name of users from other users, including team owners and team administrators. Username is shown in place of full name.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.privacy.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-PrivacySettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(PrivacySettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/rate_settings.jsx b/web/react/components/admin_console/rate_settings.jsx
deleted file mode 100644
index d3c1bffa2..000000000
--- a/web/react/components/admin_console/rate_settings.jsx
+++ /dev/null
@@ -1,367 +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 AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- queriesExample: {
- id: 'admin.rate.queriesExample',
- defaultMessage: 'Ex "10"'
- },
- memoryExample: {
- id: 'admin.rate.memoryExample',
- defaultMessage: 'Ex "10000"'
- },
- httpHeaderExample: {
- id: 'admin.rate.httpHeaderExample',
- defaultMessage: 'Ex "X-Real-IP", "X-Forwarded-For"'
- },
- saving: {
- id: 'admin.rate.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class RateSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- EnableRateLimiter: this.props.config.RateLimitSettings.EnableRateLimiter,
- VaryByRemoteAddr: this.props.config.RateLimitSettings.VaryByRemoteAddr,
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange(action) {
- var s = {saveNeeded: true, serverError: this.state.serverError};
-
- if (action === 'EnableRateLimiterTrue') {
- s.EnableRateLimiter = true;
- }
-
- if (action === 'EnableRateLimiterFalse') {
- s.EnableRateLimiter = false;
- }
-
- if (action === 'VaryByRemoteAddrTrue') {
- s.VaryByRemoteAddr = true;
- }
-
- if (action === 'VaryByRemoteAddrFalse') {
- s.VaryByRemoteAddr = false;
- }
-
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.RateLimitSettings.EnableRateLimiter = ReactDOM.findDOMNode(this.refs.EnableRateLimiter).checked;
- config.RateLimitSettings.VaryByRemoteAddr = ReactDOM.findDOMNode(this.refs.VaryByRemoteAddr).checked;
- config.RateLimitSettings.VaryByHeader = ReactDOM.findDOMNode(this.refs.VaryByHeader).value.trim();
-
- var PerSec = 10;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.PerSec).value, 10))) {
- PerSec = parseInt(ReactDOM.findDOMNode(this.refs.PerSec).value, 10);
- }
- config.RateLimitSettings.PerSec = PerSec;
- ReactDOM.findDOMNode(this.refs.PerSec).value = PerSec;
-
- var MemoryStoreSize = 10000;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MemoryStoreSize).value, 10))) {
- MemoryStoreSize = parseInt(ReactDOM.findDOMNode(this.refs.MemoryStoreSize).value, 10);
- }
- config.RateLimitSettings.MemoryStoreSize = MemoryStoreSize;
- ReactDOM.findDOMNode(this.refs.MemoryStoreSize).value = MemoryStoreSize;
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
-
- <div className='banner'>
- <div className='banner__content'>
- <h4 className='banner__heading'>
- <FormattedMessage
- id='admin.rate.noteTitle'
- defaultMessage='Note:'
- />
- </h4>
- <p>
- <FormattedMessage
- id='admin.rate.noteDescription'
- defaultMessage='Changing properties in this section will require a server restart before taking effect.'
- />
- </p>
- </div>
- </div>
-
- <h3>
- <FormattedMessage
- id='admin.rate.title'
- defaultMessage='Rate Limit Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableRateLimiter'
- >
- <FormattedMessage
- id='admin.rate.enableLimiterTitle'
- defaultMessage='Enable Rate Limiter: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableRateLimiter'
- value='true'
- ref='EnableRateLimiter'
- defaultChecked={this.props.config.RateLimitSettings.EnableRateLimiter}
- onChange={this.handleChange.bind(this, 'EnableRateLimiterTrue')}
- />
- <FormattedMessage
- id='admin.rate.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableRateLimiter'
- value='false'
- defaultChecked={!this.props.config.RateLimitSettings.EnableRateLimiter}
- onChange={this.handleChange.bind(this, 'EnableRateLimiterFalse')}
- />
- <FormattedMessage
- id='admin.rate.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.rate.enableLimiterDescription'
- defaultMessage='When true, APIs are throttled at rates specified below.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PerSec'
- >
- <FormattedMessage
- id='admin.rate.queriesTitle'
- defaultMessage='Number Of Queries Per Second:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PerSec'
- ref='PerSec'
- placeholder={formatMessage(holders.queriesExample)}
- defaultValue={this.props.config.RateLimitSettings.PerSec}
- onChange={this.handleChange}
- disabled={!this.state.EnableRateLimiter}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.rate.queriesDescription'
- defaultMessage='Throttles API at this number of requests per second.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='MemoryStoreSize'
- >
- <FormattedMessage
- id='admin.rate.memoryTitle'
- defaultMessage='Memory Store Size:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='MemoryStoreSize'
- ref='MemoryStoreSize'
- placeholder={formatMessage(holders.memoryExample)}
- defaultValue={this.props.config.RateLimitSettings.MemoryStoreSize}
- onChange={this.handleChange}
- disabled={!this.state.EnableRateLimiter}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.rate.memoryDescription'
- defaultMessage='Maximum number of users sessions connected to the system as determined by "Vary By Remote Address" and "Vary By Header" settings below.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='VaryByRemoteAddr'
- >
- <FormattedMessage
- id='admin.rate.remoteTitle'
- defaultMessage='Vary By Remote Address: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='VaryByRemoteAddr'
- value='true'
- ref='VaryByRemoteAddr'
- defaultChecked={this.props.config.RateLimitSettings.VaryByRemoteAddr}
- onChange={this.handleChange.bind(this, 'VaryByRemoteAddrTrue')}
- disabled={!this.state.EnableRateLimiter}
- />
- <FormattedMessage
- id='admin.rate.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='VaryByRemoteAddr'
- value='false'
- defaultChecked={!this.props.config.RateLimitSettings.VaryByRemoteAddr}
- onChange={this.handleChange.bind(this, 'VaryByRemoteAddrFalse')}
- disabled={!this.state.EnableRateLimiter}
- />
- <FormattedMessage
- id='admin.rate.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.rate.remoteDescription'
- defaultMessage='When true, rate limit API access by IP address.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='VaryByHeader'
- >
- <FormattedMessage
- id='admin.rate.httpHeaderTitle'
- defaultMessage='Vary By HTTP Header:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='VaryByHeader'
- ref='VaryByHeader'
- placeholder={formatMessage(holders.httpHeaderExample)}
- defaultValue={this.props.config.RateLimitSettings.VaryByHeader}
- onChange={this.handleChange}
- disabled={!this.state.EnableRateLimiter || this.state.VaryByRemoteAddr}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.rate.httpHeaderDescription'
- defaultMessage='When filled in, vary rate limiting by HTTP header field specified (e.g. when configuring NGINX set to "X-Real-IP", when configuring AmazonELB set to "X-Forwarded-For").'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.rate.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-RateSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(RateSettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/reset_password_modal.jsx b/web/react/components/admin_console/reset_password_modal.jsx
deleted file mode 100644
index 8ed519ffb..000000000
--- a/web/react/components/admin_console/reset_password_modal.jsx
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import Constants from '../../utils/constants.jsx';
-var Modal = ReactBootstrap.Modal;
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-var holders = defineMessages({
- submit: {
- id: 'admin.reset_password.submit',
- defaultMessage: 'Please enter at least {chars} characters.'
- }
-});
-
-class ResetPasswordModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.doSubmit = this.doSubmit.bind(this);
- this.doCancel = this.doCancel.bind(this);
-
- this.state = {
- serverError: null
- };
- }
-
- doSubmit(e) {
- e.preventDefault();
- var password = ReactDOM.findDOMNode(this.refs.password).value;
-
- if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
- this.setState({serverError: this.props.intl.formatMessage(holders.submit, {chars: Constants.MIN_PASSWORD_LENGTH})});
- return;
- }
-
- this.setState({serverError: null});
-
- var data = {};
- data.new_password = password;
- data.name = this.props.team.name;
- data.user_id = this.props.user.id;
-
- Client.resetPassword(data,
- () => {
- this.props.onModalSubmit(ReactDOM.findDOMNode(this.refs.password).value);
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
-
- doCancel() {
- this.setState({serverError: null});
- this.props.onModalDismissed();
- }
-
- render() {
- if (this.props.user == null) {
- return <div/>;
- }
-
- let urlClass = 'input-group input-group--limit';
- let serverError = null;
-
- if (this.state.serverError) {
- urlClass += ' has-error';
- serverError = <div className='form-group has-error'><p className='input__help error'>{this.state.serverError}</p></div>;
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.doCancel}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='admin.reset_password.title'
- defaultMessage='Reset Password'
- />
- </Modal.Title>
- </Modal.Header>
- <form
- role='form'
- className='form-horizontal'
- >
- <Modal.Body>
- <div className='form-group'>
- <div className='col-sm-10'>
- <div className={urlClass}>
- <span
- data-toggle='tooltip'
- title='New Password'
- className='input-group-addon'
- >
- <FormattedMessage
- id='admin.reset_password.newPassword'
- defaultMessage='New Password'
- />
- </span>
- <input
- type='password'
- ref='password'
- className='form-control'
- maxLength='22'
- autoFocus={true}
- tabIndex='1'
- />
- </div>
- {serverError}
- </div>
- </div>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.doCancel}
- >
- <FormattedMessage
- id='admin.reset_password.close'
- defaultMessage='Close'
- />
- </button>
- <button
- onClick={this.doSubmit}
- type='submit'
- className='btn btn-primary'
- tabIndex='2'
- >
- <FormattedMessage
- id='admin.reset_password.select'
- defaultMessage='Select'
- />
- </button>
- </Modal.Footer>
- </form>
- </Modal>
- );
- }
-}
-
-ResetPasswordModal.defaultProps = {
- show: false
-};
-
-ResetPasswordModal.propTypes = {
- intl: intlShape.isRequired,
- user: React.PropTypes.object,
- team: React.PropTypes.object,
- show: React.PropTypes.bool.isRequired,
- onModalSubmit: React.PropTypes.func,
- onModalDismissed: React.PropTypes.func
-};
-
-export default injectIntl(ResetPasswordModal); \ No newline at end of file
diff --git a/web/react/components/admin_console/select_team_modal.jsx b/web/react/components/admin_console/select_team_modal.jsx
deleted file mode 100644
index e0d070b28..000000000
--- a/web/react/components/admin_console/select_team_modal.jsx
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-var Modal = ReactBootstrap.Modal;
-
-export default class SelectTeamModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.doSubmit = this.doSubmit.bind(this);
- this.doCancel = this.doCancel.bind(this);
- }
-
- doSubmit(e) {
- e.preventDefault();
- this.props.onModalSubmit(ReactDOM.findDOMNode(this.refs.team).value);
- }
- doCancel() {
- this.props.onModalDismissed();
- }
- render() {
- if (this.props.teams == null) {
- return <div/>;
- }
-
- var options = [];
-
- for (var key in this.props.teams) {
- if (this.props.teams.hasOwnProperty(key)) {
- var team = this.props.teams[key];
- options.push(
- <option
- key={'opt_' + team.id}
- value={team.id}
- >
- {team.name}
- </option>
- );
- }
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.doCancel}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='admin.select_team.selectTeam'
- defaultMessage='Select Team'
- />
- </Modal.Title>
- </Modal.Header>
- <form
- role='form'
- className='form-horizontal'
- >
- <Modal.Body>
- <div className='form-group'>
- <div className='col-sm-12'>
- <select
- ref='team'
- size='10'
- className='form-control'
- >
- {options}
- </select>
- </div>
- </div>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.doCancel}
- >
- <FormattedMessage
- id='admin.select_team.close'
- defaultMessage='Close'
- />
- </button>
- <button
- onClick={this.doSubmit}
- type='submit'
- className='btn btn-primary'
- tabIndex='2'
- >
- <FormattedMessage
- id='admin.select_team.select'
- defaultMessage='Select'
- />
- </button>
- </Modal.Footer>
- </form>
- </Modal>
- );
- }
-}
-
-SelectTeamModal.defaultProps = {
- show: false
-};
-
-SelectTeamModal.propTypes = {
- teams: React.PropTypes.object,
- show: React.PropTypes.bool.isRequired,
- onModalSubmit: React.PropTypes.func,
- onModalDismissed: React.PropTypes.func
-}; \ No newline at end of file
diff --git a/web/react/components/admin_console/service_settings.jsx b/web/react/components/admin_console/service_settings.jsx
deleted file mode 100644
index 9ed81b6a3..000000000
--- a/web/react/components/admin_console/service_settings.jsx
+++ /dev/null
@@ -1,980 +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 AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const DefaultSessionLength = 30;
-const DefaultMaximumLoginAttempts = 10;
-const DefaultSessionCacheInMinutes = 10;
-
-var holders = defineMessages({
- listenExample: {
- id: 'admin.service.listenExample',
- defaultMessage: 'Ex ":8065"'
- },
- attemptExample: {
- id: 'admin.service.attemptExample',
- defaultMessage: 'Ex "10"'
- },
- segmentExample: {
- id: 'admin.service.segmentExample',
- defaultMessage: 'Ex "g3fgGOXJAQ43QV7rAh6iwQCkV4cA1Gs"'
- },
- googleExample: {
- id: 'admin.service.googleExample',
- defaultMessage: 'Ex "7rAh6iwQCkV4cA1Gsg3fgGOXJAQ43QV"'
- },
- sessionDaysEx: {
- id: 'admin.service.sessionDaysEx',
- defaultMessage: 'Ex "30"'
- },
- corsExample: {
- id: 'admin.service.corsEx',
- defaultMessage: 'http://example.com'
- },
- saving: {
- id: 'admin.service.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class ServiceSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange() {
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.ServiceSettings.ListenAddress = ReactDOM.findDOMNode(this.refs.ListenAddress).value.trim();
- if (config.ServiceSettings.ListenAddress === '') {
- config.ServiceSettings.ListenAddress = ':8065';
- ReactDOM.findDOMNode(this.refs.ListenAddress).value = config.ServiceSettings.ListenAddress;
- }
-
- config.ServiceSettings.SegmentDeveloperKey = ReactDOM.findDOMNode(this.refs.SegmentDeveloperKey).value.trim();
- config.ServiceSettings.GoogleDeveloperKey = ReactDOM.findDOMNode(this.refs.GoogleDeveloperKey).value.trim();
- config.ServiceSettings.EnableIncomingWebhooks = ReactDOM.findDOMNode(this.refs.EnableIncomingWebhooks).checked;
- config.ServiceSettings.EnableOutgoingWebhooks = ReactDOM.findDOMNode(this.refs.EnableOutgoingWebhooks).checked;
- config.ServiceSettings.EnablePostUsernameOverride = ReactDOM.findDOMNode(this.refs.EnablePostUsernameOverride).checked;
- config.ServiceSettings.EnablePostIconOverride = ReactDOM.findDOMNode(this.refs.EnablePostIconOverride).checked;
- config.ServiceSettings.EnableTesting = ReactDOM.findDOMNode(this.refs.EnableTesting).checked;
- config.ServiceSettings.EnableDeveloper = ReactDOM.findDOMNode(this.refs.EnableDeveloper).checked;
- config.ServiceSettings.EnableSecurityFixAlert = ReactDOM.findDOMNode(this.refs.EnableSecurityFixAlert).checked;
- config.ServiceSettings.EnableInsecureOutgoingConnections = ReactDOM.findDOMNode(this.refs.EnableInsecureOutgoingConnections).checked;
- config.ServiceSettings.EnableCommands = ReactDOM.findDOMNode(this.refs.EnableCommands).checked;
- config.ServiceSettings.EnableOnlyAdminIntegrations = ReactDOM.findDOMNode(this.refs.EnableOnlyAdminIntegrations).checked;
-
- //config.ServiceSettings.EnableOAuthServiceProvider = ReactDOM.findDOMNode(this.refs.EnableOAuthServiceProvider).checked;
-
- var MaximumLoginAttempts = DefaultMaximumLoginAttempts;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaximumLoginAttempts).value, 10))) {
- MaximumLoginAttempts = parseInt(ReactDOM.findDOMNode(this.refs.MaximumLoginAttempts).value, 10);
- }
- if (MaximumLoginAttempts < 1) {
- MaximumLoginAttempts = 1;
- }
- config.ServiceSettings.MaximumLoginAttempts = MaximumLoginAttempts;
- ReactDOM.findDOMNode(this.refs.MaximumLoginAttempts).value = MaximumLoginAttempts;
-
- var SessionLengthWebInDays = DefaultSessionLength;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthWebInDays).value, 10))) {
- SessionLengthWebInDays = parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthWebInDays).value, 10);
- }
- if (SessionLengthWebInDays < 1) {
- SessionLengthWebInDays = 1;
- }
- config.ServiceSettings.SessionLengthWebInDays = SessionLengthWebInDays;
- ReactDOM.findDOMNode(this.refs.SessionLengthWebInDays).value = SessionLengthWebInDays;
-
- var SessionLengthMobileInDays = DefaultSessionLength;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthMobileInDays).value, 10))) {
- SessionLengthMobileInDays = parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthMobileInDays).value, 10);
- }
- if (SessionLengthMobileInDays < 1) {
- SessionLengthMobileInDays = 1;
- }
- config.ServiceSettings.SessionLengthMobileInDays = SessionLengthMobileInDays;
- ReactDOM.findDOMNode(this.refs.SessionLengthMobileInDays).value = SessionLengthMobileInDays;
-
- var SessionLengthSSOInDays = DefaultSessionLength;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthSSOInDays).value, 10))) {
- SessionLengthSSOInDays = parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthSSOInDays).value, 10);
- }
- if (SessionLengthSSOInDays < 1) {
- SessionLengthSSOInDays = 1;
- }
- config.ServiceSettings.SessionLengthSSOInDays = SessionLengthSSOInDays;
- ReactDOM.findDOMNode(this.refs.SessionLengthSSOInDays).value = SessionLengthSSOInDays;
-
- var SessionCacheInMinutes = DefaultSessionCacheInMinutes;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.SessionCacheInMinutes).value, 10))) {
- SessionCacheInMinutes = parseInt(ReactDOM.findDOMNode(this.refs.SessionCacheInMinutes).value, 10);
- }
- if (SessionCacheInMinutes < -1) {
- SessionCacheInMinutes = -1;
- }
- config.ServiceSettings.SessionCacheInMinutes = SessionCacheInMinutes;
- ReactDOM.findDOMNode(this.refs.SessionCacheInMinutes).value = SessionCacheInMinutes;
-
- config.ServiceSettings.AllowCorsFrom = ReactDOM.findDOMNode(this.refs.AllowCorsFrom).value.trim();
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
-
- <h3>
- <FormattedMessage
- id='admin.service.title'
- defaultMessage='Service Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ListenAddress'
- >
- <FormattedMessage
- id='admin.service.listenAddress'
- defaultMessage='Listen Address:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='ListenAddress'
- ref='ListenAddress'
- placeholder={formatMessage(holders.listenExample)}
- defaultValue={this.props.config.ServiceSettings.ListenAddress}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.listenDescription'
- defaultMessage='The address to which to bind and listen. Entering ":8065" will bind to all interfaces or you can choose one like "127.0.0.1:8065". Changing this will require a server restart before taking effect.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='MaximumLoginAttempts'
- >
- <FormattedMessage
- id='admin.service.attemptTitle'
- defaultMessage='Maximum Login Attempts:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='MaximumLoginAttempts'
- ref='MaximumLoginAttempts'
- placeholder={formatMessage(holders.attemptExample)}
- defaultValue={this.props.config.ServiceSettings.MaximumLoginAttempts}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.attemptDescription'
- defaultMessage='Login attempts allowed before user is locked out and required to reset password via email.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SegmentDeveloperKey'
- >
- <FormattedMessage
- id='admin.service.segmentTitle'
- defaultMessage='Segment Developer Key:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SegmentDeveloperKey'
- ref='SegmentDeveloperKey'
- placeholder={formatMessage(holders.segmentExample)}
- defaultValue={this.props.config.ServiceSettings.SegmentDeveloperKey}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.segmentDescription'
- defaultMessage='For users running a SaaS services, sign up for a key at Segment.com to track metrics.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='GoogleDeveloperKey'
- >
- <FormattedMessage
- id='admin.service.googleTitle'
- defaultMessage='Google Developer Key:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='GoogleDeveloperKey'
- ref='GoogleDeveloperKey'
- placeholder={formatMessage(holders.googleExample)}
- defaultValue={this.props.config.ServiceSettings.GoogleDeveloperKey}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedHTMLMessage
- id='admin.service.googleDescription'
- defaultMessage='Set this key to enable embedding of YouTube video previews based on hyperlinks appearing in messages or comments. Instructions to obtain a key available at <a href="https://www.youtube.com/watch?v=Im69kzhpR3I" target="_blank">https://www.youtube.com/watch?v=Im69kzhpR3I</a>. Leaving the field blank disables the automatic generation of YouTube video previews from links.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableIncomingWebhooks'
- >
- <FormattedMessage
- id='admin.service.webhooksTitle'
- defaultMessage='Enable Incoming Webhooks: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableIncomingWebhooks'
- value='true'
- ref='EnableIncomingWebhooks'
- defaultChecked={this.props.config.ServiceSettings.EnableIncomingWebhooks}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableIncomingWebhooks'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableIncomingWebhooks}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.webhooksDescription'
- defaultMessage='When true, incoming webhooks will be allowed. To help combat phishing attacks, all posts from webhooks will be labelled by a BOT tag.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableOutgoingWebhooks'
- >
- <FormattedMessage
- id='admin.service.outWebhooksTitle'
- defaultMessage='Enable Outgoing Webhooks: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableOutgoingWebhooks'
- value='true'
- ref='EnableOutgoingWebhooks'
- defaultChecked={this.props.config.ServiceSettings.EnableOutgoingWebhooks}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableOutgoingWebhooks'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableOutgoingWebhooks}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.outWebhooksDesc'
- defaultMessage='When true, outgoing webhooks will be allowed.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableCommands'
- >
- <FormattedMessage
- id='admin.service.cmdsTitle'
- defaultMessage='Enable Slash Commands: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableCommands'
- value='true'
- ref='EnableCommands'
- defaultChecked={this.props.config.ServiceSettings.EnableCommands}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableCommands'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableCommands}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.cmdsDesc'
- defaultMessage='When true, user created slash commands will be allowed.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableOnlyAdminIntegrations'
- >
- <FormattedMessage
- id='admin.service.integrationAdmin'
- defaultMessage='Enable Integrations for Admin Only: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableOnlyAdminIntegrations'
- value='true'
- ref='EnableOnlyAdminIntegrations'
- defaultChecked={this.props.config.ServiceSettings.EnableOnlyAdminIntegrations}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableOnlyAdminIntegrations'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableOnlyAdminIntegrations}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.integrationAdminDesc'
- defaultMessage='When true, user created integrations can only be created by admins.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnablePostUsernameOverride'
- >
- <FormattedMessage
- id='admin.service.overrideTitle'
- defaultMessage='Enable Overriding Usernames from Webhooks and Slash Commands: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnablePostUsernameOverride'
- value='true'
- ref='EnablePostUsernameOverride'
- defaultChecked={this.props.config.ServiceSettings.EnablePostUsernameOverride}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnablePostUsernameOverride'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnablePostUsernameOverride}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.overrideDescription'
- defaultMessage='When true, webhooks and slash commands will be allowed to change the username they are posting as. Note, combined with allowing icon overriding, this could open users up to phishing attacks.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnablePostIconOverride'
- >
- <FormattedMessage
- id='admin.service.iconTitle'
- defaultMessage='Enable Overriding Icon from Webhooks and Slash Commands: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnablePostIconOverride'
- value='true'
- ref='EnablePostIconOverride'
- defaultChecked={this.props.config.ServiceSettings.EnablePostIconOverride}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnablePostIconOverride'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnablePostIconOverride}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.iconDescription'
- defaultMessage='When true, webhooks and slash commands will be allowed to change the icon they post with. Note, combined with allowing username overriding, this could open users up to phishing attacks.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableTesting'
- >
- <FormattedMessage
- id='admin.service.testingTitle'
- defaultMessage='Enable Testing: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTesting'
- value='true'
- ref='EnableTesting'
- defaultChecked={this.props.config.ServiceSettings.EnableTesting}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTesting'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableTesting}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.testingDescription'
- defaultMessage='(Developer Option) When true, /loadtest slash command is enabled to load test accounts and test data. Changing this will require a server restart before taking effect.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableDeveloper'
- >
- <FormattedMessage
- id='admin.service.developerTitle'
- defaultMessage='Enable Developer Mode: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableDeveloper'
- value='true'
- ref='EnableDeveloper'
- defaultChecked={this.props.config.ServiceSettings.EnableDeveloper}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableDeveloper'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableDeveloper}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.developerDesc'
- defaultMessage='(Developer Option) When true, extra information around errors will be displayed in the UI.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableSecurityFixAlert'
- >
- <FormattedMessage
- id='admin.service.securityTitle'
- defaultMessage='Enable Security Alerts: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableSecurityFixAlert'
- value='true'
- ref='EnableSecurityFixAlert'
- defaultChecked={this.props.config.ServiceSettings.EnableSecurityFixAlert}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableSecurityFixAlert'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableSecurityFixAlert}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.securityDesc'
- defaultMessage='When true, System Administrators are notified by email if a relevant security fix alert has been announced in the last 12 hours. Requires email to be enabled.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableInsecureOutgoingConnections'
- >
- <FormattedMessage
- id='admin.service.insecureTlsTitle'
- defaultMessage='Enable Insecure Outgoing Connections: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableInsecureOutgoingConnections'
- value='true'
- ref='EnableInsecureOutgoingConnections'
- defaultChecked={this.props.config.ServiceSettings.EnableInsecureOutgoingConnections}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableInsecureOutgoingConnections'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableInsecureOutgoingConnections}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.insecureTlsDesc'
- defaultMessage='When true, any outgoing HTTPS requests will accept unverified, self-signed certificates. For example, outgoing webhooks to a server with a self-signed TLS certificate, using any domain, will be allowed. Note that this makes these connections susceptible to man-in-the-middle attacks.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AllowCorsFrom'
- >
- <FormattedMessage
- id='admin.service.corsTitle'
- defaultMessage='Allow Cross-origin Requests from:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AllowCorsFrom'
- ref='AllowCorsFrom'
- placeholder={formatMessage(holders.corsExample)}
- defaultValue={this.props.config.ServiceSettings.AllowCorsFrom}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.corsDescription'
- defaultMessage='Enable HTTP Cross origin request from a specific domain. Use "*" if you want to allow CORS from any domain or leave it blank to disable it.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SessionLengthWebInDays'
- >
- <FormattedMessage
- id='admin.service.webSessionDays'
- defaultMessage='Session Length for Web in Days:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SessionLengthWebInDays'
- ref='SessionLengthWebInDays'
- placeholder={formatMessage(holders.sessionDaysEx)}
- defaultValue={this.props.config.ServiceSettings.SessionLengthWebInDays}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.webSessionDaysDesc'
- defaultMessage='The web session will expire after the number of days specified and will require a user to login again.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SessionLengthMobileInDays'
- >
- <FormattedMessage
- id='admin.service.mobileSessionDays'
- defaultMessage='Session Length for Mobile Device in Days:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SessionLengthMobileInDays'
- ref='SessionLengthMobileInDays'
- placeholder={formatMessage(holders.sessionDaysEx)}
- defaultValue={this.props.config.ServiceSettings.SessionLengthMobileInDays}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.mobileSessionDaysDesc'
- defaultMessage='The native mobile session will expire after the number of days specified and will require a user to login again.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SessionLengthSSOInDays'
- >
- <FormattedMessage
- id='admin.service.ssoSessionDays'
- defaultMessage='Session Length for SSO in Days:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SessionLengthSSOInDays'
- ref='SessionLengthSSOInDays'
- placeholder={formatMessage(holders.sessionDaysEx)}
- defaultValue={this.props.config.ServiceSettings.SessionLengthSSOInDays}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.ssoSessionDaysDesc'
- defaultMessage='The SSO session will expire after the number of days specified and will require a user to login again.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SessionCacheInMinutes'
- >
- <FormattedMessage
- id='admin.service.sessionCache'
- defaultMessage='Session Cache in Minutes:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SessionCacheInMinutes'
- ref='SessionCacheInMinutes'
- placeholder={formatMessage(holders.sessionDaysEx)}
- defaultValue={this.props.config.ServiceSettings.SessionCacheInMinutes}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.sessionCacheDesc'
- defaultMessage='The number of minutes to cache a session in memory.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.service.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-// <div className='form-group'>
-// <label
-// className='control-label col-sm-4'
-// htmlFor='EnableOAuthServiceProvider'
-// >
-// {'Enable OAuth Service Provider: '}
-// </label>
-// <div className='col-sm-8'>
-// <label className='radio-inline'>
-// <input
-// type='radio'
-// name='EnableOAuthServiceProvider'
-// value='true'
-// ref='EnableOAuthServiceProvider'
-// defaultChecked={this.props.config.ServiceSettings.EnableOAuthServiceProvider}
-// onChange={this.handleChange}
-// />
-// {'true'}
-// </label>
-// <label className='radio-inline'>
-// <input
-// type='radio'
-// name='EnableOAuthServiceProvider'
-// value='false'
-// defaultChecked={!this.props.config.ServiceSettings.EnableOAuthServiceProvider}
-// onChange={this.handleChange}
-// />
-// {'false'}
-// </label>
-// <p className='help-text'>{'When enabled Mattermost will act as an OAuth2 Provider. Changing this will require a server restart before taking effect.'}</p>
-// </div>
-// </div>
-
-ServiceSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(ServiceSettings);
diff --git a/web/react/components/admin_console/sql_settings.jsx b/web/react/components/admin_console/sql_settings.jsx
deleted file mode 100644
index 69ae808f6..000000000
--- a/web/react/components/admin_console/sql_settings.jsx
+++ /dev/null
@@ -1,386 +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 AsyncClient from '../../utils/async_client.jsx';
-import crypto from 'crypto';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- warning: {
- id: 'admin.sql.warning',
- defaultMessage: 'Warning: re-generating this salt may cause some columns in the database to return empty results.'
- },
- maxConnectionsExample: {
- id: 'admin.sql.maxConnectionsExample',
- defaultMessage: 'Ex "10"'
- },
- maxOpenExample: {
- id: 'admin.sql.maxOpenExample',
- defaultMessage: 'Ex "10"'
- },
- keyExample: {
- id: 'admin.sql.keyExample',
- defaultMessage: 'Ex "gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6"'
- },
- saving: {
- id: 'admin.sql.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class SqlSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleGenerate = this.handleGenerate.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange() {
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.SqlSettings.Trace = ReactDOM.findDOMNode(this.refs.Trace).checked;
- config.SqlSettings.AtRestEncryptKey = ReactDOM.findDOMNode(this.refs.AtRestEncryptKey).value.trim();
-
- if (config.SqlSettings.AtRestEncryptKey === '') {
- config.SqlSettings.AtRestEncryptKey = crypto.randomBytes(256).toString('base64').substring(0, 32);
- ReactDOM.findDOMNode(this.refs.AtRestEncryptKey).value = config.SqlSettings.AtRestEncryptKey;
- }
-
- var MaxOpenConns = 10;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaxOpenConns).value, 10))) {
- MaxOpenConns = parseInt(ReactDOM.findDOMNode(this.refs.MaxOpenConns).value, 10);
- }
- config.SqlSettings.MaxOpenConns = MaxOpenConns;
- ReactDOM.findDOMNode(this.refs.MaxOpenConns).value = MaxOpenConns;
-
- var MaxIdleConns = 10;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaxIdleConns).value, 10))) {
- MaxIdleConns = parseInt(ReactDOM.findDOMNode(this.refs.MaxIdleConns).value, 10);
- }
- config.SqlSettings.MaxIdleConns = MaxIdleConns;
- ReactDOM.findDOMNode(this.refs.MaxIdleConns).value = MaxIdleConns;
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- handleGenerate(e) {
- e.preventDefault();
-
- var cfm = global.window.confirm(this.props.intl.formatMessage(holders.warning));
- if (cfm === false) {
- return;
- }
-
- ReactDOM.findDOMNode(this.refs.AtRestEncryptKey).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- var dataSource = '**********' + this.props.config.SqlSettings.DataSource.substring(this.props.config.SqlSettings.DataSource.indexOf('@'));
-
- var dataSourceReplicas = '';
- this.props.config.SqlSettings.DataSourceReplicas.forEach((replica) => {
- dataSourceReplicas += '[**********' + replica.substring(replica.indexOf('@')) + '] ';
- });
-
- if (this.props.config.SqlSettings.DataSourceReplicas.length === 0) {
- dataSourceReplicas = 'none';
- }
-
- return (
- <div className='wrapper--fixed'>
-
- <div className='banner'>
- <div className='banner__content'>
- <h4 className='banner__heading'>
- <FormattedMessage
- id='admin.sql.noteTitle'
- defaultMessage='Note:'
- />
- </h4>
- <p>
- <FormattedMessage
- id='admin.sql.noteDescription'
- defaultMessage='Changing properties in this section will require a server restart before taking effect.'
- />
- </p>
- </div>
- </div>
-
- <h3>
- <FormattedMessage
- id='admin.sql.title'
- defaultMessage='SQL Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='DriverName'
- >
- <FormattedMessage
- id='admin.sql.driverName'
- defaultMessage='Driver Name:'
- />
- </label>
- <div className='col-sm-8'>
- <p className='help-text'>{this.props.config.SqlSettings.DriverName}</p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='DataSource'
- >
- <FormattedMessage
- id='admin.sql.dataSource'
- defaultMessage='Data Source:'
- />
- </label>
- <div className='col-sm-8'>
- <p className='help-text'>{dataSource}</p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='DataSourceReplicas'
- >
- <FormattedMessage
- id='admin.sql.replicas'
- defaultMessage='Data Source Replicas:'
- />
- </label>
- <div className='col-sm-8'>
- <p className='help-text'>{dataSourceReplicas}</p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='MaxIdleConns'
- >
- <FormattedMessage
- id='admin.sql.maxConnectionsTitle'
- defaultMessage='Maximum Idle Connections:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='MaxIdleConns'
- ref='MaxIdleConns'
- placeholder={formatMessage(holders.maxConnectionsExample)}
- defaultValue={this.props.config.SqlSettings.MaxIdleConns}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.sql.maxConnectionsDescription'
- defaultMessage='Maximum number of idle connections held open to the database.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='MaxOpenConns'
- >
- <FormattedMessage
- id='admin.sql.maxOpenTitle'
- defaultMessage='Maximum Open Connections:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='MaxOpenConns'
- ref='MaxOpenConns'
- placeholder={formatMessage(holders.maxOpenExample)}
- defaultValue={this.props.config.SqlSettings.MaxOpenConns}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.sql.maxOpenDescription'
- defaultMessage='Maximum number of open connections held open to the database.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AtRestEncryptKey'
- >
- <FormattedMessage
- id='admin.sql.keyTitle'
- defaultMessage='At Rest Encrypt Key:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AtRestEncryptKey'
- ref='AtRestEncryptKey'
- placeholder={formatMessage(holders.keyExample)}
- defaultValue={this.props.config.SqlSettings.AtRestEncryptKey}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.sql.keyDescription'
- defaultMessage='32-character salt available to encrypt and decrypt sensitive fields in database.'
- />
- </p>
- <div className='help-text'>
- <button
- className='btn btn-default'
- onClick={this.handleGenerate}
- >
- <FormattedMessage
- id='admin.sql.regenerate'
- defaultMessage='Re-Generate'
- />
- </button>
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='Trace'
- >
- <FormattedMessage
- id='admin.sql.traceTitle'
- defaultMessage='Trace: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='Trace'
- value='true'
- ref='Trace'
- defaultChecked={this.props.config.SqlSettings.Trace}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.sql.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='Trace'
- value='false'
- defaultChecked={!this.props.config.SqlSettings.Trace}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.sql.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.sql.traceDescription'
- defaultMessage='(Development Mode) When true, executing SQL statements are written to the log.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.sql.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-SqlSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(SqlSettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/team_settings.jsx b/web/react/components/admin_console/team_settings.jsx
deleted file mode 100644
index cc4ff38ba..000000000
--- a/web/react/components/admin_console/team_settings.jsx
+++ /dev/null
@@ -1,416 +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 AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- siteNameExample: {
- id: 'admin.team.siteNameExample',
- defaultMessage: 'Ex "Mattermost"'
- },
- maxUsersExample: {
- id: 'admin.team.maxUsersExample',
- defaultMessage: 'Ex "25"'
- },
- restrictExample: {
- id: 'admin.team.restrictExample',
- defaultMessage: 'Ex "corp.mattermost.com, mattermost.org"'
- },
- saving: {
- id: 'admin.team.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class TeamSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange() {
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.TeamSettings.SiteName = ReactDOM.findDOMNode(this.refs.SiteName).value.trim();
- config.TeamSettings.RestrictCreationToDomains = ReactDOM.findDOMNode(this.refs.RestrictCreationToDomains).value.trim();
- config.TeamSettings.EnableTeamCreation = ReactDOM.findDOMNode(this.refs.EnableTeamCreation).checked;
- config.TeamSettings.EnableUserCreation = ReactDOM.findDOMNode(this.refs.EnableUserCreation).checked;
- config.TeamSettings.RestrictTeamNames = ReactDOM.findDOMNode(this.refs.RestrictTeamNames).checked;
- config.TeamSettings.EnableTeamListing = ReactDOM.findDOMNode(this.refs.EnableTeamListing).checked;
-
- var MaxUsersPerTeam = 50;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value, 10))) {
- MaxUsersPerTeam = parseInt(ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value, 10);
- }
- config.TeamSettings.MaxUsersPerTeam = MaxUsersPerTeam;
- ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value = MaxUsersPerTeam;
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
-
- <h3>
- <FormattedMessage
- id='admin.team.title'
- defaultMessage='Team Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SiteName'
- >
- <FormattedMessage
- id='admin.team.siteNameTitle'
- defaultMessage='Site Name:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SiteName'
- ref='SiteName'
- placeholder={formatMessage(holders.siteNameExample)}
- defaultValue={this.props.config.TeamSettings.SiteName}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.siteNameDescription'
- defaultMessage='Name of service shown in login screens and UI.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='MaxUsersPerTeam'
- >
- <FormattedMessage
- id='admin.team.maxUsersTitle'
- defaultMessage='Max Users Per Team:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='MaxUsersPerTeam'
- ref='MaxUsersPerTeam'
- placeholder={formatMessage(holders.maxUsersExample)}
- defaultValue={this.props.config.TeamSettings.MaxUsersPerTeam}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.maxUsersDescription'
- defaultMessage='Maximum total number of users per team, including both active and inactive users.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableTeamCreation'
- >
- <FormattedMessage
- id='admin.team.teamCreationTitle'
- defaultMessage='Enable Team Creation: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTeamCreation'
- value='true'
- ref='EnableTeamCreation'
- defaultChecked={this.props.config.TeamSettings.EnableTeamCreation}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTeamCreation'
- value='false'
- defaultChecked={!this.props.config.TeamSettings.EnableTeamCreation}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.teamCreationDescription'
- defaultMessage='When false, the ability to create teams is disabled. The create team button displays error when pressed.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableUserCreation'
- >
- <FormattedMessage
- id='admin.team.userCreationTitle'
- defaultMessage='Enable User Creation: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableUserCreation'
- value='true'
- ref='EnableUserCreation'
- defaultChecked={this.props.config.TeamSettings.EnableUserCreation}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableUserCreation'
- value='false'
- defaultChecked={!this.props.config.TeamSettings.EnableUserCreation}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.userCreationDescription'
- defaultMessage='When false, the ability to create accounts is disabled. The create account button displays error when pressed.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='RestrictCreationToDomains'
- >
- <FormattedMessage
- id='admin.team.restrictTitle'
- defaultMessage='Restrict Creation To Domains:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='RestrictCreationToDomains'
- ref='RestrictCreationToDomains'
- placeholder={formatMessage(holders.restrictExample)}
- defaultValue={this.props.config.TeamSettings.RestrictCreationToDomains}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.restrictDescription'
- defaultMessage='Teams and user accounts can only be created from a specific domain (e.g. "mattermost.org") or list of comma-separated domains (e.g. "corp.mattermost.com, mattermost.org").'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='RestrictTeamNames'
- >
- <FormattedMessage
- id='admin.team.restrictNameTitle'
- defaultMessage='Restrict Team Names: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='RestrictTeamNames'
- value='true'
- ref='RestrictTeamNames'
- defaultChecked={this.props.config.TeamSettings.RestrictTeamNames}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='RestrictTeamNames'
- value='false'
- defaultChecked={!this.props.config.TeamSettings.RestrictTeamNames}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.restrictNameDesc'
- defaultMessage='When true, You cannot create a team name with reserved words like www, admin, support, test, channel, etc'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableTeamListing'
- >
- <FormattedMessage
- id='admin.team.dirTitle'
- defaultMessage='Enable Team Directory: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTeamListing'
- value='true'
- ref='EnableTeamListing'
- defaultChecked={this.props.config.TeamSettings.EnableTeamListing}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTeamListing'
- value='false'
- defaultChecked={!this.props.config.TeamSettings.EnableTeamListing}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.dirDesc'
- defaultMessage='When true, teams that are configured to show in team directory will show on main page inplace of creating a new team.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.team.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-TeamSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(TeamSettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/team_users.jsx b/web/react/components/admin_console/team_users.jsx
deleted file mode 100644
index 708907ddb..000000000
--- a/web/react/components/admin_console/team_users.jsx
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import LoadingScreen from '../loading_screen.jsx';
-import UserItem from './user_item.jsx';
-import ResetPasswordModal from './reset_password_modal.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class UserList extends React.Component {
- constructor(props) {
- super(props);
-
- this.getTeamProfiles = this.getTeamProfiles.bind(this);
- this.getCurrentTeamProfiles = this.getCurrentTeamProfiles.bind(this);
- this.doPasswordReset = this.doPasswordReset.bind(this);
- this.doPasswordResetDismiss = this.doPasswordResetDismiss.bind(this);
- this.doPasswordResetSubmit = this.doPasswordResetSubmit.bind(this);
-
- this.state = {
- teamId: props.team.id,
- users: null,
- serverError: null,
- showPasswordModal: false,
- user: null
- };
- }
-
- componentDidMount() {
- this.getCurrentTeamProfiles();
- }
-
- getCurrentTeamProfiles() {
- this.getTeamProfiles(this.props.team.id);
- }
-
- getTeamProfiles(teamId) {
- Client.getProfilesForTeam(
- teamId,
- (users) => {
- var memberList = [];
- for (var id in users) {
- if (users.hasOwnProperty(id)) {
- memberList.push(users[id]);
- }
- }
-
- memberList.sort((a, b) => {
- if (a.username < b.username) {
- return -1;
- }
-
- if (a.username > b.username) {
- return 1;
- }
-
- return 0;
- });
-
- this.setState({
- teamId: this.state.teamId,
- users: memberList,
- serverError: this.state.serverError,
- showPasswordModal: this.state.showPasswordModal,
- user: this.state.user
- });
- },
- (err) => {
- this.setState({
- teamId: this.state.teamId,
- users: null,
- serverError: err.message,
- showPasswordModal: this.state.showPasswordModal,
- user: this.state.user
- });
- }
- );
- }
-
- doPasswordReset(user) {
- this.setState({
- teamId: this.state.teamId,
- users: this.state.users,
- serverError: this.state.serverError,
- showPasswordModal: true,
- user
- });
- }
-
- doPasswordResetDismiss() {
- this.setState({
- teamId: this.state.teamId,
- users: this.state.users,
- serverError: this.state.serverError,
- showPasswordModal: false,
- user: null
- });
- }
-
- doPasswordResetSubmit() {
- this.setState({
- teamId: this.state.teamId,
- users: this.state.users,
- serverError: this.state.serverError,
- showPasswordModal: false,
- user: null
- });
- }
-
- componentWillReceiveProps(newProps) {
- this.getTeamProfiles(newProps.team.id);
- }
-
- render() {
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- if (this.state.users == null) {
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.userList.title'
- defaultMessage='Users for {team}'
- values={{
- team: this.props.team.name
- }}
- />
- </h3>
- {serverError}
- <LoadingScreen/>
- </div>
- );
- }
-
- var memberList = this.state.users.map((user) => {
- return (
- <UserItem
- key={'user_' + user.id}
- user={user}
- refreshProfiles={this.getCurrentTeamProfiles}
- doPasswordReset={this.doPasswordReset}
- />);
- });
-
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.userList.title2'
- defaultMessage='Users for {team} ({count})'
- values={{
- team: this.props.team.name,
- count: this.state.users.length
- }}
- />
- </h3>
- {serverError}
- <form
- className='form-horizontal'
- role='form'
- >
- <table className='more-modal__list member-list-holder'>
- <tbody>
- {memberList}
- </tbody>
- </table>
- </form>
- <ResetPasswordModal
- user={this.state.user}
- show={this.state.showPasswordModal}
- team={this.props.team}
- onModalSubmit={this.doPasswordResetSubmit}
- onModalDismissed={this.doPasswordResetDismiss}
- />
- </div>
- );
- }
-}
-
-UserList.propTypes = {
- team: React.PropTypes.object
-};
diff --git a/web/react/components/admin_console/user_item.jsx b/web/react/components/admin_console/user_item.jsx
deleted file mode 100644
index 722da922a..000000000
--- a/web/react/components/admin_console/user_item.jsx
+++ /dev/null
@@ -1,421 +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 UserStore from '../../stores/user_store.jsx';
-import ConfirmModal from '../confirm_modal.jsx';
-import TeamStore from '../../stores/team_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class UserItem extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleMakeMember = this.handleMakeMember.bind(this);
- this.handleMakeActive = this.handleMakeActive.bind(this);
- this.handleMakeNotActive = this.handleMakeNotActive.bind(this);
- this.handleMakeAdmin = this.handleMakeAdmin.bind(this);
- this.handleMakeSystemAdmin = this.handleMakeSystemAdmin.bind(this);
- this.handleResetPassword = this.handleResetPassword.bind(this);
- this.handleDemote = this.handleDemote.bind(this);
- this.handleDemoteSubmit = this.handleDemoteSubmit.bind(this);
- this.handleDemoteCancel = this.handleDemoteCancel.bind(this);
-
- this.state = {
- serverError: null,
- showDemoteModal: false,
- user: null,
- role: null
- };
- }
-
- handleMakeMember(e) {
- e.preventDefault();
- const me = UserStore.getCurrentUser();
- if (this.props.user.id === me.id) {
- this.handleDemote(this.props.user, '');
- } else {
- const data = {
- user_id: this.props.user.id,
- new_roles: ''
- };
-
- Client.updateRoles(data,
- () => {
- this.props.refreshProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- }
-
- handleMakeActive(e) {
- e.preventDefault();
- Client.updateActive(this.props.user.id, true,
- () => {
- this.props.refreshProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
-
- handleMakeNotActive(e) {
- e.preventDefault();
- Client.updateActive(this.props.user.id, false,
- () => {
- this.props.refreshProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
-
- handleMakeAdmin(e) {
- e.preventDefault();
- const me = UserStore.getCurrentUser();
- if (this.props.user.id === me.id) {
- this.handleDemote(this.props.user, 'admin');
- } else {
- const data = {
- user_id: this.props.user.id,
- new_roles: 'admin'
- };
-
- Client.updateRoles(data,
- () => {
- this.props.refreshProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- }
-
- handleMakeSystemAdmin(e) {
- e.preventDefault();
- const data = {
- user_id: this.props.user.id,
- new_roles: 'system_admin'
- };
-
- Client.updateRoles(data,
- () => {
- this.props.refreshProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
-
- handleResetPassword(e) {
- e.preventDefault();
- this.props.doPasswordReset(this.props.user);
- }
-
- handleDemote(user, role) {
- this.setState({
- serverError: this.state.serverError,
- showDemoteModal: true,
- user,
- role
- });
- }
-
- handleDemoteCancel() {
- this.setState({
- serverError: null,
- showDemoteModal: false,
- user: null,
- role: null
- });
- }
-
- handleDemoteSubmit() {
- const data = {
- user_id: this.props.user.id,
- new_roles: this.state.role
- };
-
- Client.updateRoles(data,
- () => {
- this.setState({
- serverError: null,
- showDemoteModal: false,
- user: null,
- role: null
- });
-
- const teamUrl = TeamStore.getCurrentTeamUrl();
- if (teamUrl) {
- window.location.href = teamUrl;
- } else {
- window.location.href = '/';
- }
- },
- (err) => {
- this.setState({
- serverError: err.message
- });
- }
- );
- }
-
- render() {
- let serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='has-error'>
- <label className='has-error control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- const user = this.props.user;
- let currentRoles = (
- <FormattedMessage
- id='admin.user_item.member'
- defaultMessage='Member'
- />
- );
- if (user.roles.length > 0) {
- if (Utils.isSystemAdmin(user.roles)) {
- currentRoles = (
- <FormattedMessage
- id='admin.user_item.sysAdmin'
- defaultMessage='System Admin'
- />
- );
- } else if (Utils.isAdmin(user.roles)) {
- currentRoles = (
- <FormattedMessage
- id='admin.user_item.teamAdmin'
- defaultMessage='Team Admin'
- />
- );
- } else {
- currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
- }
- }
-
- const email = user.email;
- let showMakeMember = user.roles === 'admin' || user.roles === 'system_admin';
- let showMakeAdmin = user.roles === '' || user.roles === 'system_admin';
- let showMakeSystemAdmin = user.roles === '' || user.roles === 'admin';
- let showMakeActive = false;
- let showMakeNotActive = user.roles !== 'system_admin';
-
- if (user.delete_at > 0) {
- currentRoles = (
- <FormattedMessage
- id='admin.user_item.inactive'
- defaultMessage='Inactive'
- />
- );
- showMakeMember = false;
- showMakeAdmin = false;
- showMakeSystemAdmin = false;
- showMakeActive = true;
- showMakeNotActive = false;
- }
-
- let makeSystemAdmin = null;
- if (showMakeSystemAdmin) {
- makeSystemAdmin = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeSystemAdmin}
- >
- <FormattedMessage
- id='admin.user_item.makeSysAdmin'
- defaultMessage='Make System Admin'
- />
- </a>
- </li>
- );
- }
-
- let makeAdmin = null;
- if (showMakeAdmin) {
- makeAdmin = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeAdmin}
- >
- <FormattedMessage
- id='admin.user_item.makeTeamAdmin'
- defaultMessage='Make Team Admin'
- />
- </a>
- </li>
- );
- }
-
- let makeMember = null;
- if (showMakeMember) {
- makeMember = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeMember}
- >
- <FormattedMessage
- id='admin.user_item.makeMember'
- defaultMessage='Make Member'
- />
- </a>
- </li>
- );
- }
-
- let makeActive = null;
- if (showMakeActive) {
- makeActive = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeActive}
- >
- <FormattedMessage
- id='admin.user_item.makeActive'
- defaultMessage='Make Active'
- />
- </a>
- </li>
- );
- }
-
- let makeNotActive = null;
- if (showMakeNotActive) {
- makeNotActive = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeNotActive}
- >
- <FormattedMessage
- id='admin.user_item.makeInactive'
- defaultMessage='Make Inactive'
- />
- </a>
- </li>
- );
- }
- const me = UserStore.getCurrentUser();
- let makeDemoteModal = null;
- if (this.props.user.id === me.id) {
- const title = (
- <FormattedMessage
- id='admin.user_item.confirmDemoteRoleTitle'
- defaultMessage='Confirm demotion from System Admin role'
- />
- );
-
- const message = (
- <div>
- <FormattedMessage
- id='admin.user_item.confirmDemoteDescription'
- defaultMessage="If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you\'ll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command."
- />
- <br/>
- <br/>
- <FormattedMessage
- id='admin.user_item.confirmDemotionCmd'
- defaultMessage='platform -assign_role -team_name="yourteam" -email="name@yourcompany.com" -role="system_admin"'
- />
- {serverError}
- </div>
- );
-
- const confirmButton = (
- <FormattedMessage
- id='admin.user_item.confirmDemotion'
- defaultMessage='Confirm Demotion'
- />
- );
-
- makeDemoteModal = (
- <ConfirmModal
- show={this.state.showDemoteModal}
- title={title}
- message={message}
- confirmButton={confirmButton}
- onConfirm={this.handleDemoteSubmit}
- onCancel={this.handleDemoteCancel}
- />
- );
- }
-
- return (
- <tr>
- <td className='more-modal__row'>
- <img
- className='more-modal__image pull-left'
- src={`/api/v1/users/${user.id}/image?time=${user.update_at}`}
- height='36'
- width='36'
- />
- <span className='more-modal__name'>{Utils.getDisplayName(user)}</span>
- <span className='more-modal__description'>{email}</span>
- <div className='dropdown member-drop'>
- <a
- href='#'
- className='dropdown-toggle theme'
- type='button'
- data-toggle='dropdown'
- aria-expanded='true'
- >
- <span>{currentRoles} </span>
- <span className='caret'></span>
- </a>
- <ul
- className='dropdown-menu member-menu'
- role='menu'
- >
- {makeAdmin}
- {makeMember}
- {makeActive}
- {makeNotActive}
- {makeSystemAdmin}
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleResetPassword}
- >
- <FormattedMessage
- id='admin.user_item.resetPwd'
- defaultMessage='Reset Password'
- />
- </a>
- </li>
- </ul>
- </div>
- {makeDemoteModal}
- {serverError}
- </td>
- </tr>
- );
- }
-}
-
-UserItem.propTypes = {
- user: React.PropTypes.object.isRequired,
- refreshProfiles: React.PropTypes.func.isRequired,
- doPasswordReset: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/analytics/doughnut_chart.jsx b/web/react/components/analytics/doughnut_chart.jsx
deleted file mode 100644
index 00bb66f0a..000000000
--- a/web/react/components/analytics/doughnut_chart.jsx
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class DoughnutChart extends React.Component {
- constructor(props) {
- super(props);
-
- this.initChart = this.initChart.bind(this);
- this.chart = null;
- }
-
- componentDidMount() {
- this.initChart(this.props);
- }
-
- componentWillReceiveProps(nextProps) {
- if (this.chart) {
- this.chart.destroy();
- this.initChart(nextProps);
- }
- }
-
- componentWillUnmount() {
- if (this.chart) {
- this.chart.destroy();
- }
- }
-
- initChart(props) {
- var el = ReactDOM.findDOMNode(this.refs.canvas);
- var ctx = el.getContext('2d');
- this.chart = new Chart(ctx).Doughnut(props.data, props.options || {}); //eslint-disable-line new-cap
- }
-
- render() {
- let content;
- if (this.props.data == null) {
- content = (
- <FormattedMessage
- id='analytics.chart.loading'
- defaultMessage='Loading...'
- />
- );
- } else {
- content = (
- <canvas
- ref='canvas'
- width={this.props.width}
- height={this.props.height}
- />
- );
- }
-
- return (
- <div className='col-sm-6'>
- <div className='total-count'>
- <div className='title'>
- {this.props.title}
- </div>
- <div className='content'>
- {content}
- </div>
- </div>
- </div>
- );
- }
-}
-
-DoughnutChart.propTypes = {
- title: React.PropTypes.node,
- width: React.PropTypes.string,
- height: React.PropTypes.string,
- data: React.PropTypes.array,
- options: React.PropTypes.object
-};
diff --git a/web/react/components/analytics/line_chart.jsx b/web/react/components/analytics/line_chart.jsx
deleted file mode 100644
index d1bb6b9cb..000000000
--- a/web/react/components/analytics/line_chart.jsx
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class LineChart extends React.Component {
- constructor(props) {
- super(props);
-
- this.initChart = this.initChart.bind(this);
- this.chart = null;
- }
-
- componentDidMount() {
- this.initChart();
- }
-
- componentDidUpdate() {
- if (this.chart) {
- this.chart.destroy();
- }
- this.initChart();
- }
-
- componentWillUnmount() {
- if (this.chart) {
- this.chart.destroy();
- }
- }
-
- initChart() {
- if (!this.refs.canvas) {
- return;
- }
- var el = ReactDOM.findDOMNode(this.refs.canvas);
- var ctx = el.getContext('2d');
- this.chart = new Chart(ctx).Line(this.props.data, this.props.options || {}); //eslint-disable-line new-cap
- }
-
- render() {
- let content;
- if (this.props.data == null) {
- content = (
- <FormattedMessage
- id='analytics.chart.loading'
- defaultMessage='Loading...'
- />
- );
- } else if (this.props.data.labels.length === 0) {
- content = (
- <h5>
- <FormattedMessage
- id='analytics.chart.meaningful'
- defaultMessage='Not enough data for a meaningful representation.'
- />
- </h5>
- );
- } else {
- content = (
- <canvas
- ref='canvas'
- width={this.props.width}
- height={this.props.height}
- />
- );
- }
-
- return (
- <div className='col-sm-12'>
- <div className='total-count by-day'>
- <div className='title'>
- {this.props.title}
- </div>
- <div className='content'>
- {content}
- </div>
- </div>
- </div>
- );
- }
-}
-
-LineChart.propTypes = {
- title: React.PropTypes.node.isRequired,
- width: React.PropTypes.string.isRequired,
- height: React.PropTypes.string.isRequired,
- data: React.PropTypes.object,
- options: React.PropTypes.object
-};
-
diff --git a/web/react/components/analytics/statistic_count.jsx b/web/react/components/analytics/statistic_count.jsx
deleted file mode 100644
index cf457310f..000000000
--- a/web/react/components/analytics/statistic_count.jsx
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class StatisticCount extends React.Component {
- render() {
- let loading = (
- <FormattedMessage
- id='analytics.chart.loading'
- defaultMessage='Loading...'
- />
- );
-
- return (
- <div className='col-sm-3'>
- <div className='total-count'>
- <div className='title'>
- {this.props.title}
- <i className={'fa ' + this.props.icon}/>
- </div>
- <div className='content'>{this.props.count == null ? loading : this.props.count}</div>
- </div>
- </div>
- );
- }
-}
-
-StatisticCount.propTypes = {
- title: React.PropTypes.node.isRequired,
- icon: React.PropTypes.string.isRequired,
- count: React.PropTypes.number
-};
diff --git a/web/react/components/analytics/system_analytics.jsx b/web/react/components/analytics/system_analytics.jsx
deleted file mode 100644
index a2b783a79..000000000
--- a/web/react/components/analytics/system_analytics.jsx
+++ /dev/null
@@ -1,346 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import LineChart from './line_chart.jsx';
-import DoughnutChart from './doughnut_chart.jsx';
-import StatisticCount from './statistic_count.jsx';
-
-import AnalyticsStore from '../../stores/analytics_store.jsx';
-
-import * as Utils from '../../utils/utils.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-import Constants from '../../utils/constants.jsx';
-const StatTypes = Constants.StatTypes;
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- analyticsPublicChannels: {
- id: 'analytics.system.publicChannels',
- defaultMessage: 'Public Channels'
- },
- analyticsPrivateGroups: {
- id: 'analytics.system.privateGroups',
- defaultMessage: 'Private Groups'
- },
- analyticsFilePosts: {
- id: 'analytics.system.totalFilePosts',
- defaultMessage: 'Posts with Files'
- },
- analyticsHashtagPosts: {
- id: 'analytics.system.totalHashtagPosts',
- defaultMessage: 'Posts with Hashtags'
- },
- analyticsTextPosts: {
- id: 'analytics.system.textPosts',
- defaultMessage: 'Posts with Text-only'
- }
-});
-
-class SystemAnalytics extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
-
- this.state = {stats: AnalyticsStore.getAllSystem()};
- }
-
- componentDidMount() {
- AnalyticsStore.addChangeListener(this.onChange);
-
- AsyncClient.getStandardAnalytics();
- AsyncClient.getPostsPerDayAnalytics();
- AsyncClient.getUsersPerDayAnalytics();
-
- if (global.window.mm_license.IsLicensed === 'true') {
- AsyncClient.getAdvancedAnalytics();
- }
- }
-
- componentWillUnmount() {
- AnalyticsStore.removeChangeListener(this.onChange);
- }
-
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(nextState.stats, this.state.stats)) {
- return true;
- }
-
- return false;
- }
-
- onChange() {
- this.setState({stats: AnalyticsStore.getAllSystem()});
- }
-
- render() {
- const stats = this.state.stats;
-
- let advancedCounts;
- let advancedGraphs;
- if (global.window.mm_license.IsLicensed === 'true') {
- advancedCounts = (
- <div className='row'>
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalSessions'
- defaultMessage='Total Sessions'
- />
- }
- icon='fa-signal'
- count={stats[StatTypes.TOTAL_SESSIONS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalCommands'
- defaultMessage='Total Commands'
- />
- }
- icon='fa-terminal'
- count={stats[StatTypes.TOTAL_COMMANDS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalIncomingWebhooks'
- defaultMessage='Incoming Webhooks'
- />
- }
- icon='fa-arrow-down'
- count={stats[StatTypes.TOTAL_IHOOKS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalOutgoingWebhooks'
- defaultMessage='Outgoing Webhooks'
- />
- }
- icon='fa-arrow-up'
- count={stats[StatTypes.TOTAL_OHOOKS]}
- />
- </div>
- );
-
- const channelTypeData = formatChannelDoughtnutData(stats[StatTypes.TOTAL_PUBLIC_CHANNELS], stats[StatTypes.TOTAL_PRIVATE_GROUPS], this.props.intl);
- const postTypeData = formatPostDoughtnutData(stats[StatTypes.TOTAL_FILE_POSTS], stats[StatTypes.TOTAL_HASHTAG_POSTS], stats[StatTypes.TOTAL_POSTS], this.props.intl);
-
- advancedGraphs = (
- <div className='row'>
- <DoughnutChart
- title={
- <FormattedMessage
- id='analytics.system.channelTypes'
- defaultMessage='Channel Types'
- />
- }
- data={channelTypeData}
- width='300'
- height='225'
- />
- <DoughnutChart
- title={
- <FormattedMessage
- id='analytics.system.postTypes'
- defaultMessage='Posts, Files and Hashtags'
- />
- }
- data={postTypeData}
- width='300'
- height='225'
- />
- </div>
- );
- }
-
- const postCountsDay = formatPostsPerDayData(stats[StatTypes.POST_PER_DAY]);
- const userCountsWithPostsDay = formatUsersWithPostsPerDayData(stats[StatTypes.USERS_WITH_POSTS_PER_DAY]);
-
- return (
- <div className='wrapper--fixed team_statistics'>
- <h3>
- <FormattedMessage
- id='analytics.system.title'
- defaultMessage='System Statistics'
- />
- </h3>
- <div className='row'>
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalUsers'
- defaultMessage='Total Users'
- />
- }
- icon='fa-user'
- count={stats[StatTypes.TOTAL_USERS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalTeams'
- defaultMessage='Total Teams'
- />
- }
- icon='fa-users'
- count={stats[StatTypes.TOTAL_TEAMS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalPosts'
- defaultMessage='Total Posts'
- />
- }
- icon='fa-comment'
- count={stats[StatTypes.TOTAL_POSTS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalChannels'
- defaultMessage='Total Channels'
- />
- }
- icon='fa-globe'
- count={stats[StatTypes.TOTAL_PUBLIC_CHANNELS] + stats[StatTypes.TOTAL_PRIVATE_GROUPS]}
- />
- </div>
- {advancedCounts}
- {advancedGraphs}
- <div className='row'>
- <LineChart
- title={
- <FormattedMessage
- id='analytics.system.totalPosts'
- defaultMessage='Total Posts'
- />
- }
- data={postCountsDay}
- width='740'
- height='225'
- />
- </div>
- <div className='row'>
- <LineChart
- title={
- <FormattedMessage
- id='analytics.system.activeUsers'
- defaultMessage='Active Users With Posts'
- />
- }
- data={userCountsWithPostsDay}
- width='740'
- height='225'
- />
- </div>
- </div>
- );
- }
-}
-
-SystemAnalytics.propTypes = {
- intl: intlShape.isRequired,
- team: React.PropTypes.object
-};
-
-export default injectIntl(SystemAnalytics);
-
-export function formatChannelDoughtnutData(totalPublic, totalPrivate, intl) {
- const {formatMessage} = intl;
- const channelTypeData = [
- {
- value: totalPublic,
- color: '#46BFBD',
- highlight: '#5AD3D1',
- label: formatMessage(holders.analyticsPublicChannels)
- },
- {
- value: totalPrivate,
- color: '#FDB45C',
- highlight: '#FFC870',
- label: formatMessage(holders.analyticsPrivateGroups)
- }
- ];
-
- return channelTypeData;
-}
-
-export function formatPostDoughtnutData(filePosts, hashtagPosts, totalPosts, intl) {
- const {formatMessage} = intl;
- const postTypeData = [
- {
- value: filePosts,
- color: '#46BFBD',
- highlight: '#5AD3D1',
- label: formatMessage(holders.analyticsFilePosts)
- },
- {
- value: hashtagPosts,
- color: '#F7464A',
- highlight: '#FF5A5E',
- label: formatMessage(holders.analyticsHashtagPosts)
- },
- {
- value: totalPosts - filePosts - hashtagPosts,
- color: '#FDB45C',
- highlight: '#FFC870',
- label: formatMessage(holders.analyticsTextPosts)
- }
- ];
-
- return postTypeData;
-}
-
-export function formatPostsPerDayData(data) {
- var chartData = {
- labels: [],
- datasets: [{
- fillColor: 'rgba(151,187,205,0.2)',
- strokeColor: 'rgba(151,187,205,1)',
- pointColor: 'rgba(151,187,205,1)',
- pointStrokeColor: '#fff',
- pointHighlightFill: '#fff',
- pointHighlightStroke: 'rgba(151,187,205,1)',
- data: []
- }]
- };
-
- for (var index in data) {
- if (data[index]) {
- var row = data[index];
- chartData.labels.push(row.name);
- chartData.datasets[0].data.push(row.value);
- }
- }
-
- return chartData;
-}
-
-export function formatUsersWithPostsPerDayData(data) {
- var chartData = {
- labels: [],
- datasets: [{
- fillColor: 'rgba(151,187,205,0.2)',
- strokeColor: 'rgba(151,187,205,1)',
- pointColor: 'rgba(151,187,205,1)',
- pointStrokeColor: '#fff',
- pointHighlightFill: '#fff',
- pointHighlightStroke: 'rgba(151,187,205,1)',
- data: []
- }]
- };
-
- for (var index in data) {
- if (data[index]) {
- var row = data[index];
- chartData.labels.push(row.name);
- chartData.datasets[0].data.push(row.value);
- }
- }
-
- return chartData;
-}
diff --git a/web/react/components/analytics/table_chart.jsx b/web/react/components/analytics/table_chart.jsx
deleted file mode 100644
index c94fa300b..000000000
--- a/web/react/components/analytics/table_chart.jsx
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../../utils/constants.jsx';
-
-const Tooltip = ReactBootstrap.Tooltip;
-const OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-export default class TableChart extends React.Component {
- render() {
- return (
- <div className='col-sm-6'>
- <div className='total-count recent-active-users'>
- <div className='title'>
- {this.props.title}
- </div>
- <div className='content'>
- <table>
- <tbody>
- {
- this.props.data.map((item) => {
- const tooltip = (
- <Tooltip id={'tip-table-entry-' + item.name}>
- {item.tip}
- </Tooltip>
- );
-
- return (
- <tr key={'table-entry-' + item.name}>
- <td>
- <OverlayTrigger
- delayShow={Constants.OVERLAY_TIME_DELAY}
- placement='top'
- overlay={tooltip}
- >
- <time>
- {item.name}
- </time>
- </OverlayTrigger>
- </td>
- <td>
- {item.value}
- </td>
- </tr>
- );
- })
- }
- </tbody>
- </table>
- </div>
- </div>
- </div>
- );
- }
-}
-
-TableChart.propTypes = {
- title: React.PropTypes.node,
- data: React.PropTypes.array
-};
diff --git a/web/react/components/analytics/team_analytics.jsx b/web/react/components/analytics/team_analytics.jsx
deleted file mode 100644
index 1236c070b..000000000
--- a/web/react/components/analytics/team_analytics.jsx
+++ /dev/null
@@ -1,235 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import LineChart from './line_chart.jsx';
-import StatisticCount from './statistic_count.jsx';
-import TableChart from './table_chart.jsx';
-
-import AnalyticsStore from '../../stores/analytics_store.jsx';
-
-import * as Utils from '../../utils/utils.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-import Constants from '../../utils/constants.jsx';
-const StatTypes = Constants.StatTypes;
-
-import {formatPostsPerDayData, formatUsersWithPostsPerDayData} from './system_analytics.jsx';
-import {injectIntl, intlShape, FormattedMessage, FormattedDate} from 'mm-intl';
-
-class TeamAnalytics extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
-
- this.state = {stats: AnalyticsStore.getAllTeam(this.props.team.id)};
- }
-
- componentDidMount() {
- AnalyticsStore.addChangeListener(this.onChange);
-
- this.getData(this.props.team.id);
- }
-
- getData(id) {
- AsyncClient.getStandardAnalytics(id);
- AsyncClient.getPostsPerDayAnalytics(id);
- AsyncClient.getUsersPerDayAnalytics(id);
- AsyncClient.getRecentAndNewUsersAnalytics(id);
- }
-
- componentWillUnmount() {
- AnalyticsStore.removeChangeListener(this.onChange);
- }
-
- componentWillReceiveProps(nextProps) {
- this.getData(nextProps.team.id);
- this.setState({stats: AnalyticsStore.getAllTeam(nextProps.team.id)});
- }
-
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(nextState.stats, this.state.stats)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(nextProps.team, this.props.team)) {
- return true;
- }
-
- return false;
- }
-
- onChange() {
- this.setState({stats: AnalyticsStore.getAllTeam(this.props.team.id)});
- }
-
- render() {
- const stats = this.state.stats;
- const postCountsDay = formatPostsPerDayData(stats[StatTypes.POST_PER_DAY]);
- const userCountsWithPostsDay = formatUsersWithPostsPerDayData(stats[StatTypes.USERS_WITH_POSTS_PER_DAY]);
- const recentActiveUsers = formatRecentUsersData(stats[StatTypes.RECENTLY_ACTIVE_USERS]);
- const newlyCreatedUsers = formatNewUsersData(stats[StatTypes.NEWLY_CREATED_USERS]);
-
- return (
- <div className='wrapper--fixed team_statistics'>
- <h3>
- <FormattedMessage
- id='analytics.team.title'
- defaultMessage='Team Statistics for {team}'
- values={{
- team: this.props.team.name
- }}
- />
- </h3>
- <div className='row'>
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.team.totalUsers'
- defaultMessage='Total Users'
- />
- }
- icon='fa-user'
- count={stats[StatTypes.TOTAL_USERS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.team.publicChannels'
- defaultMessage='Public Channels'
- />
- }
- icon='fa-users'
- count={stats[StatTypes.TOTAL_PUBLIC_CHANNELS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.team.privateGroups'
- defaultMessage='Private Groups'
- />
- }
- icon='fa-globe'
- count={stats[StatTypes.TOTAL_PRIVATE_GROUPS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.team.totalPosts'
- defaultMessage='Total Posts'
- />
- }
- icon='fa-comment'
- count={stats[StatTypes.TOTAL_POSTS]}
- />
- </div>
- <div className='row'>
- <LineChart
- title={
- <FormattedMessage
- id='analytics.team.totalPosts'
- defaultMessage='Total Posts'
- />
- }
- data={postCountsDay}
- width='740'
- height='225'
- />
- </div>
- <div className='row'>
- <LineChart
- title={
- <FormattedMessage
- id='analytics.team.activeUsers'
- defaultMessage='Active Users With Posts'
- />
- }
- data={userCountsWithPostsDay}
- width='740'
- height='225'
- />
- </div>
- <div className='row'>
- <TableChart
- title={
- <FormattedMessage
- id='analytics.team.activeUsers'
- defaultMessage='Recent Active Users'
- />
- }
- data={recentActiveUsers}
- />
- <TableChart
- title={
- <FormattedMessage
- id='analytics.team.newlyCreated'
- defaultMessage='Newly Created Users'
- />
- }
- data={newlyCreatedUsers}
- />
- </div>
- </div>
- );
- }
-}
-
-TeamAnalytics.propTypes = {
- intl: intlShape.isRequired,
- team: React.PropTypes.object.isRequired
-};
-
-export default injectIntl(TeamAnalytics);
-
-export function formatRecentUsersData(data) {
- if (data == null) {
- return [];
- }
-
- const formattedData = data.map((user) => {
- const item = {};
- item.name = user.username;
- item.value = (
- <FormattedDate
- value={user.last_activity_at}
- day='numeric'
- month='long'
- year='numeric'
- hour12={true}
- hour='2-digit'
- minute='2-digit'
- />
- );
- item.tip = user.email;
-
- return item;
- });
-
- return formattedData;
-}
-
-export function formatNewUsersData(data) {
- if (data == null) {
- return [];
- }
-
- const formattedData = data.map((user) => {
- const item = {};
- item.name = user.username;
- item.value = (
- <FormattedDate
- value={user.create_at}
- day='numeric'
- month='long'
- year='numeric'
- hour12={true}
- hour='2-digit'
- minute='2-digit'
- />
- );
- item.tip = user.email;
-
- return item;
- });
-
- return formattedData;
-}
diff --git a/web/react/components/audio_video_preview.jsx b/web/react/components/audio_video_preview.jsx
deleted file mode 100644
index 739c8c95e..000000000
--- a/web/react/components/audio_video_preview.jsx
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../utils/constants.jsx';
-import FileInfoPreview from './file_info_preview.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-export default class AudioVideoPreview extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleFileInfoChanged = this.handleFileInfoChanged.bind(this);
- this.handleLoadError = this.handleLoadError.bind(this);
-
- this.stop = this.stop.bind(this);
-
- this.state = {
- canPlay: true
- };
- }
-
- componentWillMount() {
- this.handleFileInfoChanged(this.props.fileInfo);
- }
-
- componentDidMount() {
- if (this.refs.source) {
- $(ReactDOM.findDOMNode(this.refs.source)).one('error', this.handleLoadError);
- }
- }
-
- componentWillReceiveProps(nextProps) {
- if (this.props.fileUrl !== nextProps.fileUrl) {
- this.handleFileInfoChanged(nextProps.fileInfo);
- }
- }
-
- handleFileInfoChanged(fileInfo) {
- let video = ReactDOM.findDOMNode(this.refs.video);
- if (!video) {
- video = document.createElement('video');
- }
-
- const canPlayType = video.canPlayType(fileInfo.mime_type);
-
- this.setState({
- canPlay: canPlayType === 'probably' || canPlayType === 'maybe'
- });
- }
-
- componentDidUpdate() {
- if (this.refs.source) {
- $(ReactDOM.findDOMNode(this.refs.source)).one('error', this.handleLoadError);
- }
- }
-
- handleLoadError() {
- this.setState({
- canPlay: false
- });
- }
-
- stop() {
- if (this.refs.video) {
- const video = ReactDOM.findDOMNode(this.refs.video);
- video.pause();
- video.currentTime = 0;
- }
- }
-
- render() {
- if (!this.state.canPlay) {
- return (
- <FileInfoPreview
- filename={this.props.filename}
- fileUrl={this.props.fileUrl}
- fileInfo={this.props.fileInfo}
- formatMessage={this.props.formatMessage}
- />
- );
- }
-
- let width = Constants.WEB_VIDEO_WIDTH;
- let height = Constants.WEB_VIDEO_HEIGHT;
- if (Utils.isMobile()) {
- width = Constants.MOBILE_VIDEO_WIDTH;
- height = Constants.MOBILE_VIDEO_HEIGHT;
- }
-
- // add a key to the video to prevent React from using an old video source while a new one is loading
- return (
- <video
- key={this.props.filename}
- ref='video'
- style={{maxHeight: this.props.maxHeight}}
- data-setup='{}'
- controls='controls'
- width={width}
- height={height}
- >
- <source
- ref='source'
- src={this.props.fileUrl}
- />
- </video>
- );
- }
-}
-
-AudioVideoPreview.propTypes = {
- filename: React.PropTypes.string.isRequired,
- fileUrl: React.PropTypes.string.isRequired,
- fileInfo: React.PropTypes.object.isRequired,
- maxHeight: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]).isRequired,
- formatMessage: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/audit_table.jsx b/web/react/components/audit_table.jsx
deleted file mode 100644
index 917093840..000000000
--- a/web/react/components/audit_table.jsx
+++ /dev/null
@@ -1,624 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../stores/user_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedDate, FormattedTime} from 'mm-intl';
-
-const holders = defineMessages({
- sessionRevoked: {
- id: 'audit_table.sessionRevoked',
- defaultMessage: 'The session with id {sessionId} was revoked'
- },
- channelCreated: {
- id: 'audit_table.channelCreated',
- defaultMessage: 'Created the {channelName} channel/group'
- },
- establishedDM: {
- id: 'audit_table.establishedDM',
- defaultMessage: 'Established a direct message channel with {username}'
- },
- nameUpdated: {
- id: 'audit_table.nameUpdated',
- defaultMessage: 'Updated the {channelName} channel/group name'
- },
- headerUpdated: {
- id: 'audit_table.headerUpdated',
- defaultMessage: 'Updated the {channelName} channel/group header'
- },
- channelDeleted: {
- id: 'audit_table.channelDeleted',
- defaultMessage: 'Deleted the channel/group with the URL {url}'
- },
- userAdded: {
- id: 'audit_table.userAdded',
- defaultMessage: 'Added {username} to the {channelName} channel/group'
- },
- userRemoved: {
- id: 'audit_table.userRemoved',
- defaultMessage: 'Removed {username} to the {channelName} channel/group'
- },
- attemptedRegisterApp: {
- id: 'audit_table.attemptedRegisterApp',
- defaultMessage: 'Attempted to register a new OAuth Application with ID {id}'
- },
- attemptedAllowOAuthAccess: {
- id: 'audit_table.attemptedAllowOAuthAccess',
- defaultMessage: 'Attempted to allow a new OAuth service access'
- },
- successfullOAuthAccess: {
- id: 'audit_table.successfullOAuthAccess',
- defaultMessage: 'Successfully gave a new OAuth service access'
- },
- failedOAuthAccess: {
- id: 'audit_table.failedOAuthAccess',
- defaultMessage: 'Failed to allow a new OAuth service access - the redirect URI did not match the previously registered callback'
- },
- attemptedOAuthToken: {
- id: 'audit_table.attemptedOAuthToken',
- defaultMessage: 'Attempted to get an OAuth access token'
- },
- successfullOAuthToken: {
- id: 'audit_table.successfullOAuthToken',
- defaultMessage: 'Successfully added a new OAuth service'
- },
- oauthTokenFailed: {
- id: 'audit_table.oauthTokenFailed',
- defaultMessage: 'Failed to get an OAuth access token - {token}'
- },
- attemptedLogin: {
- id: 'audit_table.attemptedLogin',
- defaultMessage: 'Attempted to login'
- },
- successfullLogin: {
- id: 'audit_table.successfullLogin',
- defaultMessage: 'Successfully logged in'
- },
- failedLogin: {
- id: 'audit_table.failedLogin',
- defaultMessage: 'FAILED login attempt'
- },
- updatePicture: {
- id: 'audit_table.updatePicture',
- defaultMessage: 'Updated your profile picture'
- },
- updateGeneral: {
- id: 'audit_table.updateGeneral',
- defaultMessage: 'Updated the general settings of your account'
- },
- attemptedPassword: {
- id: 'audit_table.attemptedPassword',
- defaultMessage: 'Attempted to change password'
- },
- successfullPassword: {
- id: 'audit_table.successfullPassword',
- defaultMessage: 'Successfully changed password'
- },
- failedPassword: {
- id: 'audit_table.failedPassword',
- defaultMessage: 'Failed to change password - tried to update user password who was logged in through oauth'
- },
- updatedRol: {
- id: 'audit_table.updatedRol',
- defaultMessage: 'Updated user role(s) to '
- },
- member: {
- id: 'audit_table.member',
- defaultMessage: 'member'
- },
- accountActive: {
- id: 'audit_table.accountActive',
- defaultMessage: 'Account made active'
- },
- accountInactive: {
- id: 'audit_table.accountInactive',
- defaultMessage: 'Account made inactive'
- },
- by: {
- id: 'audit_table.by',
- defaultMessage: ' by {username}'
- },
- byAdmin: {
- id: 'audit_table.byAdmin',
- defaultMessage: ' by an admin'
- },
- sentEmail: {
- id: 'audit_table.sentEmail',
- defaultMessage: 'Sent an email to {email} to reset your password'
- },
- attemptedReset: {
- id: 'audit_table.attemptedReset',
- defaultMessage: 'Attempted to reset password'
- },
- successfullReset: {
- id: 'audit_table.successfullReset',
- defaultMessage: 'Successfully reset password'
- },
- updateGlobalNotifications: {
- id: 'audit_table.updateGlobalNotifications',
- defaultMessage: 'Updated your global notification settings'
- },
- attemptedWebhookCreate: {
- id: 'audit_table.attemptedWebhookCreate',
- defaultMessage: 'Attempted to create a webhook'
- },
- succcessfullWebhookCreate: {
- id: 'audit_table.successfullWebhookCreate',
- defaultMessage: 'Successfully created a webhook'
- },
- failedWebhookCreate: {
- id: 'audit_table.failedWebhookCreate',
- defaultMessage: 'Failed to create a webhook - bad channel permissions'
- },
- attemptedWebhookDelete: {
- id: 'audit_table.attemptedWebhookDelete',
- defaultMessage: 'Attempted to delete a webhook'
- },
- successfullWebhookDelete: {
- id: 'audit_table.successfullWebhookDelete',
- defaultMessage: 'Successfully deleted a webhook'
- },
- failedWebhookDelete: {
- id: 'audit_table.failedWebhookDelete',
- defaultMessage: 'Failed to delete a webhook - inappropriate conditions'
- },
- logout: {
- id: 'audit_table.logout',
- defaultMessage: 'Logged out of your account'
- },
- verified: {
- id: 'audit_table.verified',
- defaultMessage: 'Sucessfully verified your email address'
- },
- revokedAll: {
- id: 'audit_table.revokedAll',
- defaultMessage: 'Revoked all current sessions for the team'
- },
- loginAttempt: {
- id: 'audit_table.loginAttempt',
- defaultMessage: ' (Login attempt)'
- },
- loginFailure: {
- id: 'audit_table.loginFailure',
- defaultMessage: ' (Login failure)'
- },
- attemptedLicenseAdd: {
- id: 'audit_table.attemptedLicenseAdd',
- defaultMessage: 'Attempted to add new license'
- },
- successfullLicenseAdd: {
- id: 'audit_table.successfullLicenseAdd',
- defaultMessage: 'Successfully added new license'
- },
- failedExpiredLicenseAdd: {
- id: 'audit_table.failedExpiredLicenseAdd',
- defaultMessage: 'Failed to add a new license as it has either expired or not yet been started'
- },
- failedInvalidLicenseAdd: {
- id: 'audit_table.failedInvalidLicenseAdd',
- defaultMessage: 'Failed to add an invalid license'
- },
- licenseRemoved: {
- id: 'audit_table.licenseRemoved',
- defaultMessage: 'Successfully removed a license'
- }
-});
-
-class AuditTable extends React.Component {
- render() {
- var accessList = [];
-
- const {formatMessage} = this.props.intl;
- for (var i = 0; i < this.props.audits.length; i++) {
- const audit = this.props.audits[i];
- const auditInfo = formatAuditInfo(audit, formatMessage);
-
- let uContent;
- if (this.props.showUserId) {
- uContent = <td>{auditInfo.userId}</td>;
- }
-
- let iContent;
- if (this.props.showIp) {
- iContent = <td>{auditInfo.ip}</td>;
- }
-
- let sContent;
- if (this.props.showSession) {
- sContent = <td>{auditInfo.sessionId}</td>;
- }
-
- let descStyle = {};
- if (auditInfo.desc.toLowerCase().indexOf('fail') !== -1) {
- descStyle.color = 'red';
- }
-
- accessList[i] = (
- <tr key={audit.id}>
- <td>{auditInfo.timestamp}</td>
- {uContent}
- <td style={descStyle}>{auditInfo.desc}</td>
- {iContent}
- {sContent}
- </tr>
- );
- }
-
- let userIdContent;
- if (this.props.showUserId) {
- userIdContent = (
- <th>
- <FormattedMessage
- id='audit_table.userId'
- defaultMessage='User ID'
- />
- </th>
- );
- }
-
- let ipContent;
- if (this.props.showIp) {
- ipContent = (
- <th>
- <FormattedMessage
- id='audit_table.ip'
- defaultMessage='IP Address'
- />
- </th>
- );
- }
-
- let sessionContent;
- if (this.props.showSession) {
- sessionContent = (
- <th>
- <FormattedMessage
- id='audit_table.session'
- defaultMessage='Session ID'
- />
- </th>
- );
- }
-
- return (
- <table className='table'>
- <thead>
- <tr>
- <th>
- <FormattedMessage
- id='audit_table.timestamp'
- defaultMessage='Timestamp'
- />
- </th>
- {userIdContent}
- <th>
- <FormattedMessage
- id='audit_table.action'
- defaultMessage='Action'
- />
- </th>
- {ipContent}
- {sessionContent}
- </tr>
- </thead>
- <tbody>
- {accessList}
- </tbody>
- </table>
- );
- }
-}
-
-AuditTable.propTypes = {
- intl: intlShape.isRequired,
- audits: React.PropTypes.array.isRequired,
- showUserId: React.PropTypes.bool,
- showIp: React.PropTypes.bool,
- showSession: React.PropTypes.bool
-};
-
-export default injectIntl(AuditTable);
-
-export function formatAuditInfo(audit, formatMessage) {
- const actionURL = audit.action.replace(/\/api\/v[1-9]/, '');
- let auditDesc = '';
-
- if (actionURL.indexOf('/channels') === 0) {
- const channelInfo = audit.extra_info.split(' ');
- const channelNameField = channelInfo[0].split('=');
-
- let channelURL = '';
- let channelObj;
- let channelName = '';
- if (channelNameField.indexOf('name') >= 0) {
- channelURL = channelNameField[channelNameField.indexOf('name') + 1];
- channelObj = ChannelStore.getByName(channelURL);
- if (channelObj) {
- channelName = channelObj.display_name;
- } else {
- channelName = channelURL;
- }
- }
-
- switch (actionURL) {
- case '/channels/create':
- auditDesc = formatMessage(holders.channelCreated, {channelName});
- break;
- case '/channels/create_direct':
- auditDesc = formatMessage(holders.establishedDM, {username: Utils.getDirectTeammate(channelObj.id).username});
- break;
- case '/channels/update':
- auditDesc = formatMessage(holders.nameUpdated, {channelName});
- break;
- case '/channels/update_desc': // support the old path
- case '/channels/update_header':
- auditDesc = formatMessage(holders.headerUpdated, {channelName});
- break;
- default: {
- let userIdField = [];
- let userId = '';
- let username = '';
-
- if (channelInfo[1]) {
- userIdField = channelInfo[1].split('=');
-
- if (userIdField.indexOf('user_id') >= 0) {
- userId = userIdField[userIdField.indexOf('user_id') + 1];
- username = UserStore.getProfile(userId).username;
- }
- }
-
- if (/\/channels\/[A-Za-z0-9]+\/delete/.test(actionURL)) {
- auditDesc = formatMessage(holders.channelDeleted, {url: channelURL});
- } else if (/\/channels\/[A-Za-z0-9]+\/add/.test(actionURL)) {
- auditDesc = formatMessage(holders.userAdded, {username, channelName});
- } else if (/\/channels\/[A-Za-z0-9]+\/remove/.test(actionURL)) {
- auditDesc = formatMessage(holders.userRemoved, {username, channelName});
- }
-
- break;
- }
- }
- } else if (actionURL.indexOf('/oauth') === 0) {
- const oauthInfo = audit.extra_info.split(' ');
-
- switch (actionURL) {
- case '/oauth/register': {
- const clientIdField = oauthInfo[0].split('=');
-
- if (clientIdField[0] === 'client_id') {
- auditDesc = formatMessage(holders.attemptedRegisterApp, {id: clientIdField[1]});
- }
-
- break;
- }
- case '/oauth/allow':
- if (oauthInfo[0] === 'attempt') {
- auditDesc = formatMessage(holders.attemptedAllowOAuthAccess);
- } else if (oauthInfo[0] === 'success') {
- auditDesc = formatMessage(holders.successfullOAuthAccess);
- } else if (oauthInfo[0] === 'fail - redirect_uri did not match registered callback') {
- auditDesc = formatMessage(holders.failedOAuthAccess);
- }
-
- break;
- case '/oauth/access_token':
- if (oauthInfo[0] === 'attempt') {
- auditDesc = formatMessage(holders.attemptedOAuthToken);
- } else if (oauthInfo[0] === 'success') {
- auditDesc = formatMessage(holders.successfullOAuthToken);
- } else {
- const oauthTokenFailure = oauthInfo[0].split('-');
-
- if (oauthTokenFailure[0].trim() === 'fail' && oauthTokenFailure[1]) {
- auditDesc = formatMessage(oauthTokenFailure, {token: oauthTokenFailure[1].trim()});
- }
- }
-
- break;
- default:
- break;
- }
- } else if (actionURL.indexOf('/users') === 0) {
- const userInfo = audit.extra_info.split(' ');
-
- switch (actionURL) {
- case '/users/login':
- if (userInfo[0] === 'attempt') {
- auditDesc = formatMessage(holders.attemptedLogin);
- } else if (userInfo[0] === 'success') {
- auditDesc = formatMessage(holders.successfullLogin);
- } else if (userInfo[0]) {
- auditDesc = formatMessage(holders.failedLogin);
- }
-
- break;
- case '/users/revoke_session':
- auditDesc = formatMessage(holders.sessionRevoked, {sessionId: userInfo[0].split('=')[1]});
- break;
- case '/users/newimage':
- auditDesc = formatMessage(holders.updatePicture);
- break;
- case '/users/update':
- auditDesc = formatMessage(holders.updateGeneral);
- break;
- case '/users/newpassword':
- if (userInfo[0] === 'attempted') {
- auditDesc = formatMessage(holders.attemptedPassword);
- } else if (userInfo[0] === 'completed') {
- auditDesc = formatMessage(holders.successfullPassword);
- } else if (userInfo[0] === 'failed - tried to update user password who was logged in through oauth') {
- auditDesc = formatMessage(holders.failedPassword);
- }
-
- break;
- case '/users/update_roles': {
- const userRoles = userInfo[0].split('=')[1];
-
- auditDesc = formatMessage(holders.updatedRol);
- if (userRoles.trim()) {
- auditDesc += userRoles;
- } else {
- auditDesc += formatMessage(holders.member);
- }
-
- break;
- }
- case '/users/update_active': {
- const updateType = userInfo[0].split('=')[0];
- const updateField = userInfo[0].split('=')[1];
-
- /* Either describes account activation/deactivation or a revoked session as part of an account deactivation */
- if (updateType === 'active') {
- if (updateField === 'true') {
- auditDesc = formatMessage(holders.accountActive);
- } else if (updateField === 'false') {
- auditDesc = formatMessage(holders.accountInactive);
- }
-
- const actingUserInfo = userInfo[1].split('=');
- if (actingUserInfo[0] === 'session_user') {
- const actingUser = UserStore.getProfile(actingUserInfo[1]);
- const user = UserStore.getCurrentUser();
- if (user && actingUser && (Utils.isAdmin(user.roles) || Utils.isSystemAdmin(user.roles))) {
- auditDesc += formatMessage(holders.by, {username: actingUser.username});
- } else if (user && actingUser) {
- auditDesc += formatMessage(holders.byAdmin);
- }
- }
- } else if (updateType === 'session_id') {
- auditDesc = formatMessage(holders.sessionRevoked, {sessionId: updateField});
- }
-
- break;
- }
- case '/users/send_password_reset':
- auditDesc = formatMessage(holders.sentEmail, {email: userInfo[0].split('=')[1]});
- break;
- case '/users/reset_password':
- if (userInfo[0] === 'attempt') {
- auditDesc = formatMessage(holders.attemptedReset);
- } else if (userInfo[0] === 'success') {
- auditDesc = formatMessage(holders.successfullReset);
- }
-
- break;
- case '/users/update_notify':
- auditDesc = formatMessage(holders.updateGlobalNotifications);
- break;
- default:
- break;
- }
- } else if (actionURL.indexOf('/hooks') === 0) {
- const webhookInfo = audit.extra_info;
-
- switch (actionURL) {
- case '/hooks/incoming/create':
- if (webhookInfo === 'attempt') {
- auditDesc = formatMessage(holders.attemptedWebhookCreate);
- } else if (webhookInfo === 'success') {
- auditDesc = formatMessage(holders.succcessfullWebhookCreate);
- } else if (webhookInfo === 'fail - bad channel permissions') {
- auditDesc = formatMessage(holders.failedWebhookCreate);
- }
-
- break;
- case '/hooks/incoming/delete':
- if (webhookInfo === 'attempt') {
- auditDesc = formatMessage(holders.attemptedWebhookDelete);
- } else if (webhookInfo === 'success') {
- auditDesc = formatMessage(holders.successfullWebhookDelete);
- } else if (webhookInfo === 'fail - inappropriate conditions') {
- auditDesc = formatMessage(holders.failedWebhookDelete);
- }
-
- break;
- default:
- break;
- }
- } else if (actionURL.indexOf('/license') === 0) {
- const licenseInfo = audit.extra_info;
-
- switch (actionURL) {
- case '/license/add':
- if (licenseInfo === 'attempt') {
- auditDesc = formatMessage(holders.attemptedLicenseAdd);
- } else if (licenseInfo === 'success') {
- auditDesc = formatMessage(holders.successfullLicenseAdd);
- } else if (licenseInfo === 'failed - expired or non-started license') {
- auditDesc = formatMessage(holders.failedExpiredLicenseAdd);
- } else if (licenseInfo === 'failed - invalid license') {
- auditDesc = formatMessage(holders.failedInvalidLicenseAdd);
- }
-
- break;
- case '/license/remove':
- auditDesc = formatMessage(holders.licenseRemoved);
- break;
- default:
- break;
- }
- } else {
- switch (actionURL) {
- case '/logout':
- auditDesc = formatMessage(holders.logout);
- break;
- case '/verify_email':
- auditDesc = formatMessage(holders.verified);
- break;
- default:
- break;
- }
- }
-
- /* If all else fails... */
- if (!auditDesc) {
- /* Currently not called anywhere */
- if (audit.extra_info.indexOf('revoked_all=') >= 0) {
- auditDesc = formatMessage(holders.revokedAll);
- } else {
- let actionDesc = '';
- if (actionURL && actionURL.lastIndexOf('/') !== -1) {
- actionDesc = actionURL.substring(actionURL.lastIndexOf('/') + 1).replace('_', ' ');
- actionDesc = Utils.toTitleCase(actionDesc);
- }
-
- let extraInfoDesc = '';
- if (audit.extra_info) {
- extraInfoDesc = audit.extra_info;
-
- if (extraInfoDesc.indexOf('=') !== -1) {
- extraInfoDesc = extraInfoDesc.substring(extraInfoDesc.indexOf('=') + 1);
- }
- }
- auditDesc = actionDesc + ' ' + extraInfoDesc;
- }
- }
-
- const date = new Date(audit.create_at);
- const auditInfo = {};
- auditInfo.timestamp = (
- <div>
- <FormattedDate
- value={date}
- day='2-digit'
- month='short'
- year='numeric'
- />
- {' - '}
- <FormattedTime
- value={date}
- hour='2-digit'
- minute='2-digit'
- />
- </div>
- );
- auditInfo.userId = audit.user_id;
- auditInfo.desc = auditDesc;
- auditInfo.ip = audit.ip_address;
- auditInfo.sessionId = audit.session_id;
-
- return auditInfo;
-}
diff --git a/web/react/components/authorize.jsx b/web/react/components/authorize.jsx
deleted file mode 100644
index 4b1cebcf4..000000000
--- a/web/react/components/authorize.jsx
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-export default class Authorize extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleAllow = this.handleAllow.bind(this);
- this.handleDeny = this.handleDeny.bind(this);
-
- this.state = {};
- }
- handleAllow() {
- const responseType = this.props.responseType;
- const clientId = this.props.clientId;
- const redirectUri = this.props.redirectUri;
- const state = this.props.state;
- const scope = this.props.scope;
-
- Client.allowOAuth2(responseType, clientId, redirectUri, state, scope,
- (data) => {
- if (data.redirect) {
- window.location.replace(data.redirect);
- }
- },
- () => {
- //Do nothing on error
- }
- );
- }
- handleDeny() {
- window.location.replace(this.props.redirectUri + '?error=access_denied');
- }
- render() {
- return (
- <div className='container-fluid'>
- <div className='prompt'>
- <div className='prompt__heading'>
- <div className='prompt__app-icon'>
- <img
- src='/static/images/icon50x50.png'
- width='50'
- height='50'
- alt=''
- />
- </div>
- <div className='text'>
- <FormattedMessage
- id='authorize.title'
- defaultMessage='An application would like to connect to your {teamName} account'
- values={{
- teamName: this.props.teamName
- }}
- />
- </div>
- </div>
- <p>
- <FormattedHTMLMessage
- id='authorize.app'
- defaultMessage='The app <strong>{appName}</strong> would like the ability to access and modify your basic information.'
- values={{
- appName: this.props.appName
- }}
- />
- </p>
- <h2 className='prompt__allow'>
- <FormattedHTMLMessage
- id='authorize.access'
- defaultMessage='Allow <strong>{appName}</strong> access?'
- values={{
- appName: this.props.appName
- }}
- />
- </h2>
- <div className='prompt__buttons'>
- <button
- type='submit'
- className='btn authorize-btn'
- onClick={this.handleDeny}
- >
- <FormattedMessage
- id='authorize.deny'
- defaultMessage='Deny'
- />
- </button>
- <button
- type='submit'
- className='btn btn-primary authorize-btn'
- onClick={this.handleAllow}
- >
- <FormattedMessage
- id='authorize.allow'
- defaultMessage='Allow'
- />
- </button>
- </div>
- </div>
- </div>
- );
- }
-}
-
-Authorize.propTypes = {
- appName: React.PropTypes.string,
- teamName: React.PropTypes.string,
- responseType: React.PropTypes.string,
- clientId: React.PropTypes.string,
- redirectUri: React.PropTypes.string,
- state: React.PropTypes.string,
- scope: React.PropTypes.string
-};
diff --git a/web/react/components/center_panel.jsx b/web/react/components/center_panel.jsx
deleted file mode 100644
index 9a5f7a5d1..000000000
--- a/web/react/components/center_panel.jsx
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import TutorialIntroScreens from './tutorial/tutorial_intro_screens.jsx';
-import CreatePost from './create_post.jsx';
-import PostsViewContainer from './posts_view_container.jsx';
-import PostFocusView from './post_focus_view.jsx';
-import ChannelHeader from './channel_header.jsx';
-import Navbar from './navbar.jsx';
-import FileUploadOverlay from './file_upload_overlay.jsx';
-
-import PreferenceStore from '../stores/preference_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-import Constants from '../utils/constants.jsx';
-const TutorialSteps = Constants.TutorialSteps;
-const Preferences = Constants.Preferences;
-
-export default class CenterPanel extends React.Component {
- constructor(props) {
- super(props);
-
- this.getStateFromStores = this.getStateFromStores.bind(this);
- this.validState = this.validState.bind(this);
- this.onStoresChange = this.onStoresChange.bind(this);
-
- this.state = this.getStateFromStores();
- }
- getStateFromStores() {
- const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
- return {
- showTutorialScreens: tutorialStep <= TutorialSteps.INTRO_SCREENS,
- showPostFocus: ChannelStore.getPostMode() === ChannelStore.POST_MODE_FOCUS,
- user: UserStore.getCurrentUser(),
- channel: ChannelStore.getCurrent(),
- profiles: JSON.parse(JSON.stringify(UserStore.getProfiles()))
- };
- }
- validState() {
- return this.state.user && this.state.channel && this.state.profiles;
- }
- onStoresChange() {
- this.setState(this.getStateFromStores());
- }
- componentDidMount() {
- PreferenceStore.addChangeListener(this.onStoresChange);
- ChannelStore.addChangeListener(this.onStoresChange);
- UserStore.addChangeListener(this.onStoresChange);
- }
- componentWillUnmount() {
- PreferenceStore.removeChangeListener(this.onStoresChange);
- ChannelStore.removeChangeListener(this.onStoresChange);
- UserStore.removeChangeListener(this.onStoresChange);
- }
- render() {
- if (!this.validState()) {
- return null;
- }
- const channel = this.state.channel;
- var handleClick = null;
- let postsContainer;
- let createPost;
- if (this.state.showTutorialScreens) {
- postsContainer = <TutorialIntroScreens/>;
- createPost = null;
- } else if (this.state.showPostFocus) {
- postsContainer = <PostFocusView profiles={this.state.profiles}/>;
-
- handleClick = function clickHandler(e) {
- e.preventDefault();
- Utils.switchChannel(channel);
- };
-
- createPost = (
- <div
- id='archive-link-home'
- onClick={handleClick}
- >
- <a href=''>
- <FormattedMessage
- id='center_panel.recent'
- defaultMessage='Click here to jump to recent messages. '
- />
- <i className='fa fa-arrow-down'></i>
- </a>
- </div>
- );
- } else {
- postsContainer = <PostsViewContainer profiles={this.state.profiles}/>;
- createPost = (
- <div
- className='post-create__container'
- id='post-create'
- >
- <CreatePost/>
- </div>
- );
- }
-
- return (
- <div className='inner-wrap channel__wrap'>
- <div className='row header'>
- <div id='navbar'>
- <Navbar/>
- </div>
- </div>
- <div className='row main'>
- <FileUploadOverlay
- id='file_upload_overlay'
- overlayType='center'
- />
- <div
- id='app-content'
- className='app__content'
- >
- <div id='channel-header'>
- <ChannelHeader
- user={this.state.user}
- />
- </div>
- {postsContainer}
- {createPost}
- </div>
- </div>
- </div>
- );
- }
-}
-
-CenterPanel.defaultProps = {
-};
-
-CenterPanel.propTypes = {
-};
diff --git a/web/react/components/change_url_modal.jsx b/web/react/components/change_url_modal.jsx
deleted file mode 100644
index 49d1b86b4..000000000
--- a/web/react/components/change_url_modal.jsx
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-var Modal = ReactBootstrap.Modal;
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class ChangeUrlModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.onURLChanged = this.onURLChanged.bind(this);
- this.doSubmit = this.doSubmit.bind(this);
- this.doCancel = this.doCancel.bind(this);
-
- this.state = {
- currentURL: props.currentURL,
- urlError: '',
- userEdit: false
- };
- }
- componentWillReceiveProps(nextProps) {
- // This check prevents the url being deleted when we re-render
- // because of user status check
- if (!this.state.userEdit) {
- this.setState({
- currentURL: nextProps.currentURL
- });
- }
- }
- componentDidUpdate(prevProps) {
- if (this.props.show === true && prevProps.show === false) {
- ReactDOM.findDOMNode(this.refs.urlinput).select();
- }
- }
- onURLChanged(e) {
- const url = e.target.value.trim();
- this.setState({currentURL: url.replace(/[^A-Za-z0-9-_]/g, '').toLowerCase(), userEdit: true});
- }
- getURLError(url) {
- let error = []; //eslint-disable-line prefer-const
- if (url.length < 2) {
- error.push(
- <span key='error1'>
- <FormattedMessage
- id='change_url.longer'
- defaultMessage='Must be longer than two characters'
- />
- <br/>
- </span>
- );
- }
- if (url.charAt(0) === '-' || url.charAt(0) === '_') {
- error.push(
- <span key='error2'>
- <FormattedMessage
- id='change_url.startWithLetter'
- defaultMessage='Must start with a letter or number'
- />
- <br/>
- </span>
- );
- }
- if (url.length > 1 && (url.charAt(url.length - 1) === '-' || url.charAt(url.length - 1) === '_')) {
- error.push(
- <span key='error3'>
- <FormattedMessage
- id='change_url.endWithLetter'
- defaultMessage='Must end with a letter or number'
- />
- <br/>
- </span>);
- }
- if (url.indexOf('__') > -1) {
- error.push(
- <span key='error4'>
- <FormattedMessage
- id='change_url.noUnderscore'
- defaultMessage='Can not contain two underscores in a row.'
- />
- <br/>
- </span>);
- }
-
- // In case of error we don't detect
- if (error.length === 0) {
- error.push(
- <span key='errorlast'>
- <FormattedMessage
- id='change_url.invalidUrl'
- defaultMessage='Invalid URL'
- />
- <br/>
- </span>);
- }
- return error;
- }
- doSubmit(e) {
- e.preventDefault();
-
- const url = ReactDOM.findDOMNode(this.refs.urlinput).value;
- const cleanedURL = Utils.cleanUpUrlable(url);
- if (cleanedURL !== url || url.length < 2 || url.indexOf('__') > -1) {
- this.setState({urlError: this.getURLError(url)});
- return;
- }
- this.setState({urlError: '', userEdit: false});
- this.props.onModalSubmit(url);
- }
- doCancel() {
- this.setState({urlError: '', userEdit: false});
- this.props.onModalDismissed();
- }
- render() {
- let urlClass = 'input-group input-group--limit';
- let urlError = null;
- let serverError = null;
-
- if (this.state.urlError) {
- urlClass += ' has-error';
- urlError = (<p className='input__help error'>{this.state.urlError}</p>);
- }
-
- if (this.props.serverError) {
- serverError = <div className='form-group has-error'><p className='input__help error'>{this.props.serverError}</p></div>;
- }
-
- const fullTeamUrl = Utils.getTeamURLFromAddressBar();
- const teamURL = Utils.getShortenedTeamURL();
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.doCancel}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>{this.props.title}</Modal.Title>
- </Modal.Header>
- <form
- role='form'
- className='form-horizontal'
- >
- <Modal.Body>
- <div className='modal-intro'>{this.props.description}</div>
- <div className='form-group'>
- <label className='col-sm-2 form__label control-label'>{this.props.urlLabel}</label>
- <div className='col-sm-10'>
- <div className={urlClass}>
- <span
- data-toggle='tooltip'
- title={fullTeamUrl}
- className='input-group-addon'
- >
- {teamURL}
- </span>
- <input
- type='text'
- ref='urlinput'
- className='form-control'
- maxLength='22'
- onChange={this.onURLChanged}
- value={this.state.currentURL}
- autoFocus={true}
- tabIndex='1'
- />
- </div>
- {urlError}
- {serverError}
- </div>
- </div>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.doCancel}
- >
- <FormattedMessage
- id='change_url.close'
- defaultMessage='Close'
- />
- </button>
- <button
- onClick={this.doSubmit}
- type='submit'
- className='btn btn-primary'
- tabIndex='2'
- >
- {this.props.submitButtonText}
- </button>
- </Modal.Footer>
- </form>
- </Modal>
- );
- }
-}
-
-ChangeUrlModal.defaultProps = {
- show: false,
- title: 'Change URL',
- desciption: '',
- urlLabel: 'URL',
- submitButtonText: 'Save',
- currentURL: '',
- serverError: ''
-};
-
-ChangeUrlModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- title: React.PropTypes.string,
- description: React.PropTypes.string,
- urlLabel: React.PropTypes.string,
- submitButtonText: React.PropTypes.string,
- currentURL: React.PropTypes.string,
- serverError: React.PropTypes.string,
- onModalSubmit: React.PropTypes.func.isRequired,
- onModalDismissed: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
deleted file mode 100644
index 882c575f0..000000000
--- a/web/react/components/channel_header.jsx
+++ /dev/null
@@ -1,517 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import NavbarSearchBox from './search_bar.jsx';
-import MessageWrapper from './message_wrapper.jsx';
-import PopoverListMembers from './popover_list_members.jsx';
-import EditChannelHeaderModal from './edit_channel_header_modal.jsx';
-import EditChannelPurposeModal from './edit_channel_purpose_modal.jsx';
-import ChannelInfoModal from './channel_info_modal.jsx';
-import ChannelInviteModal from './channel_invite_modal.jsx';
-import ChannelMembersModal from './channel_members_modal.jsx';
-import ChannelNotificationsModal from './channel_notifications_modal.jsx';
-import DeleteChannelModal from './delete_channel_modal.jsx';
-import RenameChannelModal from './rename_channel_modal.jsx';
-import ToggleModalButton from './toggle_modal_button.jsx';
-
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import SearchStore from '../stores/search_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as Utils from '../utils/utils.jsx';
-import * as TextFormatting from '../utils/text_formatting.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const ActionTypes = Constants.ActionTypes;
-const Popover = ReactBootstrap.Popover;
-const OverlayTrigger = ReactBootstrap.OverlayTrigger;
-const Tooltip = ReactBootstrap.Tooltip;
-
-export default class ChannelHeader extends React.Component {
- constructor(props) {
- super(props);
-
- this.onListenerChange = this.onListenerChange.bind(this);
- this.handleLeave = this.handleLeave.bind(this);
- this.searchMentions = this.searchMentions.bind(this);
- this.showRenameChannelModal = this.showRenameChannelModal.bind(this);
- this.hideRenameChannelModal = this.hideRenameChannelModal.bind(this);
-
- const state = this.getStateFromStores();
- state.showEditChannelPurposeModal = false;
- state.showMembersModal = false;
- state.showRenameChannelModal = false;
- this.state = state;
- }
- getStateFromStores() {
- const extraInfo = ChannelStore.getCurrentExtraInfo();
-
- return {
- channel: ChannelStore.getCurrent(),
- memberChannel: ChannelStore.getCurrentMember(),
- users: extraInfo.members,
- userCount: extraInfo.member_count,
- searchVisible: SearchStore.getSearchResults() !== null,
- currentUser: UserStore.getCurrentUser()
- };
- }
- validState() {
- if (!this.state.channel ||
- !this.state.memberChannel ||
- !this.state.users ||
- !this.state.userCount ||
- !this.state.currentUser) {
- return false;
- }
- return true;
- }
- componentDidMount() {
- ChannelStore.addChangeListener(this.onListenerChange);
- ChannelStore.addExtraInfoChangeListener(this.onListenerChange);
- SearchStore.addSearchChangeListener(this.onListenerChange);
- PreferenceStore.addChangeListener(this.onListenerChange);
- UserStore.addChangeListener(this.onListenerChange);
- }
- componentWillUnmount() {
- ChannelStore.removeChangeListener(this.onListenerChange);
- ChannelStore.removeExtraInfoChangeListener(this.onListenerChange);
- SearchStore.removeSearchChangeListener(this.onListenerChange);
- PreferenceStore.removeChangeListener(this.onListenerChange);
- UserStore.removeChangeListener(this.onListenerChange);
- }
- onListenerChange() {
- const newState = this.getStateFromStores();
- if (!Utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- $('.channel-header__info .description').popover({placement: 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}});
- }
- handleLeave() {
- Client.leaveChannel(this.state.channel.id,
- () => {
- AppDispatcher.handleViewAction({
- type: ActionTypes.LEAVE_CHANNEL,
- id: this.state.channel.id
- });
-
- const townsquare = ChannelStore.getByName('town-square');
- Utils.switchChannel(townsquare);
- },
- (err) => {
- AsyncClient.dispatchError(err, 'handleLeave');
- }
- );
- }
- searchMentions(e) {
- e.preventDefault();
-
- const user = this.state.currentUser;
-
- let terms = '';
- if (user.notify_props && user.notify_props.mention_keys) {
- const termKeys = UserStore.getMentionKeys(user.id);
-
- if (user.notify_props.all === 'true' && termKeys.indexOf('@all') !== -1) {
- termKeys.splice(termKeys.indexOf('@all'), 1);
- }
-
- if (user.notify_props.channel === 'true' && termKeys.indexOf('@channel') !== -1) {
- termKeys.splice(termKeys.indexOf('@channel'), 1);
- }
- terms = termKeys.join(' ');
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH_TERM,
- term: terms,
- do_search: true,
- is_mention_search: true
- });
- }
- showRenameChannelModal(e) {
- e.preventDefault();
-
- this.setState({
- showRenameChannelModal: true
- });
- }
- hideRenameChannelModal() {
- this.setState({
- showRenameChannelModal: false
- });
- }
- render() {
- if (!this.validState()) {
- return null;
- }
-
- const channel = this.state.channel;
- const recentMentionsTooltip = (
- <Tooltip id='recentMentionsTooltip'>
- <FormattedMessage
- id='channel_header.recentMentions'
- defaultMessage='Recent Mentions'
- />
- </Tooltip>
- );
- const popoverContent = (
- <Popover
- id='hader-popover'
- bStyle='info'
- bSize='large'
- placement='bottom'
- className='description'
- onMouseOver={() => this.refs.headerOverlay.show()}
- onMouseOut={() => this.refs.headerOverlay.hide()}
- >
- <MessageWrapper
- message={channel.header}
- />
- </Popover>
- );
- let channelTitle = channel.display_name;
- const currentId = this.state.currentUser.id;
- const isAdmin = Utils.isAdmin(this.state.memberChannel.roles) || Utils.isAdmin(this.state.currentUser.roles);
- const isDirect = (this.state.channel.type === 'D');
-
- if (isDirect) {
- if (this.state.users.length > 1) {
- let contact;
- if (this.state.users[0].id === currentId) {
- contact = this.state.users[1];
- } else {
- contact = this.state.users[0];
- }
- channelTitle = Utils.displayUsername(contact.id);
- }
- }
-
- let channelTerm = (
- <FormattedMessage
- id='channel_header.channel'
- defaultMessage='Channel'
- />
- );
- if (channel.type === Constants.PRIVATE_CHANNEL) {
- channelTerm = (
- <FormattedMessage
- id='channel_header.group'
- defaultMessage='Group'
- />
- );
- }
-
- let popoverListMembers;
- if (!isDirect) {
- popoverListMembers = (
- <PopoverListMembers
- members={this.state.users}
- memberCount={this.state.userCount}
- channelId={channel.id}
- />
- );
- }
-
- const dropdownContents = [];
- if (isDirect) {
- dropdownContents.push(
- <li
- key='edit_header_direct'
- role='presentation'
- >
- <ToggleModalButton
- role='menuitem'
- dialogType={EditChannelHeaderModal}
- dialogProps={{channel}}
- >
- <FormattedMessage
- id='channel_header.channelHeader'
- defaultMessage='Set Channel Header...'
- />
- </ToggleModalButton>
- </li>
- );
- } else {
- dropdownContents.push(
- <li
- key='view_info'
- role='presentation'
- >
- <ToggleModalButton
- role='menuitem'
- dialogType={ChannelInfoModal}
- dialogProps={{channel}}
- >
- <FormattedMessage
- id='channel_header.viewInfo'
- defaultMessage='View Info'
- />
- </ToggleModalButton>
- </li>
- );
-
- if (!ChannelStore.isDefault(channel)) {
- dropdownContents.push(
- <li
- key='add_members'
- role='presentation'
- >
- <ToggleModalButton
- role='menuitem'
- dialogType={ChannelInviteModal}
- dialogProps={{channel, currentUser: this.state.currentUser}}
- >
- <FormattedMessage
- id='chanel_header.addMembers'
- defaultMessage='Add Members'
- />
- </ToggleModalButton>
- </li>
- );
-
- if (isAdmin) {
- dropdownContents.push(
- <li
- key='manage_members'
- role='presentation'
- >
- <a
- role='menuitem'
- href='#'
- onClick={() => this.setState({showMembersModal: true})}
- >
- <FormattedMessage
- id='channel_header.manageMembers'
- defaultMessage='Manage Members'
- />
- </a>
- </li>
- );
- }
- }
-
- dropdownContents.push(
- <li
- key='set_channel_header'
- role='presentation'
- >
- <ToggleModalButton
- role='menuitem'
- dialogType={EditChannelHeaderModal}
- dialogProps={{channel}}
- >
- <FormattedMessage
- id='channel_header.setHeader'
- defaultMessage='Set {term} Header...'
- values={{
- term: (channelTerm)
- }}
- />
- </ToggleModalButton>
- </li>
- );
- dropdownContents.push(
- <li
- key='set_channel_purpose'
- role='presentation'
- >
- <a
- role='menuitem'
- href='#'
- onClick={() => this.setState({showEditChannelPurposeModal: true})}
- >
- <FormattedMessage
- id='channel_header.setPurpose'
- defaultMessage='Set {term} Purpose...'
- values={{
- term: (channelTerm)
- }}
- />
- </a>
- </li>
- );
- dropdownContents.push(
- <li
- key='notification_preferences'
- role='presentation'
- >
- <ToggleModalButton
- role='menuitem'
- dialogType={ChannelNotificationsModal}
- dialogProps={{
- channel,
- channelMember: this.state.memberChannel,
- currentUser: this.state.currentUser
- }}
- >
- <FormattedMessage
- id='channel_header.notificationPreferences'
- defaultMessage='Notification Preferences'
- />
- </ToggleModalButton>
- </li>
- );
-
- if (isAdmin) {
- dropdownContents.push(
- <li
- key='rename_channel'
- role='presentation'
- >
- <a
- role='menuitem'
- href='#'
- onClick={this.showRenameChannelModal}
- >
- <FormattedMessage
- id='channel_header.rename'
- defaultMessage='Rename {term}...'
- values={{
- term: (channelTerm)
- }}
- />
- </a>
- </li>
- );
-
- if (!ChannelStore.isDefault(channel)) {
- dropdownContents.push(
- <li
- key='delete_channel'
- role='presentation'
- >
- <ToggleModalButton
- role='menuitem'
- dialogType={DeleteChannelModal}
- dialogProps={{channel}}
- >
- <FormattedMessage
- id='channel_header.delete'
- defaultMessage='Delete {term}...'
- values={{
- term: (channelTerm)
- }}
- />
- </ToggleModalButton>
- </li>
- );
- }
- }
-
- if (!ChannelStore.isDefault(channel)) {
- dropdownContents.push(
- <li
- key='leave_channel'
- role='presentation'
- >
- <a
- role='menuitem'
- href='#'
- onClick={this.handleLeave}
- >
- <FormattedMessage
- id='channel_header.leave'
- defaultMessage='Leave {term}'
- values={{
- term: (channelTerm)
- }}
- />
- </a>
- </li>
- );
- }
- }
-
- return (
- <div>
- <table className='channel-header alt'>
- <tbody>
- <tr>
- <th>
- <div className='channel-header__info'>
- <div className='dropdown'>
- <a
- href='#'
- className='dropdown-toggle theme'
- type='button'
- id='channel_header_dropdown'
- data-toggle='dropdown'
- aria-expanded='true'
- >
- <strong className='heading'>{channelTitle} </strong>
- <span className='glyphicon glyphicon-chevron-down header-dropdown__icon'/>
- </a>
- <ul
- className='dropdown-menu'
- role='menu'
- aria-labelledby='channel_header_dropdown'
- >
- {dropdownContents}
- </ul>
- </div>
- <OverlayTrigger
- trigger={'click'}
- placement='bottom'
- overlay={popoverContent}
- ref='headerOverlay'
- >
- <div
- onClick={TextFormatting.handleClick}
- className='description'
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(channel.header, {singleline: true, mentionHighlight: false})}}
- />
- </OverlayTrigger>
- </div>
- </th>
- <th>
- {popoverListMembers}
- </th>
- <th className='search-bar__container'><NavbarSearchBox/></th>
- <th>
- <div className='dropdown channel-header__links'>
- <OverlayTrigger
- delayShow={Constants.OVERLAY_TIME_DELAY}
- placement='bottom'
- overlay={recentMentionsTooltip}
- >
- <a
- href='#'
- type='button'
- onClick={this.searchMentions}
- >
- {'@'}
- </a>
- </OverlayTrigger>
- </div>
- </th>
- </tr>
- </tbody>
- </table>
- <EditChannelPurposeModal
- show={this.state.showEditChannelPurposeModal}
- onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})}
- channel={channel}
- />
- <ChannelMembersModal
- show={this.state.showMembersModal}
- onModalDismissed={() => this.setState({showMembersModal: false})}
- channel={channel}
- />
- <RenameChannelModal
- show={this.state.showRenameChannelModal}
- onHide={this.hideRenameChannelModal}
- channel={channel}
- />
- </div>
- );
- }
-}
-
-ChannelHeader.propTypes = {
-};
diff --git a/web/react/components/channel_info_modal.jsx b/web/react/components/channel_info_modal.jsx
deleted file mode 100644
index 83f5aba65..000000000
--- a/web/react/components/channel_info_modal.jsx
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-const holders = defineMessages({
- notFound: {
- id: 'channel_info.notFound',
- defaultMessage: 'No Channel Found'
- }
-});
-
-class ChannelInfoModal extends React.Component {
- render() {
- const {formatMessage} = this.props.intl;
- let channel = this.props.channel;
- if (!channel) {
- channel = {
- display_name: formatMessage(holders.notFound),
- name: formatMessage(holders.notFound),
- purpose: formatMessage(holders.notFound),
- id: formatMessage(holders.notFound)
- };
- }
-
- const channelURL = Utils.getShortenedTeamURL() + channel.name;
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.props.onHide}
- >
- <Modal.Header closeButtton={true}>
- {channel.display_name}
- </Modal.Header>
- <Modal.Body ref='modalBody'>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='channel_info.name'
- defaultMessage='Channel Name:'
- />
- </div>
- <div className='col-sm-9'>{channel.display_name}</div>
- </div>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='channel_info.url'
- defaultMessage='Channel URL:'
- />
- </div>
- <div className='col-sm-9'>{channelURL}</div>
- </div>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='channel_info.id'
- defaultMessage='Channel ID:'
- />
- </div>
- <div className='col-sm-9'>{channel.id}</div>
- </div>
- <div className='row'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='channel_info.purpose'
- defaultMessage='Channel Purpose:'
- />
- </div>
- <div className='col-sm-9'>{channel.purpose}</div>
- </div>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onHide}
- >
- <FormattedMessage
- id='channel_info.close'
- defaultMessage='Close'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-ChannelInfoModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired
-};
-
-export default injectIntl(ChannelInfoModal); \ No newline at end of file
diff --git a/web/react/components/channel_invite_modal.jsx b/web/react/components/channel_invite_modal.jsx
deleted file mode 100644
index 4157812a9..000000000
--- a/web/react/components/channel_invite_modal.jsx
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import FilteredUserList from './filtered_user_list.jsx';
-import LoadingScreen from './loading_screen.jsx';
-
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-export default class ChannelInviteModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.onListenerChange = this.onListenerChange.bind(this);
- this.handleInvite = this.handleInvite.bind(this);
- this.getStateFromStores = this.getStateFromStores.bind(this);
- this.createInviteButton = this.createInviteButton.bind(this);
-
- this.state = this.getStateFromStores();
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (!this.props.show && !nextProps.show) {
- return false;
- }
-
- if (!Utils.areObjectsEqual(this.props, nextProps)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(this.state, nextState)) {
- return true;
- }
-
- return false;
- }
- getStateFromStores() {
- const users = UserStore.getActiveOnlyProfiles();
-
- if ($.isEmptyObject(users)) {
- return {
- loading: true
- };
- }
-
- // make sure we have all members of this channel before rendering
- const extraInfo = ChannelStore.getCurrentExtraInfo();
- if (extraInfo.member_count !== extraInfo.members.length) {
- AsyncClient.getChannelExtraInfo(this.props.channel.id, -1);
-
- return {
- loading: true
- };
- }
-
- const currentUser = UserStore.getCurrentUser();
- if (!currentUser) {
- return {
- loading: true
- };
- }
-
- const currentMember = ChannelStore.getCurrentMember();
- if (!currentMember) {
- return {
- loading: true
- };
- }
-
- const memberIds = extraInfo.members.map((user) => user.id);
-
- var nonmembers = [];
- for (var id in users) {
- if (memberIds.indexOf(id) === -1) {
- nonmembers.push(users[id]);
- }
- }
-
- nonmembers.sort((a, b) => {
- return a.username.localeCompare(b.username);
- });
-
- return {
- nonmembers,
- loading: false,
- currentUser,
- currentMember
- };
- }
- componentWillReceiveProps(nextProps) {
- if (!this.props.show && nextProps.show) {
- ChannelStore.addExtraInfoChangeListener(this.onListenerChange);
- ChannelStore.addChangeListener(this.onListenerChange);
- UserStore.addChangeListener(this.onListenerChange);
- this.onListenerChange();
- } else if (this.props.show && !nextProps.show) {
- ChannelStore.removeExtraInfoChangeListener(this.onListenerChange);
- ChannelStore.removeChangeListener(this.onListenerChange);
- UserStore.removeChangeListener(this.onListenerChange);
- }
- }
- componentWillUnmount() {
- ChannelStore.removeExtraInfoChangeListener(this.onListenerChange);
- ChannelStore.removeChangeListener(this.onListenerChange);
- UserStore.removeChangeListener(this.onListenerChange);
- }
- onListenerChange() {
- var newState = this.getStateFromStores();
- if (!Utils.areObjectsEqual(this.state, newState)) {
- this.setState(newState);
- }
- }
- handleInvite(user) {
- const data = {
- user_id: user.id
- };
-
- Client.addChannelMember(
- this.props.channel.id,
- data,
- () => {
- this.setState({inviteError: null});
- AsyncClient.getChannelExtraInfo();
- },
- (err) => {
- this.setState({inviteError: err.message});
- }
- );
- }
- createInviteButton({user}) {
- return (
- <a
- onClick={this.handleInvite.bind(this, user)}
- className='btn btn-sm btn-primary'
- >
- <i className='glyphicon glyphicon-envelope'/>
- <FormattedMessage
- id='channel_invite.add'
- defaultMessage=' Add'
- />
- </a>
- );
- }
- render() {
- var inviteError = null;
- if (this.state.inviteError) {
- inviteError = (<label className='has-error control-label'>{this.state.inviteError}</label>);
- }
-
- var content;
- if (this.state.loading) {
- content = (<LoadingScreen/>);
- } else {
- let maxHeight = 1000;
- if (Utils.windowHeight() <= 1200) {
- maxHeight = Utils.windowHeight() - 300;
- }
- content = (
- <FilteredUserList
- style={{maxHeight}}
- users={this.state.nonmembers}
- actions={[this.createInviteButton]}
- />
- );
- }
-
- return (
- <Modal
- dialogClassName='more-modal'
- show={this.props.show}
- onHide={this.props.onHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='channel_invite.addNewMembers'
- defaultMessage='Add New Members to '
- />
- <span className='name'>{this.props.channel.display_name}</span>
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- {inviteError}
- {content}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onHide}
- >
- <FormattedMessage
- id='channel_invite.close'
- defaultMessage='Close'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-ChannelInviteModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/channel_members_modal.jsx b/web/react/components/channel_members_modal.jsx
deleted file mode 100644
index dc8e3baaf..000000000
--- a/web/react/components/channel_members_modal.jsx
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import FilteredUserList from './filtered_user_list.jsx';
-import LoadingScreen from './loading_screen.jsx';
-import ChannelInviteModal from './channel_invite_modal.jsx';
-
-import UserStore from '../stores/user_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-export default class ChannelMembersModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.getStateFromStores = this.getStateFromStores.bind(this);
- this.onChange = this.onChange.bind(this);
- this.handleRemove = this.handleRemove.bind(this);
-
- this.createRemoveMemberButton = this.createRemoveMemberButton.bind(this);
-
- // the rest of the state gets populated when the modal is shown
- this.state = {
- showInviteModal: false
- };
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(this.props, nextProps)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(this.state, nextState)) {
- return true;
- }
-
- return false;
- }
- getStateFromStores() {
- const extraInfo = ChannelStore.getCurrentExtraInfo();
- const profiles = UserStore.getActiveOnlyProfiles();
-
- if (extraInfo.member_count !== extraInfo.members.length) {
- AsyncClient.getChannelExtraInfo(this.props.channel.id, -1);
-
- return {
- loading: true
- };
- }
-
- const memberList = extraInfo.members.map((member) => {
- return profiles[member.id];
- });
-
- function compareByUsername(a, b) {
- if (a.username < b.username) {
- return -1;
- } else if (a.username > b.username) {
- return 1;
- }
-
- return 0;
- }
-
- memberList.sort(compareByUsername);
-
- return {
- memberList,
- loading: false
- };
- }
- componentWillReceiveProps(nextProps) {
- if (!this.props.show && nextProps.show) {
- ChannelStore.addExtraInfoChangeListener(this.onChange);
- ChannelStore.addChangeListener(this.onChange);
-
- this.onChange();
- } else if (this.props.show && !nextProps.show) {
- ChannelStore.removeExtraInfoChangeListener(this.onChange);
- ChannelStore.removeChangeListener(this.onChange);
- }
- }
- onChange() {
- const newState = this.getStateFromStores();
- if (!Utils.areObjectsEqual(this.state, newState)) {
- this.setState(newState);
- }
- }
- handleRemove(user) {
- const userId = user.id;
-
- const data = {};
- data.user_id = userId;
-
- Client.removeChannelMember(
- ChannelStore.getCurrentId(),
- data,
- () => {
- const memberList = this.state.memberList.slice();
- for (let i = 0; i < memberList.length; i++) {
- if (userId === memberList[i].id) {
- memberList.splice(i, 1);
- break;
- }
- }
-
- this.setState({memberList});
- AsyncClient.getChannelExtraInfo();
- },
- (err) => {
- this.setState({inviteError: err.message});
- }
- );
- }
- createRemoveMemberButton({user}) {
- if (user.id === UserStore.getCurrentId()) {
- return null;
- }
-
- return (
- <button
- type='button'
- className='btn btn-primary btn-message'
- onClick={this.handleRemove.bind(this, user)}
- >
- <FormattedMessage
- id='channel_members_modal.remove'
- defaultMessage='Remove'
- />
- </button>
- );
- }
- render() {
- let content;
- if (this.state.loading) {
- content = (<LoadingScreen/>);
- } else {
- let maxHeight = 1000;
- if (Utils.windowHeight() <= 1200) {
- maxHeight = Utils.windowHeight() - 300;
- }
-
- content = (
- <FilteredUserList
- style={{maxHeight}}
- users={this.state.memberList}
- actions={[this.createRemoveMemberButton]}
- />
- );
- }
-
- return (
- <div>
- <Modal
- dialogClassName='more-modal'
- show={this.props.show}
- onHide={this.props.onModalDismissed}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <span className='name'>{this.props.channel.display_name}</span>
- <FormattedMessage
- id='channel_memebers_modal.members'
- defaultMessage=' Members'
- />
- </Modal.Title>
- <a
- className='btn btn-md btn-primary'
- href='#'
- onClick={() => {
- this.setState({showInviteModal: true});
- this.props.onModalDismissed();
- }}
- >
- <FormattedMessage
- id='channel_members_modal.addNew'
- defaultMessage=' Add New Members'
- />
- </a>
- </Modal.Header>
- <Modal.Body
- ref='modalBody'
- >
- {content}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onModalDismissed}
- >
- <FormattedMessage
- id='channel_members_modal.close'
- defaultMessage='Close'
- />
- </button>
- </Modal.Footer>
- </Modal>
- <ChannelInviteModal
- show={this.state.showInviteModal}
- onHide={() => this.setState({showInviteModal: false})}
- channel={this.props.channel}
- />
- </div>
- );
- }
-}
-
-ChannelMembersModal.defaultProps = {
- show: false
-};
-
-ChannelMembersModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onModalDismissed: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/channel_notifications_modal.jsx b/web/react/components/channel_notifications_modal.jsx
deleted file mode 100644
index acefaf024..000000000
--- a/web/react/components/channel_notifications_modal.jsx
+++ /dev/null
@@ -1,415 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-var Modal = ReactBootstrap.Modal;
-import SettingItemMin from './setting_item_min.jsx';
-import SettingItemMax from './setting_item_max.jsx';
-
-import * as Client from '../utils/client.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class ChannelNotificationsModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateSection = this.updateSection.bind(this);
-
- this.handleSubmitNotifyLevel = this.handleSubmitNotifyLevel.bind(this);
- this.handleUpdateNotifyLevel = this.handleUpdateNotifyLevel.bind(this);
- this.createNotifyLevelSection = this.createNotifyLevelSection.bind(this);
-
- this.handleSubmitMarkUnreadLevel = this.handleSubmitMarkUnreadLevel.bind(this);
- this.handleUpdateMarkUnreadLevel = this.handleUpdateMarkUnreadLevel.bind(this);
- this.createMarkUnreadLevelSection = this.createMarkUnreadLevelSection.bind(this);
-
- this.state = {
- activeSection: '',
- notifyLevel: '',
- unreadLevel: ''
- };
- }
- updateSection(section) {
- this.setState({activeSection: section});
- }
- componentWillReceiveProps(nextProps) {
- if (!this.props.show && nextProps.show) {
- this.setState({
- notifyLevel: nextProps.channelMember.notify_props.desktop,
- unreadLevel: nextProps.channelMember.notify_props.mark_unread
- });
- }
- }
- handleSubmitNotifyLevel() {
- var channelId = this.props.channel.id;
- var notifyLevel = this.state.notifyLevel;
-
- if (this.props.channelMember.notify_props.desktop === notifyLevel) {
- this.updateSection('');
- return;
- }
-
- var data = {};
- data.channel_id = channelId;
- data.user_id = this.props.currentUser.id;
- data.desktop = notifyLevel;
-
- //TODO: This should be moved to event_helpers
- Client.updateNotifyProps(data,
- () => {
- // YUCK
- var member = ChannelStore.getMember(channelId);
- member.notify_props.desktop = notifyLevel;
- ChannelStore.setChannelMember(member);
- this.updateSection('');
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- handleUpdateNotifyLevel(notifyLevel) {
- this.setState({notifyLevel});
- }
- createNotifyLevelSection(serverError) {
- // Get glabal user setting for notifications
- const globalNotifyLevel = this.props.currentUser.notify_props.desktop;
- let globalNotifyLevelName;
- if (globalNotifyLevel === 'all') {
- globalNotifyLevelName = (
- <FormattedMessage
- id='channel_notifications.allActivity'
- defaultMessage='For all activity'
- />
- );
- } else if (globalNotifyLevel === 'mention') {
- globalNotifyLevelName = (
- <FormattedMessage
- id='channel_notifications.onlyMentions'
- defaultMessage='Only for mentions'
- />
- );
- } else {
- globalNotifyLevelName = (
- <FormattedMessage
- id='channel_notifications.never'
- defaultMessage='Never'
- />
- );
- }
-
- const sendDesktop = (
- <FormattedMessage
- id='channel_notifications.sendDesktop'
- defaultMessage='Send desktop notifications'
- />
- );
-
- const notificationLevel = this.state.notifyLevel;
-
- if (this.state.activeSection === 'desktop') {
- const notifyActive = [false, false, false, false];
- if (notificationLevel === 'default') {
- notifyActive[0] = true;
- } else if (notificationLevel === 'all') {
- notifyActive[1] = true;
- } else if (notificationLevel === 'mention') {
- notifyActive[2] = true;
- } else {
- notifyActive[3] = true;
- }
-
- var inputs = [];
-
- inputs.push(
- <div key='channel-notification-level-radio'>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={notifyActive[0]}
- onChange={this.handleUpdateNotifyLevel.bind(this, 'default')}
- />
- <FormattedMessage
- id='channel_notifications.globalDefault'
- defaultMessage='Global default ({notifyLevel})'
- values={{
- notifyLevel: (globalNotifyLevelName)
- }}
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={notifyActive[1]}
- onChange={this.handleUpdateNotifyLevel.bind(this, 'all')}
- />
- <FormattedMessage id='channel_notifications.allActivity'/>
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={notifyActive[2]}
- onChange={this.handleUpdateNotifyLevel.bind(this, 'mention')}
- />
- <FormattedMessage id='channel_notifications.onlyMentions'/>
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={notifyActive[3]}
- onChange={this.handleUpdateNotifyLevel.bind(this, 'none')}
- />
- <FormattedMessage id='channel_notifications.never'/>
- </label>
- </div>
- </div>
- );
-
- const handleUpdateSection = function updateSection(e) {
- this.updateSection('');
- this.onListenerChange();
- e.preventDefault();
- }.bind(this);
-
- const extraInfo = (
- <span>
- <FormattedMessage
- id='channel_notifications.override'
- defaultMessage='Selecting an option other than "Default" will override the global notification settings. Desktop notifications are available on Firefox, Safari, and Chrome.'
- />
- </span>
- );
-
- return (
- <SettingItemMax
- title={sendDesktop}
- inputs={inputs}
- submit={this.handleSubmitNotifyLevel}
- server_error={serverError}
- updateSection={handleUpdateSection}
- extraInfo={extraInfo}
- />
- );
- }
-
- var describe;
- if (notificationLevel === 'default') {
- describe = (
- <FormattedMessage
- id='channel_notifications.globalDefault'
- values={{
- notifyLevel: (globalNotifyLevelName)
- }}
- />
- );
- } else if (notificationLevel === 'mention') {
- describe = (<FormattedMessage id='channel_notifications.onlyMentions'/>);
- } else if (notificationLevel === 'all') {
- describe = (<FormattedMessage id='channel_notifications.allActivity'/>);
- } else {
- describe = (<FormattedMessage id='channel_notifications.never'/>);
- }
-
- return (
- <SettingItemMin
- title={sendDesktop}
- describe={describe}
- updateSection={() => {
- this.updateSection('desktop');
- }}
- />
- );
- }
-
- handleSubmitMarkUnreadLevel() {
- const channelId = this.props.channel.id;
- const markUnreadLevel = this.state.unreadLevel;
-
- if (this.props.channelMember.notify_props.mark_unread === markUnreadLevel) {
- this.updateSection('');
- return;
- }
-
- const data = {
- channel_id: channelId,
- user_id: this.props.currentUser.id,
- mark_unread: markUnreadLevel
- };
-
- //TODO: This should be fixed, moved to event_helpers
- Client.updateNotifyProps(data,
- () => {
- // Yuck...
- var member = ChannelStore.getMember(channelId);
- member.notify_props.mark_unread = markUnreadLevel;
- ChannelStore.setChannelMember(member);
- this.updateSection('');
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
-
- handleUpdateMarkUnreadLevel(unreadLevel) {
- this.setState({unreadLevel});
- }
-
- createMarkUnreadLevelSection(serverError) {
- let content;
-
- const markUnread = (
- <FormattedMessage
- id='channel_notifications.markUnread'
- defaultMessage='Mark Channel Unread'
- />
- );
- if (this.state.activeSection === 'markUnreadLevel') {
- const inputs = [(
- <div key='channel-notification-unread-radio'>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={this.state.unreadLevel === 'all'}
- onChange={this.handleUpdateMarkUnreadLevel.bind(this, 'all')}
- />
- <FormattedMessage
- id='channel_notifications.allUnread'
- defaultMessage='For all unread messages'
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={this.state.unreadLevel === 'mention'}
- onChange={this.handleUpdateMarkUnreadLevel.bind(this, 'mention')}
- />
- <FormattedMessage id='channel_notifications.onlyMentions'/>
- </label>
- <br/>
- </div>
- </div>
- )];
-
- const handleUpdateSection = function handleUpdateSection(e) {
- this.updateSection('');
- this.onListenerChange();
- e.preventDefault();
- }.bind(this);
-
- const extraInfo = (
- <span>
- <FormattedMessage
- id='channel_notifications.unreadInfo'
- defaultMessage='The channel name is bolded in the sidebar when there are unread messages. Selecting "Only for mentions" will bold the channel only when you are mentioned.'
- />
- </span>
- );
-
- content = (
- <SettingItemMax
- title={markUnread}
- inputs={inputs}
- submit={this.handleSubmitMarkUnreadLevel}
- server_error={serverError}
- updateSection={handleUpdateSection}
- extraInfo={extraInfo}
- />
- );
- } else {
- let describe;
-
- if (!this.state.unreadLevel || this.state.unreadLevel === 'all') {
- describe = (
- <FormattedMessage
- id='channel_notifications.allUnread'
- defaultMessage='For all unread messages'
- />
- );
- } else {
- describe = (<FormattedMessage id='channel_notifications.onlyMentions'/>);
- }
-
- const handleUpdateSection = function handleUpdateSection(e) {
- this.updateSection('markUnreadLevel');
- e.preventDefault();
- }.bind(this);
-
- content = (
- <SettingItemMin
- title={markUnread}
- describe={describe}
- updateSection={handleUpdateSection}
- />
- );
- }
-
- return content;
- }
-
- render() {
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- return (
- <Modal
- show={this.props.show}
- dialogClassName='settings-modal'
- onHide={this.props.onHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='channel_notifications.preferences'
- defaultMessage='Notification Preferences for '
- />
- <span className='name'>{this.props.channel.display_name}</span>
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <div className='settings-table'>
- <div className='settings-content'>
- <div
- ref='wrapper'
- className='user-settings'
- >
- <br/>
- <div className='divider-dark first'/>
- {this.createNotifyLevelSection(serverError)}
- <div className='divider-light'/>
- {this.createMarkUnreadLevelSection(serverError)}
- <div className='divider-dark'/>
- </div>
- </div>
- </div>
- {serverError}
- </Modal.Body>
- </Modal>
- );
- }
-}
-
-ChannelNotificationsModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired,
- channelMember: React.PropTypes.object.isRequired,
- currentUser: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/channel_view.jsx b/web/react/components/channel_view.jsx
deleted file mode 100644
index 76744d6d7..000000000
--- a/web/react/components/channel_view.jsx
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import CenterPanel from '../components/center_panel.jsx';
-
-export default class ChannelView extends React.Component {
- render() {
- return (
- <CenterPanel/>
- );
- }
-}
-ChannelView.defaultProps = {
-};
-
-ChannelView.propTypes = {
- params: React.PropTypes.object
-};
diff --git a/web/react/components/claim/claim_account.jsx b/web/react/components/claim/claim_account.jsx
deleted file mode 100644
index 42fd8dafa..000000000
--- a/web/react/components/claim/claim_account.jsx
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import EmailToSSO from './email_to_sso.jsx';
-import SSOToEmail from './sso_to_email.jsx';
-import TeamStore from '../../stores/team_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class ClaimAccount extends React.Component {
- constructor(props) {
- super(props);
-
- this.onTeamChange = this.onTeamChange.bind(this);
- this.updateStateFromStores = this.updateStateFromStores.bind(this);
-
- this.state = {};
- }
- componentWillMount() {
- this.setState({
- email: this.props.location.query.email,
- newType: this.props.location.query.new_type,
- oldType: this.props.location.query.old_type,
- teamName: this.props.params.team,
- teamDisplayName: ''
- });
- this.updateStateFromStores();
- }
- componentDidMount() {
- TeamStore.addChangeListener(this.onTeamChange);
- }
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.onTeamChange);
- }
- updateStateFromStores() {
- const team = TeamStore.getByName(this.state.teamName);
- let displayName = '';
- if (team) {
- displayName = team.displayName;
- }
- this.setState({
- teamDisplayName: displayName
- });
- }
- onTeamChange() {
- this.updateStateFromStores();
- }
- render() {
- if (this.state.teamDisplayName === '') {
- return (<div/>);
- }
- let content;
- if (this.state.email === '') {
- content = (
- <p>
- <FormattedMessage
- id='claim.account.noEmail'
- defaultMessage='No email specified'
- />
- </p>
- );
- } else if (this.state.oldType === '' && this.state.newType !== '') {
- content = (
- <EmailToSSO
- email={this.state.email}
- type={this.state.newType}
- teamName={this.state.teamName}
- teamDisplayName={this.state.teamDisplayName}
- />
- );
- } else {
- content = (
- <SSOToEmail
- email={this.state.email}
- currentType={this.state.oldType}
- teamName={this.state.teamName}
- teamDisplayName={this.state.teamDisplayName}
- />
- );
- }
-
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <div id='claim'>
- {content}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-ClaimAccount.defaultProps = {
-};
-ClaimAccount.propTypes = {
- params: React.PropTypes.object.isRequired,
- location: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/claim/email_to_sso.jsx b/web/react/components/claim/email_to_sso.jsx
deleted file mode 100644
index c3eea9495..000000000
--- a/web/react/components/claim/email_to_sso.jsx
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../utils/utils.jsx';
-import * as Client from '../../utils/client.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- pwdError: {
- id: 'claim.email_to_sso.pwdError',
- defaultMessage: 'Please enter your password.'
- },
- pwd: {
- id: 'claim.email_to_sso.pwd',
- defaultMessage: 'Password'
- }
-});
-
-class EmailToSSO extends React.Component {
- constructor(props) {
- super(props);
-
- this.submit = this.submit.bind(this);
-
- this.state = {};
- }
- submit(e) {
- e.preventDefault();
- var state = {};
-
- var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!password) {
- state.error = this.props.intl.formatMessage(holders.pwdError);
- this.setState(state);
- return;
- }
-
- state.error = null;
- this.setState(state);
-
- var postData = {};
- postData.password = password;
- postData.email = this.props.email;
- postData.team_name = this.props.teamName;
- postData.service = this.props.type;
-
- Client.switchToSSO(postData,
- (data) => {
- if (data.follow_link) {
- window.location.href = data.follow_link;
- }
- },
- (error) => {
- this.setState({error});
- }
- );
- }
- render() {
- var error = null;
- if (this.state.error) {
- error = <div className='form-group has-error'><label className='control-label'>{this.state.error}</label></div>;
- }
-
- var formClass = 'form-group';
- if (error) {
- formClass += ' has-error';
- }
-
- const uiType = Utils.toTitleCase(this.props.type) + ' SSO';
-
- return (
- <div>
- <h3>
- <FormattedMessage
- id='claim.email_to_sso.title'
- defaultMessage='Switch Email/Password Account to {uiType}'
- values={{
- uiType: uiType
- }}
- />
- </h3>
- <form onSubmit={this.submit}>
- <p>
- <FormattedMessage
- id='claim.email_to_sso.ssoType'
- defaultMessage='Upon claiming your account, you will only be able to login with {type} SSO'
- values={{
- type: Utils.toTitleCase(this.props.type)
- }}
- />
- </p>
- <p>
- <FormattedMessage
- id='claim.email_to_sso.ssoNote'
- defaultMessage='You must already have a valid {type} account'
- values={{
- type: Utils.toTitleCase(this.props.type)
- }}
- />
- </p>
- <p>
- <FormattedMessage
- id='claim.email_to_sso.enterPwd'
- defaultMessage='Enter the password for your {team} {site} account'
- values={{
- team: this.props.teamDisplayName,
- site: global.window.mm_config.SiteName
- }}
- />
- </p>
- <div className={formClass}>
- <input
- type='password'
- className='form-control'
- name='password'
- ref='password'
- placeholder={this.props.intl.formatMessage(holders.pwd)}
- spellCheck='false'
- />
- </div>
- {error}
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='claim.email_to_sso.switchTo'
- defaultMessage='Switch account to {uiType}'
- values={{
- uiType: uiType
- }}
- />
- </button>
- </form>
- </div>
- );
- }
-}
-
-EmailToSSO.defaultProps = {
-};
-EmailToSSO.propTypes = {
- intl: intlShape.isRequired,
- type: React.PropTypes.string.isRequired,
- email: React.PropTypes.string.isRequired,
- teamName: React.PropTypes.string.isRequired,
- teamDisplayName: React.PropTypes.string.isRequired
-};
-
-export default injectIntl(EmailToSSO);
diff --git a/web/react/components/claim/sso_to_email.jsx b/web/react/components/claim/sso_to_email.jsx
deleted file mode 100644
index a16efb57b..000000000
--- a/web/react/components/claim/sso_to_email.jsx
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../utils/utils.jsx';
-import * as Client from '../../utils/client.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- enterPwd: {
- id: 'claim.sso_to_email.enterPwd',
- defaultMessage: 'Please enter a password.'
- },
- pwdNotMatch: {
- id: 'claim.sso_to_email.pwdNotMatch',
- defaultMessage: 'Password do not match.'
- },
- newPwd: {
- id: 'claim.sso_to_email.newPwd',
- defaultMessage: 'New Password'
- },
- confirm: {
- id: 'claim.sso_to_email.confirm',
- defaultMessage: 'Confirm Password'
- }
-});
-
-class SSOToEmail extends React.Component {
- constructor(props) {
- super(props);
-
- this.submit = this.submit.bind(this);
-
- this.state = {};
- }
- submit(e) {
- const {formatMessage} = this.props.intl;
- e.preventDefault();
- const state = {};
-
- const password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!password) {
- state.error = formatMessage(holders.enterPwd);
- this.setState(state);
- return;
- }
-
- const confirmPassword = ReactDOM.findDOMNode(this.refs.passwordconfirm).value.trim();
- if (!confirmPassword || password !== confirmPassword) {
- state.error = formatMessage(holders.pwdNotMatch);
- this.setState(state);
- return;
- }
-
- state.error = null;
- this.setState(state);
-
- var postData = {};
- postData.password = password;
- postData.email = this.props.email;
- postData.team_name = this.props.teamName;
-
- Client.switchToEmail(postData,
- (data) => {
- if (data.follow_link) {
- window.location.href = data.follow_link;
- }
- },
- (error) => {
- this.setState({error});
- }
- );
- }
- render() {
- const {formatMessage} = this.props.intl;
- var error = null;
- if (this.state.error) {
- error = <div className='form-group has-error'><label className='control-label'>{this.state.error}</label></div>;
- }
-
- var formClass = 'form-group';
- if (error) {
- formClass += ' has-error';
- }
-
- const uiType = Utils.toTitleCase(this.props.currentType) + ' SSO';
-
- return (
- <div>
- <h3>
- <FormattedMessage
- id='claim.sso_to_email.title'
- defaultMessage='Switch {type} Account to Email'
- values={{
- type: uiType
- }}
- />
- </h3>
- <form onSubmit={this.submit}>
- <p>
- <FormattedMessage
- id='claim.sso_to_email.description'
- defaultMessage='Upon changing your account type, you will only be able to login with your email and password.'
- />
- </p>
- <p>
- <FormattedMessage
- id='claim.sso_to_email_newPwd'
- defaultMessage='Enter a new password for your {team} {site} account'
- values={{
- team: this.props.teamDisplayName,
- site: global.window.mm_config.SiteName
- }}
- />
- </p>
- <div className={formClass}>
- <input
- type='password'
- className='form-control'
- name='password'
- ref='password'
- placeholder={formatMessage(holders.newPwd)}
- spellCheck='false'
- />
- </div>
- <div className={formClass}>
- <input
- type='password'
- className='form-control'
- name='passwordconfirm'
- ref='passwordconfirm'
- placeholder={formatMessage(holders.confirm)}
- spellCheck='false'
- />
- </div>
- {error}
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='claim.sso_to_email.switchTo'
- defaultMessage='Switch {type} to email and password'
- values={{
- type: uiType
- }}
- />
- </button>
- </form>
- </div>
- );
- }
-}
-
-SSOToEmail.defaultProps = {
-};
-SSOToEmail.propTypes = {
- intl: intlShape.isRequired,
- currentType: React.PropTypes.string.isRequired,
- email: React.PropTypes.string.isRequired,
- teamName: React.PropTypes.string.isRequired,
- teamDisplayName: React.PropTypes.string
-};
-
-export default injectIntl(SSOToEmail);
diff --git a/web/react/components/confirm_modal.jsx b/web/react/components/confirm_modal.jsx
deleted file mode 100644
index bb3576684..000000000
--- a/web/react/components/confirm_modal.jsx
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-const Modal = ReactBootstrap.Modal;
-
-export default class ConfirmModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleConfirm = this.handleConfirm.bind(this);
- }
-
- handleConfirm() {
- this.props.onConfirm();
- }
-
- render() {
- return (
- <Modal
- className='modal-confirm'
- show={this.props.show}
- onHide={this.props.onCancel}
- >
- <Modal.Header closeButton={false}>
- <Modal.Title>{this.props.title}</Modal.Title>
- </Modal.Header>
- <Modal.Body>
- {this.props.message}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onCancel}
- >
- <FormattedMessage
- id='confirm_modal.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- type='button'
- className='btn btn-primary'
- onClick={this.props.onConfirm}
- >
- {this.props.confirmButton}
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-ConfirmModal.defaultProps = {
- title: '',
- message: '',
- confirmButton: ''
-};
-ConfirmModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- title: React.PropTypes.node,
- message: React.PropTypes.node,
- confirmButton: React.PropTypes.node,
- onConfirm: React.PropTypes.func.isRequired,
- onCancel: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
deleted file mode 100644
index 24e0ff6e9..000000000
--- a/web/react/components/create_comment.jsx
+++ /dev/null
@@ -1,448 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import SocketStore from '../stores/socket_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import PostDeletedModal from './post_deleted_modal.jsx';
-import PostStore from '../stores/post_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-import Textbox from './textbox.jsx';
-import MsgTyping from './msg_typing.jsx';
-import FileUpload from './file_upload.jsx';
-import FilePreview from './file_preview.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 KeyCodes = Constants.KeyCodes;
-
-const holders = defineMessages({
- commentLength: {
- id: 'create_comment.commentLength',
- defaultMessage: 'Comment length must be less than {max} characters.'
- },
- comment: {
- id: 'create_comment.comment',
- defaultMessage: 'Add Comment'
- },
- addComment: {
- id: 'create_comment.addComment',
- defaultMessage: 'Add a comment...'
- },
- commentTitle: {
- id: 'create_comment.commentTitle',
- defaultMessage: 'Comment'
- }
-});
-
-class CreateComment extends React.Component {
- constructor(props) {
- super(props);
-
- this.lastTime = 0;
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.commentMsgKeyPress = this.commentMsgKeyPress.bind(this);
- this.handleUserInput = this.handleUserInput.bind(this);
- this.handleKeyDown = this.handleKeyDown.bind(this);
- this.handleUploadClick = this.handleUploadClick.bind(this);
- this.handleUploadStart = this.handleUploadStart.bind(this);
- this.handleFileUploadComplete = this.handleFileUploadComplete.bind(this);
- this.handleUploadError = this.handleUploadError.bind(this);
- this.removePreview = this.removePreview.bind(this);
- this.getFileCount = this.getFileCount.bind(this);
- this.handleResize = this.handleResize.bind(this);
- this.onPreferenceChange = this.onPreferenceChange.bind(this);
- this.focusTextbox = this.focusTextbox.bind(this);
- this.showPostDeletedModal = this.showPostDeletedModal.bind(this);
- this.hidePostDeletedModal = this.hidePostDeletedModal.bind(this);
-
- PostStore.clearCommentDraftUploads();
-
- const draft = PostStore.getCommentDraft(this.props.rootId);
- this.state = {
- messageText: draft.message,
- uploadsInProgress: draft.uploadsInProgress,
- previews: draft.previews,
- submitting: false,
- windowWidth: Utils.windowWidth(),
- ctrlSend: PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter'),
- showPostDeletedModal: false
- };
- }
- componentDidMount() {
- PreferenceStore.addChangeListener(this.onPreferenceChange);
- window.addEventListener('resize', this.handleResize);
-
- this.focusTextbox();
- }
- componentWillUnmount() {
- PreferenceStore.removeChangeListener(this.onPreferenceChange);
- window.removeEventListener('resize', this.handleResize);
- }
- onPreferenceChange() {
- this.setState({
- ctrlSend: PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter')
- });
- }
- handleResize() {
- this.setState({windowWidth: Utils.windowWidth()});
- }
- componentDidUpdate(prevProps, prevState) {
- if (prevState.uploadsInProgress < this.state.uploadsInProgress) {
- $('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight);
- if (this.state.windowWidth > 768) {
- $('.post-right__scroll').perfectScrollbar('update');
- }
- }
-
- if (prevProps.rootId !== this.props.rootId) {
- this.focusTextbox();
- }
- }
- handleSubmit(e) {
- e.preventDefault();
-
- if (this.state.uploadsInProgress.length > 0) {
- return;
- }
-
- if (this.state.submitting) {
- return;
- }
-
- let post = {};
- post.filenames = [];
- post.message = this.state.messageText;
-
- if (post.message.trim().length === 0 && this.state.previews.length === 0) {
- return;
- }
-
- if (post.message.length > Constants.CHARACTER_LIMIT) {
- this.setState({postError: this.props.intl.formatMessage(holders.commentLength, {max: Constants.CHARACTER_LIMIT})});
- return;
- }
-
- const userId = UserStore.getCurrentId();
-
- post.channel_id = this.props.channelId;
- post.root_id = this.props.rootId;
- post.parent_id = this.props.rootId;
- post.filenames = this.state.previews;
- const time = Utils.getTimestamp();
- post.pending_post_id = `${userId}:${time}`;
- post.user_id = userId;
- post.create_at = time;
-
- PostStore.storePendingPost(post);
- PostStore.storeCommentDraft(this.props.rootId, null);
-
- Client.createPost(
- post,
- ChannelStore.getCurrent(),
- (data) => {
- AsyncClient.getPosts(this.props.channelId);
-
- const channel = ChannelStore.get(this.props.channelId);
- let member = ChannelStore.getMember(this.props.channelId);
- member.msg_count = channel.total_msg_count;
- member.last_viewed_at = Date.now();
- ChannelStore.setChannelMember(member);
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST,
- post: data
- });
- },
- (err) => {
- if (err.id === 'api.post.create_post.root_id.app_error') {
- this.showPostDeletedModal();
-
- PostStore.removePendingPost(post.channel_id, post.pending_post_id);
- } else {
- post.state = Constants.POST_FAILED;
- PostStore.updatePendingPost(post);
- }
-
- this.setState({
- submitting: false
- });
- }
- );
-
- this.setState({
- messageText: '',
- submitting: false,
- postError: null,
- previews: [],
- serverError: null
- });
- }
- commentMsgKeyPress(e) {
- if (this.state.ctrlSend && e.ctrlKey || !this.state.ctrlSend) {
- if (e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
- e.preventDefault();
- ReactDOM.findDOMNode(this.refs.textbox).blur();
- this.handleSubmit(e);
- }
- }
-
- const t = Date.now();
- if ((t - this.lastTime) > Constants.UPDATE_TYPING_MS) {
- SocketStore.sendMessage({channel_id: this.props.channelId, action: 'typing', props: {parent_id: this.props.rootId}});
- this.lastTime = t;
- }
- }
- handleUserInput(messageText) {
- let draft = PostStore.getCommentDraft(this.props.rootId);
- draft.message = messageText;
- PostStore.storeCommentDraft(this.props.rootId, draft);
-
- $('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight);
- $('.post-right__scroll').perfectScrollbar('update');
- this.setState({messageText: messageText});
- }
- handleKeyDown(e) {
- if (this.state.ctrlSend && e.keyCode === KeyCodes.ENTER && e.ctrlKey === true) {
- this.commentMsgKeyPress(e);
- return;
- }
-
- if (e.keyCode === KeyCodes.UP && this.state.messageText === '') {
- e.preventDefault();
-
- const lastPost = PostStore.getCurrentUsersLatestPost(this.props.channelId, this.props.rootId);
- if (!lastPost) {
- return;
- }
-
- AppDispatcher.handleViewAction({
- type: ActionTypes.RECEIVED_EDIT_POST,
- refocusId: '#reply_textbox',
- title: this.props.intl.formatMessage(holders.commentTitle),
- message: lastPost.message,
- postId: lastPost.id,
- channelId: lastPost.channel_id,
- comments: PostStore.getCommentCount(lastPost)
- });
- }
- }
- handleUploadClick() {
- this.focusTextbox();
- }
- handleUploadStart(clientIds) {
- let draft = PostStore.getCommentDraft(this.props.rootId);
-
- draft.uploadsInProgress = draft.uploadsInProgress.concat(clientIds);
- PostStore.storeCommentDraft(this.props.rootId, draft);
-
- this.setState({uploadsInProgress: draft.uploadsInProgress});
-
- // this is a bit redundant with the code that sets focus when the file input is clicked,
- // but this also resets the focus after a drag and drop
- this.focusTextbox();
- }
- handleFileUploadComplete(filenames, clientIds) {
- let draft = PostStore.getCommentDraft(this.props.rootId);
-
- // remove each finished file from uploads
- for (let i = 0; i < clientIds.length; i++) {
- const index = draft.uploadsInProgress.indexOf(clientIds[i]);
-
- if (index !== -1) {
- draft.uploadsInProgress.splice(index, 1);
- }
- }
-
- draft.previews = draft.previews.concat(filenames);
- PostStore.storeCommentDraft(this.props.rootId, draft);
-
- this.setState({uploadsInProgress: draft.uploadsInProgress, previews: draft.previews});
- }
- handleUploadError(err, clientId) {
- if (clientId === -1) {
- this.setState({serverError: err});
- } else {
- let draft = PostStore.getCommentDraft(this.props.rootId);
-
- const index = draft.uploadsInProgress.indexOf(clientId);
- if (index !== -1) {
- draft.uploadsInProgress.splice(index, 1);
- }
-
- PostStore.storeCommentDraft(this.props.rootId, draft);
-
- this.setState({uploadsInProgress: draft.uploadsInProgress, serverError: err});
- }
- }
- removePreview(id) {
- let previews = this.state.previews;
- let uploadsInProgress = this.state.uploadsInProgress;
-
- // id can either be the path of an uploaded file or the client id of an in progress upload
- let index = previews.indexOf(id);
- if (index === -1) {
- index = uploadsInProgress.indexOf(id);
-
- if (index !== -1) {
- uploadsInProgress.splice(index, 1);
- this.refs.fileUpload.getWrappedInstance().cancelUpload(id);
- }
- } else {
- previews.splice(index, 1);
- }
-
- let draft = PostStore.getCommentDraft(this.props.rootId);
- draft.previews = previews;
- draft.uploadsInProgress = uploadsInProgress;
- PostStore.storeCommentDraft(this.props.rootId, draft);
-
- this.setState({previews: previews, uploadsInProgress: uploadsInProgress});
- }
- componentWillReceiveProps(newProps) {
- if (newProps.rootId !== this.props.rootId) {
- const draft = PostStore.getCommentDraft(newProps.rootId);
- this.setState({messageText: draft.message, uploadsInProgress: draft.uploadsInProgress, previews: draft.previews});
- }
- }
- getFileCount() {
- return this.state.previews.length + this.state.uploadsInProgress.length;
- }
- focusTextbox() {
- if (!Utils.isMobile()) {
- this.refs.textbox.focus();
- }
- }
- showPostDeletedModal() {
- this.setState({
- showPostDeletedModal: true
- });
- }
- hidePostDeletedModal() {
- this.setState({
- showPostDeletedModal: false
- });
- }
- render() {
- let serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='form-group has-error'>
- <label className='control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- let postError = null;
- if (this.state.postError) {
- postError = <label className='control-label'>{this.state.postError}</label>;
- }
-
- let preview = null;
- if (this.state.previews.length > 0 || this.state.uploadsInProgress.length > 0) {
- preview = (
- <FilePreview
- files={this.state.previews}
- onRemove={this.removePreview}
- uploadsInProgress={this.state.uploadsInProgress}
- />
- );
- }
-
- let postFooterClassName = 'post-create-footer';
- if (postError) {
- postFooterClassName += ' has-error';
- }
-
- let uploadsInProgressText = null;
- if (this.state.uploadsInProgress.length > 0) {
- uploadsInProgressText = (
- <span className='pull-right post-right-comments-upload-in-progress'>
- {this.state.uploadsInProgress.length === 1 ? (
- <FormattedMessage
- id='create_comment.file'
- defaultMessage='File uploading'
- />
- ) : (
- <FormattedMessage
- id='create_comment.files'
- defaultMessage='Files uploading'
- />
- )}
- </span>
- );
- }
-
- const {formatMessage} = this.props.intl;
- return (
- <form onSubmit={this.handleSubmit}>
- <div className='post-create'>
- <div
- id={this.props.rootId}
- className='post-create-body comment-create-body'
- >
- <div className='post-body__cell'>
- <Textbox
- onUserInput={this.handleUserInput}
- onKeyPress={this.commentMsgKeyPress}
- onKeyDown={this.handleKeyDown}
- messageText={this.state.messageText}
- createMessage={formatMessage(holders.addComment)}
- initialText=''
- supportsCommands={false}
- id='reply_textbox'
- ref='textbox'
- />
- <FileUpload
- ref='fileUpload'
- getFileCount={this.getFileCount}
- onClick={this.handleUploadClick}
- onUploadStart={this.handleUploadStart}
- onFileUpload={this.handleFileUploadComplete}
- onUploadError={this.handleUploadError}
- postType='comment'
- channelId={this.props.channelId}
- />
- </div>
- </div>
- <MsgTyping
- channelId={this.props.channelId}
- parentId={this.props.rootId}
- />
- <div className={postFooterClassName}>
- <input
- type='button'
- className='btn btn-primary comment-btn pull-right'
- value={formatMessage(holders.comment)}
- onClick={this.handleSubmit}
- />
- {uploadsInProgressText}
- {preview}
- {postError}
- {serverError}
- </div>
- </div>
- <PostDeletedModal
- show={this.state.showPostDeletedModal}
- onHide={this.hidePostDeletedModal}
- />
- </form>
- );
- }
-}
-
-CreateComment.propTypes = {
- intl: intlShape.isRequired,
- channelId: React.PropTypes.string.isRequired,
- rootId: React.PropTypes.string.isRequired
-};
-
-export default injectIntl(CreateComment);
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
deleted file mode 100644
index 69cc74842..000000000
--- a/web/react/components/create_post.jsx
+++ /dev/null
@@ -1,507 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import MsgTyping from './msg_typing.jsx';
-import Textbox from './textbox.jsx';
-import FileUpload from './file_upload.jsx';
-import FilePreview from './file_preview.jsx';
-import PostDeletedModal from './post_deleted_modal.jsx';
-import TutorialTip from './tutorial/tutorial_tip.jsx';
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import ChannelStore from '../stores/channel_store.jsx';
-import PostStore from '../stores/post_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-import SocketStore from '../stores/socket_store.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedHTMLMessage} from 'mm-intl';
-
-const Preferences = Constants.Preferences;
-const TutorialSteps = Constants.TutorialSteps;
-const ActionTypes = Constants.ActionTypes;
-const KeyCodes = Constants.KeyCodes;
-
-const holders = defineMessages({
- comment: {
- id: 'create_post.comment',
- defaultMessage: 'Comment'
- },
- post: {
- id: 'create_post.post',
- defaultMessage: 'Post'
- },
- write: {
- id: 'create_post.write',
- defaultMessage: 'Write a message...'
- }
-});
-
-class CreatePost extends React.Component {
- constructor(props) {
- super(props);
-
- this.lastTime = 0;
-
- this.getCurrentDraft = this.getCurrentDraft.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.postMsgKeyPress = this.postMsgKeyPress.bind(this);
- this.handleUserInput = this.handleUserInput.bind(this);
- this.handleUploadClick = this.handleUploadClick.bind(this);
- this.handleUploadStart = this.handleUploadStart.bind(this);
- this.handleFileUploadComplete = this.handleFileUploadComplete.bind(this);
- this.handleUploadError = this.handleUploadError.bind(this);
- this.removePreview = this.removePreview.bind(this);
- this.onChange = this.onChange.bind(this);
- this.onPreferenceChange = this.onPreferenceChange.bind(this);
- this.getFileCount = this.getFileCount.bind(this);
- this.handleKeyDown = this.handleKeyDown.bind(this);
- this.sendMessage = this.sendMessage.bind(this);
- this.focusTextbox = this.focusTextbox.bind(this);
- this.showPostDeletedModal = this.showPostDeletedModal.bind(this);
- this.hidePostDeletedModal = this.hidePostDeletedModal.bind(this);
-
- PostStore.clearDraftUploads();
-
- const draft = this.getCurrentDraft();
-
- this.state = {
- channelId: ChannelStore.getCurrentId(),
- messageText: draft.messageText,
- uploadsInProgress: draft.uploadsInProgress,
- previews: draft.previews,
- submitting: false,
- initialText: draft.messageText,
- ctrlSend: false,
- showTutorialTip: false,
- showPostDeletedModal: false
- };
- }
- getCurrentDraft() {
- const draft = PostStore.getCurrentDraft();
- const safeDraft = {previews: [], messageText: '', uploadsInProgress: []};
-
- if (draft) {
- if (draft.message) {
- safeDraft.messageText = draft.message;
- }
- if (draft.previews) {
- safeDraft.previews = draft.previews;
- }
- if (draft.uploadsInProgress) {
- safeDraft.uploadsInProgress = draft.uploadsInProgress;
- }
- }
-
- return safeDraft;
- }
- handleSubmit(e) {
- e.preventDefault();
-
- if (this.state.uploadsInProgress.length > 0 || this.state.submitting) {
- return;
- }
-
- const post = {};
- post.filenames = [];
- post.message = this.state.messageText;
-
- if (post.message.trim().length === 0 && this.state.previews.length === 0) {
- return;
- }
-
- if (post.message.length > Constants.CHARACTER_LIMIT) {
- this.setState({postError: `Post length must be less than ${Constants.CHARACTER_LIMIT} characters.`});
- return;
- }
-
- this.setState({submitting: true, serverError: null});
-
- if (post.message.indexOf('/') === 0) {
- Client.executeCommand(
- this.state.channelId,
- post.message,
- false,
- (data) => {
- PostStore.storeDraft(this.state.channelId, null);
- this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
-
- if (data.goto_location && data.goto_location.length > 0) {
- window.location.href = data.goto_location;
- }
- },
- (err) => {
- if (err.sendMessage) {
- this.sendMessage(post);
- } else {
- const state = {};
- state.serverError = err.message;
- state.submitting = false;
- this.setState(state);
- }
- }
- );
- } else {
- this.sendMessage(post);
- }
- }
- sendMessage(post) {
- post.channel_id = this.state.channelId;
- post.filenames = this.state.previews;
-
- const time = Utils.getTimestamp();
- const userId = UserStore.getCurrentId();
- post.pending_post_id = `${userId}:${time}`;
- post.user_id = userId;
- post.create_at = time;
- post.parent_id = this.state.parentId;
-
- const channel = ChannelStore.get(this.state.channelId);
-
- GlobalActions.emitUserPostedEvent(post);
- this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
-
- Client.createPost(post, channel,
- (data) => {
- AsyncClient.getPosts();
-
- const member = ChannelStore.getMember(channel.id);
- member.msg_count = channel.total_msg_count;
- member.last_viewed_at = Date.now();
- ChannelStore.setChannelMember(member);
-
- GlobalActions.emitPostRecievedEvent(data);
- },
- (err) => {
- if (err.id === 'api.post.create_post.root_id.app_error') {
- // this should never actually happen since you can't reply from this textbox
- this.showPostDeletedModal();
-
- PostStore.removePendingPost(post.pending_post_id);
- } else {
- post.state = Constants.POST_FAILED;
- PostStore.updatePendingPost(post);
- }
-
- this.setState({
- submitting: false
- });
- }
- );
- }
- focusTextbox() {
- if (!Utils.isMobile()) {
- this.refs.textbox.focus();
- }
- }
- postMsgKeyPress(e) {
- if (this.state.ctrlSend && e.ctrlKey || !this.state.ctrlSend) {
- if (e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
- e.preventDefault();
- ReactDOM.findDOMNode(this.refs.textbox).blur();
- this.handleSubmit(e);
- }
- }
-
- const t = Date.now();
- if ((t - this.lastTime) > Constants.UPDATE_TYPING_MS) {
- SocketStore.sendMessage({channel_id: this.state.channelId, action: 'typing', props: {parent_id: ''}, state: {}});
- this.lastTime = t;
- }
- }
- handleUserInput(messageText) {
- this.setState({messageText});
-
- const draft = PostStore.getCurrentDraft();
- draft.message = messageText;
- PostStore.storeCurrentDraft(draft);
- }
- handleUploadClick() {
- this.focusTextbox();
- }
- handleUploadStart(clientIds, channelId) {
- const draft = PostStore.getDraft(channelId);
-
- draft.uploadsInProgress = draft.uploadsInProgress.concat(clientIds);
- PostStore.storeDraft(channelId, draft);
-
- this.setState({uploadsInProgress: draft.uploadsInProgress});
-
- // this is a bit redundant with the code that sets focus when the file input is clicked,
- // but this also resets the focus after a drag and drop
- this.focusTextbox();
- }
- handleFileUploadComplete(filenames, clientIds, channelId) {
- const draft = PostStore.getDraft(channelId);
-
- // remove each finished file from uploads
- for (let i = 0; i < clientIds.length; i++) {
- const index = draft.uploadsInProgress.indexOf(clientIds[i]);
-
- if (index !== -1) {
- draft.uploadsInProgress.splice(index, 1);
- }
- }
-
- draft.previews = draft.previews.concat(filenames);
- PostStore.storeDraft(channelId, draft);
-
- this.setState({uploadsInProgress: draft.uploadsInProgress, previews: draft.previews});
- }
- handleUploadError(err, clientId) {
- let message = err;
- if (message && typeof message !== 'string') {
- // err is an AppError from the server
- message = err.message;
- }
-
- if (clientId !== -1) {
- const draft = PostStore.getDraft(this.state.channelId);
-
- const index = draft.uploadsInProgress.indexOf(clientId);
- if (index !== -1) {
- draft.uploadsInProgress.splice(index, 1);
- }
-
- PostStore.storeDraft(this.state.channelId, draft);
-
- this.setState({uploadsInProgress: draft.uploadsInProgress});
- }
-
- this.setState({serverError: message});
- }
- removePreview(id) {
- const previews = Object.assign([], this.state.previews);
- const uploadsInProgress = this.state.uploadsInProgress;
-
- // id can either be the path of an uploaded file or the client id of an in progress upload
- let index = previews.indexOf(id);
- if (index === -1) {
- index = uploadsInProgress.indexOf(id);
-
- if (index !== -1) {
- uploadsInProgress.splice(index, 1);
- this.refs.fileUpload.getWrappedInstance().cancelUpload(id);
- }
- } else {
- previews.splice(index, 1);
- }
-
- const draft = PostStore.getCurrentDraft();
- draft.previews = previews;
- draft.uploadsInProgress = uploadsInProgress;
- PostStore.storeCurrentDraft(draft);
-
- this.setState({previews, uploadsInProgress});
- }
- componentWillMount() {
- const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
-
- // wait to load these since they may have changed since the component was constructed (particularly in the case of skipping the tutorial)
- this.setState({
- ctrlSend: PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter'),
- showTutorialTip: tutorialStep === TutorialSteps.POST_POPOVER
- });
- }
- componentDidMount() {
- ChannelStore.addChangeListener(this.onChange);
- PreferenceStore.addChangeListener(this.onPreferenceChange);
-
- this.focusTextbox();
- }
- componentDidUpdate(prevProps, prevState) {
- if (prevState.channelId !== this.state.channelId) {
- this.focusTextbox();
- }
- }
- componentWillUnmount() {
- ChannelStore.removeChangeListener(this.onChange);
- PreferenceStore.removeChangeListener(this.onPreferenceChange);
- }
- onChange() {
- const channelId = ChannelStore.getCurrentId();
- if (this.state.channelId !== channelId) {
- const draft = this.getCurrentDraft();
-
- this.setState({channelId, messageText: draft.messageText, initialText: draft.messageText, submitting: false, serverError: null, postError: null, previews: draft.previews, uploadsInProgress: draft.uploadsInProgress});
- }
- }
- onPreferenceChange() {
- const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
- this.setState({
- showTutorialTip: tutorialStep === TutorialSteps.POST_POPOVER,
- ctrlSend: PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter')
- });
- }
- getFileCount(channelId) {
- if (channelId === this.state.channelId) {
- return this.state.previews.length + this.state.uploadsInProgress.length;
- }
-
- const draft = PostStore.getDraft(channelId);
- return draft.previews.length + draft.uploadsInProgress.length;
- }
- handleKeyDown(e) {
- if (this.state.ctrlSend && e.keyCode === KeyCodes.ENTER && e.ctrlKey === true) {
- this.postMsgKeyPress(e);
- return;
- }
-
- if (e.keyCode === KeyCodes.UP && this.state.messageText === '') {
- e.preventDefault();
-
- const channelId = ChannelStore.getCurrentId();
- const lastPost = PostStore.getCurrentUsersLatestPost(channelId);
- if (!lastPost) {
- return;
- }
- const {formatMessage} = this.props.intl;
- var type = (lastPost.root_id && lastPost.root_id.length > 0) ? formatMessage(holders.comment) : formatMessage(holders.post);
-
- AppDispatcher.handleViewAction({
- type: ActionTypes.RECEIVED_EDIT_POST,
- refocusId: '#post_textbox',
- title: type,
- message: lastPost.message,
- postId: lastPost.id,
- channelId: lastPost.channel_id,
- comments: PostStore.getCommentCount(lastPost)
- });
- }
- }
- showPostDeletedModal() {
- this.setState({
- showPostDeletedModal: true
- });
- }
- hidePostDeletedModal() {
- this.setState({
- showPostDeletedModal: false
- });
- }
- createTutorialTip() {
- const screens = [];
-
- screens.push(
- <div>
- <FormattedHTMLMessage
- id='create_post.tutorialTip'
- defaultMessage='<h4>Sending Messages</h4><p>Type here to write a message and press <strong>Enter</strong> to post it.</p><p>Click the <strong>Attachment</strong> button to upload an image or a file.</p>'
- />
- </div>
- );
-
- return (
- <TutorialTip
- placement='top'
- screens={screens}
- overlayClass='tip-overlay--chat'
- />
- );
- }
- render() {
- let serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='has-error'>
- <label className='control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- let postError = null;
- if (this.state.postError) {
- postError = <label className='control-label'>{this.state.postError}</label>;
- }
-
- let preview = null;
- if (this.state.previews.length > 0 || this.state.uploadsInProgress.length > 0) {
- preview = (
- <FilePreview
- files={this.state.previews}
- onRemove={this.removePreview}
- uploadsInProgress={this.state.uploadsInProgress}
- />
- );
- }
-
- let postFooterClassName = 'post-create-footer';
- if (postError) {
- postFooterClassName += ' has-error';
- }
-
- let tutorialTip = null;
- if (this.state.showTutorialTip) {
- tutorialTip = this.createTutorialTip();
- }
-
- return (
- <form
- id='create_post'
- ref='topDiv'
- role='form'
- onSubmit={this.handleSubmit}
- >
- <div className='post-create'>
- <div className='post-create-body'>
- <div className='post-body__cell'>
- <Textbox
- onUserInput={this.handleUserInput}
- onKeyPress={this.postMsgKeyPress}
- onKeyDown={this.handleKeyDown}
- messageText={this.state.messageText}
- createMessage={this.props.intl.formatMessage(holders.write)}
- channelId={this.state.channelId}
- id='post_textbox'
- ref='textbox'
- />
- <FileUpload
- ref='fileUpload'
- getFileCount={this.getFileCount}
- onClick={this.handleUploadClick}
- onUploadStart={this.handleUploadStart}
- onFileUpload={this.handleFileUploadComplete}
- onUploadError={this.handleUploadError}
- postType='post'
- channelId=''
- />
- </div>
- <a
- className='send-button theme'
- onClick={this.handleSubmit}
- >
- <i className='fa fa-paper-plane'/>
- </a>
- {tutorialTip}
- </div>
- <div className={postFooterClassName}>
- <MsgTyping
- channelId={this.state.channelId}
- parentId=''
- />
- {preview}
- {postError}
- {serverError}
- </div>
- </div>
- <PostDeletedModal
- show={this.state.showPostDeletedModal}
- onHide={this.hidePostDeletedModal}
- />
- </form>
- );
- }
-}
-
-CreatePost.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(CreatePost);
diff --git a/web/react/components/delete_channel_modal.jsx b/web/react/components/delete_channel_modal.jsx
deleted file mode 100644
index 70e7a67a8..000000000
--- a/web/react/components/delete_channel_modal.jsx
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-const Modal = ReactBootstrap.Modal;
-import TeamStore from '../stores/team_store.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-import {browserHistory} from 'react-router';
-
-export default class DeleteChannelModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleDelete = this.handleDelete.bind(this);
- }
-
- handleDelete() {
- if (this.props.channel.id.length !== 26) {
- return;
- }
-
- browserHistory.push(TeamStore.getCurrentTeamUrl() + '/channels/town-square');
- Client.deleteChannel(
- this.props.channel.id,
- () => {
- AsyncClient.getChannels(true);
- },
- (err) => {
- AsyncClient.dispatchError(err, 'handleDelete');
- }
- );
- }
-
- render() {
- let channelTerm = (
- <FormattedMessage
- id='delete_channel.channel'
- defaultMessage='channel'
- />
- );
- if (this.props.channel.type === Constants.PRIVATE_CHANNEL) {
- channelTerm = (
- <FormattedMessage
- id='delete_channel.group'
- defaultMessage='group'
- />
- );
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.props.onHide}
- >
- <Modal.Header closeButton={true}>
- <h4 className='modal-title'>
- <FormattedMessage
- id='delete_channel.confirm'
- defaultMessage='Confirm DELETE Channel'
- />
- </h4>
- </Modal.Header>
- <Modal.Body>
- <FormattedMessage
- id='delete_channel.question'
- defaultMessage='Are you sure you wish to delete the {display_name} {term}?'
- values={{
- display_name: this.props.channel.display_name,
- term: (channelTerm)
- }}
- />
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onHide}
- >
- <FormattedMessage
- id='delete_channel.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- type='button'
- className='btn btn-danger'
- data-dismiss='modal'
- onClick={this.handleDelete}
- >
- <FormattedMessage
- id='delete_channel.del'
- defaultMessage='Delete'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-DeleteChannelModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/delete_post_modal.jsx b/web/react/components/delete_post_modal.jsx
deleted file mode 100644
index 5b2dd1197..000000000
--- a/web/react/components/delete_post_modal.jsx
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-import PostStore from '../stores/post_store.jsx';
-import ModalStore from '../stores/modal_store.jsx';
-var Modal = ReactBootstrap.Modal;
-import * as AsyncClient from '../utils/async_client.jsx';
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-var ActionTypes = Constants.ActionTypes;
-
-export default class DeletePostModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleDelete = this.handleDelete.bind(this);
- this.handleToggle = this.handleToggle.bind(this);
- this.handleHide = this.handleHide.bind(this);
-
- this.state = {
- show: false,
- post: null,
- commentCount: 0,
- error: ''
- };
- }
-
- componentDidMount() {
- ModalStore.addModalListener(ActionTypes.TOGGLE_DELETE_POST_MODAL, this.handleToggle);
- }
-
- componentWillUnmount() {
- ModalStore.removeModalListener(ActionTypes.TOGGLE_DELETE_POST_MODAL, this.handleToggle);
- }
-
- componentDidUpdate(prevProps, prevState) {
- if (this.state.show && !prevState.show) {
- setTimeout(() => {
- $(ReactDOM.findDOMNode(this.refs.deletePostBtn)).focus();
- }, 0);
- }
- }
-
- handleDelete() {
- Client.deletePost(
- this.state.post.channel_id,
- this.state.post.id,
- () => {
- PostStore.deletePost(this.state.post);
- AsyncClient.getPosts(this.state.post.channel_id);
-
- if (this.state.post.id === PostStore.getSelectedPostId()) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
- }
- },
- (err) => {
- AsyncClient.dispatchError(err, 'deletePost');
- }
- );
-
- this.handleHide();
- }
-
- handleToggle(value, args) {
- this.setState({
- show: value,
- post: args.post,
- commentCount: args.commentCount,
- error: ''
- });
- }
-
- handleHide() {
- this.setState({show: false});
- }
-
- render() {
- if (!this.state.post) {
- return null;
- }
-
- var error = null;
- if (this.state.error) {
- error = <div className='form-group has-error'><label className='control-label'>{this.state.error}</label></div>;
- }
-
- var commentWarning = '';
- if (this.state.commentCount > 0) {
- commentWarning = (
- <FormattedMessage
- id='delete_post.warning'
- defaultMessage='This post has {count} comment(s) on it.'
- values={{
- count: this.state.commentCount
- }}
- />
- );
- }
-
- const postTerm = this.state.post.root_id ? (
- <FormattedMessage
- id='delete_post.comment'
- defaultMessage='Comment'
- />
- ) : (
- <FormattedMessage
- id='delete_post.post'
- defaultMessage='Post'
- />
- );
-
- return (
- <Modal
- show={this.state.show}
- onHide={this.handleHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='delete_post.confirm'
- defaultMessage='Confirm {term} Delete'
- values={{
- term: (postTerm)
- }}
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <FormattedMessage
- id='delete_post.question'
- defaultMessage='Are you sure you want to delete this {term}?'
- values={{
- term: (postTerm)
- }}
- />
- <br/>
- <br/>
- {commentWarning}
- {error}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.handleHide}
- >
- <FormattedMessage
- id='delete_post.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- ref='deletePostBtn'
- type='button'
- className='btn btn-danger'
- onClick={this.handleDelete}
- >
- <FormattedMessage
- id='delete_post.del'
- defaultMessage='Delete'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
diff --git a/web/react/components/do_verify_email.jsx b/web/react/components/do_verify_email.jsx
deleted file mode 100644
index df98bf463..000000000
--- a/web/react/components/do_verify_email.jsx
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-import * as Client from '../utils/client.jsx';
-import LoadingScreen from './loading_screen.jsx';
-
-import {browserHistory} from 'react-router';
-
-export default class DoVerifyEmail extends React.Component {
- constructor(props) {
- super(props);
-
- this.state = {
- verifyStatus: 'pending',
- serverError: ''
- };
- }
- componentWillMount() {
- const uid = this.props.location.query.uid;
- const hid = this.props.location.query.hid;
- const teamName = this.props.location.query.teamname;
- const email = this.props.location.query.email;
-
- Client.verifyEmail(
- () => {
- browserHistory.push('/' + teamName + '/login?extra=verified&email=' + email);
- },
- (err) => {
- this.setState({verifyStatus: 'failure', serverError: err.message});
- },
- uid,
- hid
- );
- }
- render() {
- if (this.state.verifyStatus !== 'failure') {
- return (<LoadingScreen/>);
- }
-
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <h3>
- <FormattedMessage
- id='email_verify.almost'
- defaultMessage='{siteName}: You are almost done'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </h3>
- <div>
- <p>
- <FormattedMessage id='email_verify.verifyFailed'/>
- </p>
- <p className='alert alert-danger'>
- <i className='fa fa-times'/>
- {this.state.serverError}
- </p>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-DoVerifyEmail.defaultProps = {
-};
-DoVerifyEmail.propTypes = {
- location: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/edit_channel_header_modal.jsx b/web/react/components/edit_channel_header_modal.jsx
deleted file mode 100644
index f6865fadd..000000000
--- a/web/react/components/edit_channel_header_modal.jsx
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as Client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-const holders = defineMessages({
- error: {
- id: 'edit_channel_header_modal.error',
- defaultMessage: 'This channel header is too long, please enter a shorter one'
- }
-});
-
-class EditChannelHeaderModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.onShow = this.onShow.bind(this);
- this.onHide = this.onHide.bind(this);
-
- this.state = {
- header: props.channel.header,
- serverError: ''
- };
- }
-
- componentDidMount() {
- if (this.props.show) {
- this.onShow();
- }
- }
-
- componentWillReceiveProps(nextProps) {
- if (this.props.channel.header !== nextProps.channel.header) {
- this.setState({
- header: nextProps.channel.header
- });
- }
- }
-
- componentDidUpdate(prevProps) {
- if (this.props.show && !prevProps.show) {
- this.onShow();
- }
- }
-
- handleChange(e) {
- this.setState({
- header: e.target.value
- });
- }
-
- handleSubmit() {
- Client.updateChannelHeader(
- this.props.channel.id,
- this.state.header,
- (channel) => {
- this.setState({serverError: ''});
- this.onHide();
-
- AppDispatcher.handleServerAction({
- type: Constants.ActionTypes.RECEIVED_CHANNEL,
- channel
- });
- },
- (err) => {
- if (err.id === 'api.context.invalid_param.app_error') {
- this.setState({serverError: this.props.intl.formatMessage(holders.error)});
- } else {
- this.setState({serverError: err.message});
- }
- }
- );
- }
-
- onShow() {
- const textarea = ReactDOM.findDOMNode(this.refs.textarea);
- Utils.placeCaretAtEnd(textarea);
- }
-
- onHide() {
- this.setState({
- serverError: '',
- header: this.props.channel.header
- });
-
- this.props.onHide();
- }
-
- render() {
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><br/><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.onHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='edit_channel_header_modal.title'
- defaultMessage='Edit Header for {channel}'
- values={{
- channel: this.props.channel.display_name
- }}
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <p>
- <FormattedMessage
- id='edit_channel_header_modal.description'
- defaultMessage='Edit the text appearing next to the channel name in the channel header.'
- />
- </p>
- <textarea
- ref='textarea'
- className='form-control no-resize'
- rows='6'
- id='edit_header'
- maxLength='1024'
- value={this.state.header}
- onChange={this.handleChange}
- />
- {serverError}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.onHide}
- >
- <FormattedMessage
- id='edit_channel_header_modal.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- type='button'
- className='btn btn-primary'
- onClick={this.handleSubmit}
- >
- <FormattedMessage
- id='edit_channel_header_modal.save'
- defaultMessage='Save'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-EditChannelHeaderModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired
-};
-
-export default injectIntl(EditChannelHeaderModal);
diff --git a/web/react/components/edit_channel_purpose_modal.jsx b/web/react/components/edit_channel_purpose_modal.jsx
deleted file mode 100644
index d8354f59d..000000000
--- a/web/react/components/edit_channel_purpose_modal.jsx
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-const holders = defineMessages({
- error: {
- id: 'edit_channel_purpose_modal.error',
- defaultMessage: 'This channel purpose is too long, please enter a shorter one'
- }
-});
-
-export default class EditChannelPurposeModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleHide = this.handleHide.bind(this);
- this.handleSave = this.handleSave.bind(this);
-
- this.state = {serverError: ''};
- }
-
- componentDidUpdate() {
- if (this.props.show) {
- $(ReactDOM.findDOMNode(this.refs.purpose)).focus();
- }
- }
-
- handleHide() {
- this.setState({serverError: ''});
-
- if (this.props.onModalDismissed) {
- this.props.onModalDismissed();
- }
- }
-
- handleSave() {
- if (!this.props.channel) {
- return;
- }
-
- const data = {
- channel_id: this.props.channel.id,
- channel_purpose: ReactDOM.findDOMNode(this.refs.purpose).value.trim()
- };
-
- Client.updateChannelPurpose(data,
- () => {
- AsyncClient.getChannel(this.props.channel.id);
-
- this.handleHide();
- },
- (err) => {
- if (err.id === 'api.context.invalid_param.app_error') {
- this.setState({serverError: this.props.intl.formatMessage(holders.error)});
- } else {
- this.setState({serverError: err.message});
- }
- }
- );
- }
-
- render() {
- if (!this.props.show) {
- return null;
- }
-
- let serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='form-group has-error'>
- <br/>
- <label className='control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- let title = (
- <span>
- <FormattedMessage
- id='edit_channel_purpose_modal.title1'
- defaultMessage='Edit Purpose'
- />
- </span>
- );
- if (this.props.channel.display_name) {
- title = (
- <span>
- <FormattedMessage
- id='edit_channel_purpose_modal.title2'
- defaultMessage='Edit Purpose for '
- />
- <span className='name'>{this.props.channel.display_name}</span>
- </span>
- );
- }
-
- let channelType = (
- <FormattedMessage
- id='edit_channel_purpose_modal.channel'
- defaultMessage='Channel'
- />
- );
- if (this.props.channel.type === Constants.PRIVATE_CHANNEL) {
- channelType = (
- <FormattedMessage
- id='edit_channel_purpose_modal.group'
- defaultMessage='Group'
- />
- );
- }
-
- return (
- <Modal
- className='modal-edit-channel-purpose'
- ref='modal'
- show={this.props.show}
- onHide={this.handleHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- {title}
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <p>
- <FormattedMessage
- id='edit_channel_purpose_modal.body'
- defaultMessage='Describe how this {type} should be used. This text appears in the channel list in the "More..." menu and helps others decide whether to join.'
- values={{
- type: (channelType)
- }}
- />
- </p>
- <textarea
- ref='purpose'
- className='form-control no-resize'
- rows='6'
- maxLength='128'
- defaultValue={this.props.channel.purpose}
- />
- {serverError}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.handleHide}
- >
- <FormattedMessage
- id='edit_channel_purpose_modal.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- type='button'
- className='btn btn-primary'
- onClick={this.handleSave}
- >
- <FormattedMessage
- id='edit_channel_purpose_modal.save'
- defaultMessage='Save'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-EditChannelPurposeModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- channel: React.PropTypes.object,
- onModalDismissed: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(EditChannelPurposeModal); \ No newline at end of file
diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx
deleted file mode 100644
index f02239fcf..000000000
--- a/web/react/components/edit_post_modal.jsx
+++ /dev/null
@@ -1,220 +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 AsyncClient from '../utils/async_client.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import Textbox from './textbox.jsx';
-import BrowserStore from '../stores/browser_store.jsx';
-import PostStore from '../stores/post_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-var KeyCodes = Constants.KeyCodes;
-
-const holders = defineMessages({
- editPost: {
- id: 'edit_post.editPost',
- defaultMessage: 'Edit the post...'
- }
-});
-
-class EditPostModal extends React.Component {
- constructor() {
- super();
-
- this.handleEdit = this.handleEdit.bind(this);
- this.handleEditInput = this.handleEditInput.bind(this);
- this.handleEditKeyPress = this.handleEditKeyPress.bind(this);
- this.handleUserInput = this.handleUserInput.bind(this);
- this.handleEditPostEvent = this.handleEditPostEvent.bind(this);
- this.handleKeyDown = this.handleKeyDown.bind(this);
- this.onPreferenceChange = this.onPreferenceChange.bind(this);
-
- this.state = {editText: '', title: '', post_id: '', channel_id: '', comments: 0, refocusId: ''};
- }
- handleEdit() {
- var updatedPost = {};
- updatedPost.message = this.state.editText.trim();
-
- if (updatedPost.message.length === 0) {
- var tempState = this.state;
- delete tempState.editText;
- BrowserStore.setItem('edit_state_transfer', tempState);
- $('#edit_post').modal('hide');
- GlobalActions.showDeletePostModal(PostStore.getPost(this.state.channel_id, this.state.post_id), this.state.comments);
- return;
- }
-
- updatedPost.id = this.state.post_id;
- updatedPost.channel_id = this.state.channel_id;
-
- Client.updatePost(updatedPost,
- function success() {
- AsyncClient.getPosts(updatedPost.channel_id);
- window.scrollTo(0, 0);
- },
- function error(err) {
- AsyncClient.dispatchError(err, 'updatePost');
- }
- );
-
- $('#edit_post').modal('hide');
- }
- handleEditInput(editMessage) {
- this.setState({editText: editMessage});
- }
- handleEditKeyPress(e) {
- if (!this.state.ctrlSend && e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
- e.preventDefault();
- ReactDOM.findDOMNode(this.refs.editbox).blur();
- this.handleEdit(e);
- }
- }
- handleUserInput(e) {
- this.setState({editText: e.target.value});
- }
- handleEditPostEvent(options) {
- this.setState({
- editText: options.message || '',
- title: options.title || '',
- post_id: options.postId || '',
- channel_id: options.channelId || '',
- comments: options.comments || 0,
- refocusId: options.refocusId || ''
- });
-
- $(ReactDOM.findDOMNode(this.refs.modal)).modal('show');
- }
- handleKeyDown(e) {
- if (this.state.ctrlSend && e.keyCode === KeyCodes.ENTER && e.ctrlKey === true) {
- this.handleEdit(e);
- }
- }
- onPreferenceChange() {
- this.setState({
- ctrlSend: PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter')
- });
- }
- componentDidMount() {
- var self = this;
-
- $(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function onHidden() {
- self.setState({editText: '', title: '', channel_id: '', post_id: '', comments: 0, refocusId: '', error: ''});
- });
-
- $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function onShow(e) {
- var button = e.relatedTarget;
- if (!button) {
- return;
- }
- self.setState({editText: $(button).attr('data-message'), title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments'), refocusId: $(button).attr('data-refocusid')});
- });
-
- $(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', function onShown() {
- self.refs.editbox.resize();
- $('#edit_textbox').get(0).focus();
- });
-
- $(ReactDOM.findDOMNode(this.refs.modal)).on('hide.bs.modal', function onShown() {
- if (self.state.refocusId !== '') {
- setTimeout(() => {
- $(self.state.refocusId).get(0).focus();
- });
- }
- });
-
- PostStore.addEditPostListener(this.handleEditPostEvent);
- PreferenceStore.addChangeListener(this.onPreferenceChange);
- }
- componentWillUnmount() {
- PostStore.removeEditPostListner(this.handleEditPostEvent);
- PreferenceStore.removeChangeListener(this.onPreferenceChange);
- }
- render() {
- var error = (<div className='form-group'><br/></div>);
- if (this.state.error) {
- error = (<div className='form-group has-error'><br/><label className='control-label'>{this.state.error}</label></div>);
- }
-
- return (
- <div
- className='modal fade edit-modal'
- ref='modal'
- id='edit_post'
- role='dialog'
- tabIndex='-1'
- aria-hidden='true'
- >
- <div className='modal-dialog modal-push-down'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- onClick={this.handleEditClose}
- >
- <span aria-hidden='true'>&times;</span>
- </button>
- <h4 className='modal-title'>
- <FormattedMessage
- id='edit_post.edit'
- defaultMessage='Edit {title}'
- values={{
- title: this.state.title
- }}
- />
- </h4>
- </div>
- <div className='edit-modal-body modal-body'>
- <Textbox
- onUserInput={this.handleEditInput}
- onKeyPress={this.handleEditKeyPress}
- onKeyDown={this.handleKeyDown}
- messageText={this.state.editText}
- createMessage={this.props.intl.formatMessage(holders.editPost)}
- supportsCommands={false}
- id='edit_textbox'
- ref='editbox'
- />
- {error}
- </div>
- <div className='modal-footer'>
- <button
- type='button'
- className='btn btn-default'
- data-dismiss='modal'
- >
- <FormattedMessage
- id='edit_post.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- type='button'
- className='btn btn-primary'
- onClick={this.handleEdit}
- >
- <FormattedMessage
- id='edit_post.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-EditPostModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(EditPostModal); \ No newline at end of file
diff --git a/web/react/components/error_bar.jsx b/web/react/components/error_bar.jsx
deleted file mode 100644
index 9a114c544..000000000
--- a/web/react/components/error_bar.jsx
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ErrorStore from '../stores/error_store.jsx';
-
-// import mm-intl is required for the tool to be able to extract the messages
-import {defineMessages} from 'mm-intl';
-
-var messages = defineMessages({
- preview: {
- id: 'error_bar.preview_mode',
- defaultMessage: 'Preview Mode: Email notifications have not been configured'
- }
-});
-
-export default class ErrorBar extends React.Component {
- constructor() {
- super();
-
- this.onErrorChange = this.onErrorChange.bind(this);
- this.handleClose = this.handleClose.bind(this);
-
- this.state = ErrorStore.getLastError();
- }
-
- static propTypes() {
- return {
- intl: ReactIntl.intlShape.isRequired
- };
- }
-
- isValidError(s) {
- if (!s) {
- return false;
- }
-
- if (!s.message) {
- return false;
- }
-
- return true;
- }
-
- componentWillMount() {
- if (global.window.mm_config.SendEmailNotifications === 'false') {
- ErrorStore.storeLastError({message: this.props.intl.formatMessage(messages.preview)});
- this.onErrorChange();
- }
- }
-
- componentDidMount() {
- ErrorStore.addChangeListener(this.onErrorChange);
- }
-
- componentWillUnmount() {
- ErrorStore.removeChangeListener(this.onErrorChange);
- }
-
- onErrorChange() {
- var newState = ErrorStore.getLastError();
-
- if (newState) {
- this.setState(newState);
- } else {
- this.setState({message: null});
- }
- }
-
- handleClose(e) {
- if (e) {
- e.preventDefault();
- }
-
- ErrorStore.clearLastError();
- this.setState({message: null});
- }
-
- render() {
- if (!this.isValidError(this.state)) {
- return <div/>;
- }
-
- return (
- <div className='error-bar'>
- <span>{this.state.message}</span>
- <a
- href='#'
- className='error-bar__close'
- onClick={this.handleClose}
- >
- &times;
- </a>
- </div>
- );
- }
-}
-
-export default ReactIntl.injectIntl(ErrorBar);
diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx
deleted file mode 100644
index 383529212..000000000
--- a/web/react/components/file_attachment.jsx
+++ /dev/null
@@ -1,225 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- download: {
- id: 'file_attachment.download',
- defaultMessage: 'Download'
- }
-});
-
-class FileAttachment extends React.Component {
- constructor(props) {
- super(props);
-
- this.loadFiles = this.loadFiles.bind(this);
- this.addBackgroundImage = this.addBackgroundImage.bind(this);
-
- this.canSetState = false;
- this.state = {fileSize: -1};
- }
- componentDidMount() {
- this.loadFiles();
- }
- componentDidUpdate(prevProps) {
- if (this.props.filename !== prevProps.filename) {
- this.loadFiles();
- }
- }
- loadFiles() {
- this.canSetState = true;
-
- var filename = this.props.filename;
-
- if (filename) {
- var fileInfo = this.getFileInfoFromName(filename);
- var type = utils.getFileType(fileInfo.ext);
-
- if (type === 'image') {
- var self = this; // Need this reference since we use the given "this"
- $('<img/>').attr('src', fileInfo.path + '_thumb.jpg').load(function loadWrapper(path, name) {
- return function loader() {
- $(this).remove();
- if (name in self.refs) {
- var imgDiv = ReactDOM.findDOMNode(self.refs[name]);
-
- $(imgDiv).removeClass('post-image__load');
- $(imgDiv).addClass('post-image');
-
- var width = this.width || $(this).width();
- var height = this.height || $(this).height();
-
- if (width < Constants.THUMBNAIL_WIDTH &&
- height < Constants.THUMBNAIL_HEIGHT) {
- $(imgDiv).addClass('small');
- } else {
- $(imgDiv).addClass('normal');
- }
-
- self.addBackgroundImage(name, path);
- }
- };
- }(fileInfo.path, filename));
- }
- }
- }
- componentWillUnmount() {
- // keep track of when this component is mounted so that we can asynchronously change state without worrying about whether or not we're mounted
- this.canSetState = false;
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (!utils.areObjectsEqual(nextProps, this.props)) {
- return true;
- }
-
- // the only time this object should update is when it receives an updated file size which we can usually handle without re-rendering
- if (nextState.fileSize !== this.state.fileSize) {
- if (this.refs.fileSize) {
- // update the UI element to display the file size without re-rendering the whole component
- ReactDOM.findDOMNode(this.refs.fileSize).innerHTML = utils.fileSizeToString(nextState.fileSize);
-
- return false;
- }
-
- // we can't find the element that should hold the file size so we must not have rendered yet
- return true;
- }
-
- return true;
- }
- getFileInfoFromName(name) {
- var fileInfo = utils.splitFileLocation(name);
-
- fileInfo.path = utils.getWindowLocationOrigin() + '/api/v1/files/get' + fileInfo.path;
-
- return fileInfo;
- }
- addBackgroundImage(name, path) {
- var fileUrl = path;
-
- if (name in this.refs) {
- if (!path) {
- fileUrl = this.getFileInfoFromName(name).path;
- }
-
- var imgDiv = ReactDOM.findDOMNode(this.refs[name]);
- var re1 = new RegExp(' ', 'g');
- var re2 = new RegExp('\\(', 'g');
- var re3 = new RegExp('\\)', 'g');
- var url = fileUrl.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29');
-
- $(imgDiv).css('background-image', 'url(' + url + '_thumb.jpg)');
- }
- }
- removeBackgroundImage(name) {
- if (name in this.refs) {
- $(ReactDOM.findDOMNode(this.refs[name])).css('background-image', 'initial');
- }
- }
- render() {
- var filename = this.props.filename;
-
- var fileInfo = utils.splitFileLocation(filename);
- var fileUrl = utils.getFileUrl(filename, true);
- var type = utils.getFileType(fileInfo.ext);
-
- var thumbnail;
- if (type === 'image') {
- thumbnail = (
- <div
- ref={filename}
- className='post-image__load'
- style={{backgroundImage: 'url(/static/images/load.gif)'}}
- />
- );
- } else {
- thumbnail = <div className={'file-icon ' + utils.getIconClassName(type)}/>;
- }
-
- var fileSizeString = '';
- if (this.state.fileSize < 0) {
- Client.getFileInfo(
- filename,
- function success(data) {
- if (this.canSetState) {
- this.setState({fileSize: parseInt(data.size, 10)});
- }
- }.bind(this),
- function error() {
- // Do nothing
- }
- );
- } else {
- fileSizeString = utils.fileSizeToString(this.state.fileSize);
- }
-
- var filenameString = decodeURIComponent(utils.getFileName(filename));
- var trimmedFilename;
- if (filenameString.length > 35) {
- trimmedFilename = filenameString.substring(0, Math.min(35, filenameString.length)) + '...';
- } else {
- trimmedFilename = filenameString;
- }
-
- return (
- <div
- className='post-image__column'
- key={filename}
- >
- <a className='post-image__thumbnail'
- href='#'
- onClick={() => this.props.handleImageClick(this.props.index)}
- >
- {thumbnail}
- </a>
- <div className='post-image__details'>
- <a
- href={fileUrl}
- download={filenameString}
- data-toggle='tooltip'
- title={this.props.intl.formatMessage(holders.download) + ' \"' + filenameString + '\"'}
- className='post-image__name'
- target='_blank'
- >
- {trimmedFilename}
- </a>
- <div>
- <a
- href={fileUrl}
- download={filenameString}
- className='post-image__download'
- target='_blank'
- >
- <span
- className='fa fa-download'
- />
- </a>
- <span className='post-image__type'>{fileInfo.ext.toUpperCase()}</span>
- <span className='post-image__size'>{fileSizeString}</span>
- </div>
- </div>
- </div>
- );
- }
-}
-
-FileAttachment.propTypes = {
- intl: intlShape.isRequired,
-
- // a list of file pathes displayed by the parent FileAttachmentList
- filename: React.PropTypes.string.isRequired,
-
- // the index of this attachment preview in the parent FileAttachmentList
- index: React.PropTypes.number.isRequired,
-
- // handler for when the thumbnail is clicked passed the index above
- handleImageClick: React.PropTypes.func
-};
-
-export default injectIntl(FileAttachment);
diff --git a/web/react/components/file_attachment_list.jsx b/web/react/components/file_attachment_list.jsx
deleted file mode 100644
index da1b2ba3d..000000000
--- a/web/react/components/file_attachment_list.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ViewImageModal from './view_image.jsx';
-import FileAttachment from './file_attachment.jsx';
-import Constants from '../utils/constants.jsx';
-
-export default class FileAttachmentList extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleImageClick = this.handleImageClick.bind(this);
-
- this.state = {showPreviewModal: false, startImgId: 0};
- }
- handleImageClick(indexClicked) {
- this.setState({showPreviewModal: true, startImgId: indexClicked});
- }
- render() {
- var filenames = this.props.filenames;
-
- var postFiles = [];
- for (var i = 0; i < filenames.length && i < Constants.MAX_DISPLAY_FILES; i++) {
- postFiles.push(
- <FileAttachment
- key={'file_attachment_' + i}
- filename={filenames[i]}
- index={i}
- handleImageClick={this.handleImageClick}
- />
- );
- }
-
- return (
- <div>
- <div className='post-image__columns'>
- {postFiles}
- </div>
- <ViewImageModal
- show={this.state.showPreviewModal}
- onModalDismissed={() => this.setState({showPreviewModal: false})}
- channelId={this.props.channelId}
- userId={this.props.userId}
- startId={this.state.startImgId}
- filenames={filenames}
- />
- </div>
- );
- }
-}
-
-FileAttachmentList.propTypes = {
-
- // a list of file pathes displayed by this
- filenames: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
-
- // the channel that this is part of
- channelId: React.PropTypes.string,
-
- // the user that owns the post that this is attached to
- userId: React.PropTypes.string
-};
diff --git a/web/react/components/file_info_preview.jsx b/web/react/components/file_info_preview.jsx
deleted file mode 100644
index 30c3fc97f..000000000
--- a/web/react/components/file_info_preview.jsx
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-
-import {defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- type: {
- id: 'file_info_preview.type',
- defaultMessage: 'File type '
- },
- size: {
- id: 'file_info_preview.size',
- defaultMessage: 'Size '
- }
-});
-
-export default function FileInfoPreview({filename, fileUrl, fileInfo, formatMessage}) {
- // non-image files include a section providing details about the file
- const infoParts = [];
-
- if (fileInfo.extension !== '') {
- infoParts.push(formatMessage(holders.type) + fileInfo.extension.toUpperCase());
- }
-
- infoParts.push(formatMessage(holders.size) + Utils.fileSizeToString(fileInfo.size));
-
- const infoString = infoParts.join(', ');
-
- const name = decodeURIComponent(Utils.getFileName(filename));
-
- return (
- <div className='file-details__container'>
- <a
- className={'file-details__preview'}
- href={fileUrl}
- target='_blank'
- >
- <span className='file-details__preview-helper'/>
- <img src={Utils.getPreviewImagePath(filename)}/>
- </a>
- <div className='file-details'>
- <div className='file-details__name'>{name}</div>
- <div className='file-details__info'>{infoString}</div>
- </div>
- </div>
- );
-}
-
-FileInfoPreview.propTypes = {
- filename: React.PropTypes.string.isRequired,
- fileUrl: React.PropTypes.string.isRequired,
- fileInfo: React.PropTypes.object.isRequired,
- formatMessage: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/file_preview.jsx b/web/react/components/file_preview.jsx
deleted file mode 100644
index 97949ca3d..000000000
--- a/web/react/components/file_preview.jsx
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-
-export default class FilePreview extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleRemove = this.handleRemove.bind(this);
- }
-
- componentDidUpdate() {
- if (this.props.uploadsInProgress.length > 0) {
- ReactDOM.findDOMNode(this.refs[this.props.uploadsInProgress[0]]).scrollIntoView();
- }
- }
-
- handleRemove(e) {
- var previewDiv = e.target.parentNode.parentNode;
-
- if (previewDiv.hasAttribute('data-filename')) {
- this.props.onRemove(previewDiv.getAttribute('data-filename'));
- } else if (previewDiv.hasAttribute('data-client-id')) {
- this.props.onRemove(previewDiv.getAttribute('data-client-id'));
- }
- }
-
- render() {
- var previews = [];
- this.props.files.forEach((fullFilename) => {
- var filename = fullFilename;
- var originalFilename = filename;
- var filenameSplit = filename.split('.');
- var ext = filenameSplit[filenameSplit.length - 1];
- var type = Utils.getFileType(ext);
-
- filename = Utils.getFileUrl(filename);
-
- if (type === 'image') {
- previews.push(
- <div
- key={filename}
- className='file-preview'
- data-filename={originalFilename}
- >
- <img
- className='file-preview__image'
- src={filename}
- />
- <a
- className='file-preview__remove'
- onClick={this.handleRemove}
- >
- <i className='glyphicon glyphicon-remove'/>
- </a>
- </div>
- );
- } else {
- previews.push(
- <div
- key={filename}
- className='file-preview custom-file'
- data-filename={originalFilename}
- >
- <div className={'file-icon ' + Utils.getIconClassName(type)}/>
- <a
- className='file-preview__remove'
- onClick={this.handleRemove}
- >
- <i className='glyphicon glyphicon-remove'/>
- </a>
- </div>
- );
- }
- });
-
- this.props.uploadsInProgress.forEach((clientId) => {
- previews.push(
- <div
- ref={clientId}
- key={clientId}
- className='file-preview'
- data-client-id={clientId}
- >
- <img
- className='spinner'
- src='/static/images/load.gif'
- />
- <a
- className='file-preview__remove'
- onClick={this.handleRemove}
- >
- <i className='glyphicon glyphicon-remove'/>
- </a>
- </div>
- );
- });
-
- return (
- <div className='file-preview__container'>
- {previews}
- </div>
- );
- }
-}
-
-FilePreview.defaultProps = {
- files: [],
- uploadsInProgress: []
-};
-FilePreview.propTypes = {
- onRemove: React.PropTypes.func.isRequired,
- files: React.PropTypes.array,
- uploadsInProgress: React.PropTypes.array
-};
diff --git a/web/react/components/file_upload.jsx b/web/react/components/file_upload.jsx
deleted file mode 100644
index f8965e13e..000000000
--- a/web/react/components/file_upload.jsx
+++ /dev/null
@@ -1,335 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- limited: {
- id: 'file_upload.limited',
- defaultMessage: 'Uploads limited to {count} files maximum. Please use additional posts for more files.'
- },
- filesAbove: {
- id: 'file_upload.filesAbove',
- defaultMessage: 'Files above {max}MB could not be uploaded: {filenames}'
- },
- fileAbove: {
- id: 'file_upload.fileAbove',
- defaultMessage: 'File above {max}MB could not be uploaded: {filename}'
- },
- pasted: {
- id: 'file_upload.pasted',
- defaultMessage: 'Image Pasted at '
- }
-});
-
-class FileUpload extends React.Component {
- constructor(props) {
- super(props);
-
- this.uploadFiles = this.uploadFiles.bind(this);
- this.handleChange = this.handleChange.bind(this);
- this.handleDrop = this.handleDrop.bind(this);
- this.cancelUpload = this.cancelUpload.bind(this);
-
- this.state = {
- requests: {}
- };
- }
-
- fileUploadSuccess(channelId, data) {
- this.props.onFileUpload(data.filenames, data.client_ids, channelId);
-
- var requests = this.state.requests;
- for (var j = 0; j < data.client_ids.length; j++) {
- delete requests[data.client_ids[j]];
- }
- this.setState({requests});
- }
-
- fileUploadFail(clientId, err) {
- this.props.onUploadError(err, clientId);
- }
-
- uploadFiles(files) {
- // clear any existing errors
- this.props.onUploadError(null);
-
- var channelId = this.props.channelId || ChannelStore.getCurrentId();
-
- var uploadsRemaining = Constants.MAX_UPLOAD_FILES - this.props.getFileCount(channelId);
- var numUploads = 0;
-
- // keep track of how many files have been too large
- var tooLargeFiles = [];
-
- for (let i = 0; i < files.length && numUploads < uploadsRemaining; i++) {
- if (files[i].size > Constants.MAX_FILE_SIZE) {
- tooLargeFiles.push(files[i]);
- continue;
- }
-
- // generate a unique id that can be used by other components to refer back to this upload
- const clientId = Utils.generateId();
-
- // prepare data to be uploaded
- var formData = new FormData();
- formData.append('channel_id', channelId);
- formData.append('files', files[i], files[i].name);
- formData.append('client_ids', clientId);
-
- var request = Client.uploadFile(formData,
- this.fileUploadSuccess.bind(this, channelId),
- this.fileUploadFail.bind(this, clientId)
- );
-
- var requests = this.state.requests;
- requests[clientId] = request;
- this.setState({requests});
-
- this.props.onUploadStart([clientId], channelId);
-
- numUploads += 1;
- }
-
- const {formatMessage} = this.props.intl;
- if (files.length > uploadsRemaining) {
- this.props.onUploadError(formatMessage(holders.limited, {count: Constants.MAX_UPLOAD_FILES}));
- } else if (tooLargeFiles.length > 1) {
- var tooLargeFilenames = tooLargeFiles.map((file) => file.name).join(', ');
-
- this.props.onUploadError(formatMessage(holders.filesAbove, {max: (Constants.MAX_FILE_SIZE / 1000000), filenames: tooLargeFilenames}));
- } else if (tooLargeFiles.length > 0) {
- this.props.onUploadError(formatMessage(holders.fileAbove, {max: (Constants.MAX_FILE_SIZE / 1000000), filename: tooLargeFiles[0].name}));
- }
- }
-
- handleChange(e) {
- if (e.target.files.length > 0) {
- this.uploadFiles(e.target.files);
-
- Utils.clearFileInput(e.target);
- }
- }
-
- handleDrop(e) {
- this.props.onUploadError(null);
-
- var files = e.originalEvent.dataTransfer.files;
-
- if (typeof files !== 'string' && files.length) {
- this.uploadFiles(files);
- }
- }
-
- componentDidMount() {
- var inputDiv = ReactDOM.findDOMNode(this.refs.input);
- var self = this;
- const {formatMessage} = this.props.intl;
-
- if (this.props.postType === 'post') {
- $('.row.main').dragster({
- enter(dragsterEvent, e) {
- var files = e.originalEvent.dataTransfer;
-
- if (Utils.isFileTransfer(files)) {
- $('.center-file-overlay').removeClass('hidden');
- }
- },
- leave(dragsterEvent, e) {
- var files = e.originalEvent.dataTransfer;
-
- if (Utils.isFileTransfer(files)) {
- $('.center-file-overlay').addClass('hidden');
- }
- },
- drop(dragsterEvent, e) {
- $('.center-file-overlay').addClass('hidden');
- self.handleDrop(e);
- }
- });
- } else if (this.props.postType === 'comment') {
- $('.post-right__container').dragster({
- enter(dragsterEvent, e) {
- var files = e.originalEvent.dataTransfer;
-
- if (Utils.isFileTransfer(files)) {
- $('.right-file-overlay').removeClass('hidden');
- }
- },
- leave(dragsterEvent, e) {
- var files = e.originalEvent.dataTransfer;
-
- if (Utils.isFileTransfer(files)) {
- $('.right-file-overlay').addClass('hidden');
- }
- },
- drop(dragsterEvent, e) {
- $('.right-file-overlay').addClass('hidden');
- self.handleDrop(e);
- }
- });
- }
-
- document.addEventListener('paste', (e) => {
- if (!e.clipboardData) {
- return;
- }
-
- var textarea = $(inputDiv.parentNode.parentNode).find('.custom-textarea')[0];
-
- if (textarea !== e.target && !$.contains(textarea, e.target)) {
- return;
- }
-
- self.props.onUploadError(null);
-
- // This looks redundant, but must be done this way due to
- // setState being an asynchronous call
- var items = e.clipboardData.items;
- var numItems = 0;
- if (items) {
- for (let i = 0; i < items.length; i++) {
- if (items[i].type.indexOf('image') !== -1) {
- var testExt = items[i].type.split('/')[1].toLowerCase();
-
- if (Constants.IMAGE_TYPES.indexOf(testExt) < 0) {
- continue;
- }
-
- numItems++;
- }
- }
-
- var numToUpload = Math.min(Constants.MAX_UPLOAD_FILES - self.props.getFileCount(ChannelStore.getCurrentId()), numItems);
-
- if (numItems > numToUpload) {
- self.props.onUploadError(formatMessage(holders.limited, {count: Constants.MAX_UPLOAD_FILES}));
- }
-
- for (var i = 0; i < items.length && i < numToUpload; i++) {
- if (items[i].type.indexOf('image') !== -1) {
- var file = items[i].getAsFile();
-
- var ext = items[i].type.split('/')[1].toLowerCase();
-
- if (Constants.IMAGE_TYPES.indexOf(ext) < 0) {
- continue;
- }
-
- var channelId = self.props.channelId || ChannelStore.getCurrentId();
-
- // generate a unique id that can be used by other components to refer back to this file upload
- var clientId = Utils.generateId();
-
- var formData = new FormData();
- formData.append('channel_id', channelId);
- var d = new Date();
- var hour;
- if (d.getHours() < 10) {
- hour = '0' + d.getHours();
- } else {
- hour = String(d.getHours());
- }
- var min;
- if (d.getMinutes() < 10) {
- min = '0' + d.getMinutes();
- } else {
- min = String(d.getMinutes());
- }
-
- var name = formatMessage(holders.pasted) + d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate() + ' ' + hour + '-' + min + '.' + ext;
- formData.append('files', file, name);
- formData.append('client_ids', clientId);
-
- var request = Client.uploadFile(formData,
- self.fileUploadSuccess.bind(self, channelId),
- self.fileUploadFail.bind(self, clientId)
- );
-
- var requests = self.state.requests;
- requests[clientId] = request;
- self.setState({requests});
-
- self.props.onUploadStart([clientId], channelId);
- }
- }
- }
- });
- }
-
- componentWillUnmount() {
- let target;
- if (this.props.postType === 'post') {
- target = $('.row.main');
- } else {
- target = $('.post-right__container');
- }
-
- // jquery-dragster doesn't provide a function to unregister itself so do it manually
- target.off('dragenter dragleave dragover drop dragster:enter dragster:leave dragster:over dragster:drop');
- }
-
- cancelUpload(clientId) {
- var requests = this.state.requests;
- var request = requests[clientId];
-
- if (request) {
- request.abort();
-
- delete requests[clientId];
- this.setState({requests});
- }
- }
-
- render() {
- let multiple = true;
- if (Utils.isMobileApp()) {
- // iOS WebViews don't upload videos properly in multiple mode
- multiple = false;
- }
-
- let accept = '';
- if (Utils.isIosChrome()) {
- // iOS Chrome can't upload videos at all
- accept = 'image/*';
- }
-
- return (
- <span
- ref='input'
- className='btn btn-file'
- >
- <span>
- <i className='glyphicon glyphicon-paperclip'/>
- </span>
- <input
- ref='fileInput'
- type='file'
- onChange={this.handleChange}
- onClick={this.props.onClick}
- multiple={multiple}
- accept={accept}
- />
- </span>
- );
- }
-}
-
-FileUpload.propTypes = {
- intl: intlShape.isRequired,
- onUploadError: React.PropTypes.func,
- getFileCount: React.PropTypes.func,
- onClick: React.PropTypes.func,
- onFileUpload: React.PropTypes.func,
- onUploadStart: React.PropTypes.func,
- onTextDrop: React.PropTypes.func,
- channelId: React.PropTypes.string,
- postType: React.PropTypes.string
-};
-
-export default injectIntl(FileUpload, {withRef: true});
diff --git a/web/react/components/file_upload_overlay.jsx b/web/react/components/file_upload_overlay.jsx
deleted file mode 100644
index 497d5aee2..000000000
--- a/web/react/components/file_upload_overlay.jsx
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class FileUploadOverlay extends React.Component {
- render() {
- var overlayClass = 'file-overlay hidden';
- if (this.props.overlayType === 'right') {
- overlayClass += ' right-file-overlay';
- } else if (this.props.overlayType === 'center') {
- overlayClass += ' center-file-overlay';
- }
-
- return (
- <div className={overlayClass}>
- <div className='overlay__indent'>
- <div className='overlay__circle'>
- <img
- className='overlay__files'
- src='/static/images/filesOverlay.png'
- alt='Files'
- />
- <span><i className='fa fa-upload'></i>
- <FormattedMessage
- id='upload_overlay.info'
- defaultMessage='Drop a file to upload it.'
- />
- </span>
- <img
- className='overlay__logo'
- src='/static/images/logoWhite.png'
- width='100'
- alt='Logo'
- />
- </div>
- </div>
- </div>
- );
- }
-}
-
-FileUploadOverlay.propTypes = {
- overlayType: React.PropTypes.string
-};
diff --git a/web/react/components/filtered_user_list.jsx b/web/react/components/filtered_user_list.jsx
deleted file mode 100644
index 77a8c8810..000000000
--- a/web/react/components/filtered_user_list.jsx
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserList from './user_list.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- member: {
- id: 'filtered_user_list.member',
- defaultMessage: 'Member'
- },
- search: {
- id: 'filtered_user_list.search',
- defaultMessage: 'Search members'
- }
-});
-
-class FilteredUserList extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleFilterChange = this.handleFilterChange.bind(this);
-
- this.state = {
- filter: ''
- };
- }
-
- componentDidUpdate(prevProps, prevState) {
- if (prevState.filter !== this.state.filter) {
- $(ReactDOM.findDOMNode(this.refs.userList)).scrollTop(0);
- }
- }
-
- handleFilterChange(e) {
- this.setState({
- filter: e.target.value
- });
- }
-
- render() {
- const {formatMessage} = this.props.intl;
-
- let users = this.props.users;
-
- if (this.state.filter) {
- const filter = this.state.filter.toLowerCase();
-
- users = users.filter((user) => {
- return user.username.toLowerCase().indexOf(filter) !== -1 ||
- (user.first_name && user.first_name.toLowerCase().indexOf(filter) !== -1) ||
- (user.last_name && user.last_name.toLowerCase().indexOf(filter) !== -1) ||
- (user.nickname && user.nickname.toLowerCase().indexOf(filter) !== -1);
- });
- }
-
- let count;
- if (users.length === this.props.users.length) {
- count = (
- <FormattedMessage
- id='filtered_user_list.count'
- defaultMessage='{count} {count, plural,
- one {member}
- other {members}
- }'
- values={{
- count: users.length
- }}
- />
- );
- } else {
- count = (
- <FormattedMessage
- id='filtered_user_list.countTotal'
- defaultMessage='{count} {count, plural,
- one {member}
- other {members}
- } of {total} Total'
- values={{
- count: users.length,
- total: this.props.users.length
- }}
- />
- );
- }
-
- return (
- <div
- className='filtered-user-list'
- style={this.props.style}
- >
- <div className='filter-row'>
- <div className='col-sm-6'>
- <input
- ref='filter'
- className='form-control filter-textbox'
- placeholder={formatMessage(holders.search)}
- onInput={this.handleFilterChange}
- />
- </div>
- <div className='col-sm-6'>
- <span className='member-count'>{count}</span>
- </div>
- </div>
- <div
- ref='userList'
- className='more-modal__list'
- >
- <UserList
- users={users}
- actions={this.props.actions}
- />
- </div>
- </div>
- );
- }
-}
-
-FilteredUserList.defaultProps = {
- users: [],
- actions: []
-};
-
-FilteredUserList.propTypes = {
- intl: intlShape.isRequired,
- users: React.PropTypes.arrayOf(React.PropTypes.object),
- actions: React.PropTypes.arrayOf(React.PropTypes.func),
- style: React.PropTypes.object
-};
-
-export default injectIntl(FilteredUserList);
diff --git a/web/react/components/get_link_modal.jsx b/web/react/components/get_link_modal.jsx
deleted file mode 100644
index dd21f21e0..000000000
--- a/web/react/components/get_link_modal.jsx
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-export default class GetLinkModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.onHide = this.onHide.bind(this);
-
- this.copyLink = this.copyLink.bind(this);
-
- this.state = {
- copiedLink: false
- };
- }
-
- onHide() {
- this.setState({copiedLink: false});
-
- this.props.onHide();
- }
-
- copyLink() {
- var copyTextarea = $(ReactDOM.findDOMNode(this.refs.textarea));
- copyTextarea.select();
-
- try {
- var successful = document.execCommand('copy');
- if (successful) {
- this.setState({copiedLink: true});
- } else {
- this.setState({copiedLink: false});
- }
- } catch (err) {
- this.setState({copiedLink: false});
- }
- }
-
- render() {
- let helpText = null;
- if (this.props.helpText) {
- helpText = (
- <p>
- {this.props.helpText}
- <br/>
- <br/>
- </p>
- );
- }
-
- let copyLink = null;
- if (document.queryCommandSupported('copy')) {
- copyLink = (
- <button
- data-copy-btn='true'
- type='button'
- className='btn btn-primary pull-left'
- onClick={this.copyLink}
- >
- <FormattedMessage
- id='get_link.copy'
- defaultMessage='Copy Link'
- />
- </button>
- );
- }
-
- const linkText = (
- <textarea
- className='form-control no-resize min-height'
- readOnly='true'
- ref='textarea'
- value={this.props.link}
- />
- );
-
- var copyLinkConfirm = null;
- if (this.state.copiedLink) {
- copyLinkConfirm = (
- <p className='alert alert-success alert--confirm'>
- <i className='fa fa-check'></i>
- <FormattedMessage
- id='get_link.clipboard'
- defaultMessage=' Link copied to clipboard.'
- />
- </p>
- );
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.onHide}
- >
- <Modal.Header closeButton={true}>
- <h4 className='modal-title'>{this.props.title}</h4>
- </Modal.Header>
- <Modal.Body>
- {helpText}
- {linkText}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.onHide}
- >
- <FormattedMessage
- id='get_link.close'
- defaultMessage='Close'
- />
- </button>
- {copyLink}
- {copyLinkConfirm}
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-GetLinkModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- title: React.PropTypes.string.isRequired,
- helpText: React.PropTypes.string,
- link: React.PropTypes.string.isRequired
-};
-
-GetLinkModal.defaultProps = {
- helpText: null
-};
diff --git a/web/react/components/get_post_link_modal.jsx b/web/react/components/get_post_link_modal.jsx
deleted file mode 100644
index b7b2a8e7c..000000000
--- a/web/react/components/get_post_link_modal.jsx
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../utils/constants.jsx';
-import GetLinkModal from './get_link_modal.jsx';
-import ModalStore from '../stores/modal_store.jsx';
-import TeamStore from '../stores/team_store.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- title: {
- id: 'get_post_link_modal.title',
- defaultMessage: 'Copy Permalink'
- },
- help: {
- id: 'get_post_link_modal.help',
- defaultMessage: 'The link below allows authorized users to see your post.'
- }
-});
-
-class GetPostLinkModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleToggle = this.handleToggle.bind(this);
-
- this.hide = this.hide.bind(this);
-
- this.state = {
- show: false,
- post: {}
- };
- }
-
- componentDidMount() {
- ModalStore.addModalListener(Constants.ActionTypes.TOGGLE_GET_POST_LINK_MODAL, this.handleToggle);
- }
-
- componentWillUnmount() {
- ModalStore.removeModalListener(Constants.ActionTypes.TOGGLE_GET_POST_LINK_MODAL, this.handleToggle);
- }
-
- handleToggle(value, args) {
- this.setState({
- show: value,
- post: args.post
- });
- }
-
- hide() {
- this.setState({
- show: false
- });
- }
-
- render() {
- const {formatMessage} = this.props.intl;
-
- return (
- <GetLinkModal
- show={this.state.show}
- onHide={this.hide}
- title={formatMessage(holders.title)}
- helpText={formatMessage(holders.help)}
- link={TeamStore.getCurrentTeamUrl() + '/pl/' + this.state.post.id}
- />
- );
- }
-}
-
-GetPostLinkModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(GetPostLinkModal);
diff --git a/web/react/components/get_team_invite_link_modal.jsx b/web/react/components/get_team_invite_link_modal.jsx
deleted file mode 100644
index ba6164dbf..000000000
--- a/web/react/components/get_team_invite_link_modal.jsx
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../utils/constants.jsx';
-import GetLinkModal from './get_link_modal.jsx';
-import ModalStore from '../stores/modal_store.jsx';
-import TeamStore from '../stores/team_store.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- title: {
- id: 'get_team_invite_link_modal.title',
- defaultMessage: 'Team Invite Link'
- },
- help: {
- id: 'get_team_invite_link_modal.help',
- defaultMessage: 'Send teammates the link below for them to sign-up to this team site. The Team Invite Link can be shared with multiple teammates as it does not change unless it\'s regenerated in Team Settings by a Team Admin.'
- },
- helpDisabled: {
- id: 'get_team_invite_link_modal.helpDisabled',
- defaultMessage: 'User creation has been disabled for your team. Please ask your team administrator for details.'
- }
-});
-
-class GetTeamInviteLinkModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleToggle = this.handleToggle.bind(this);
-
- this.state = {
- show: false
- };
- }
-
- componentDidMount() {
- ModalStore.addModalListener(Constants.ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL, this.handleToggle);
- }
-
- componentWillUnmount() {
- ModalStore.removeModalListener(Constants.ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL, this.handleToggle);
- }
-
- handleToggle(value) {
- this.setState({
- show: value
- });
- }
-
- render() {
- const {formatMessage} = this.props.intl;
-
- let helpText = formatMessage(holders.helpDisabled);
-
- if (global.window.mm_config.EnableUserCreation === 'true') {
- helpText = formatMessage(holders.help);
- }
-
- return (
- <GetLinkModal
- show={this.state.show}
- onHide={() => this.setState({show: false})}
- title={formatMessage(holders.title)}
- helpText={helpText}
- link={TeamStore.getCurrentInviteLink()}
- />
- );
- }
-}
-
-GetTeamInviteLinkModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(GetTeamInviteLinkModal);
diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx
deleted file mode 100644
index 71cd5b8b6..000000000
--- a/web/react/components/invite_member_modal.jsx
+++ /dev/null
@@ -1,515 +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';
-const ActionTypes = Constants.ActionTypes;
-import * as Client from '../utils/client.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import ModalStore from '../stores/modal_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import TeamStore from '../stores/team_store.jsx';
-import ConfirmModal from './confirm_modal.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-const holders = defineMessages({
- emailError: {
- id: 'invite_member.emailError',
- defaultMessage: 'Please enter a valid email address'
- },
- firstname: {
- id: 'invite_member.firstname',
- defaultMessage: 'First name'
- },
- lastname: {
- id: 'invite_member.lastname',
- defaultMessage: 'Last name'
- },
- modalTitle: {
- id: 'invite_member.modalTitle',
- defaultMessage: 'Discard Invitations?'
- },
- modalMessage: {
- id: 'invite_member.modalMessage',
- defaultMessage: 'You have unsent invitations, are you sure you want to discard them?'
- },
- modalButton: {
- id: 'invite_member.modalButton',
- defaultMessage: 'Yes, Discard'
- }
-});
-
-class InviteMemberModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleToggle = this.handleToggle.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleHide = this.handleHide.bind(this);
- this.addInviteFields = this.addInviteFields.bind(this);
- this.clearFields = this.clearFields.bind(this);
- this.removeInviteFields = this.removeInviteFields.bind(this);
- this.showGetTeamInviteLinkModal = this.showGetTeamInviteLinkModal.bind(this);
-
- this.state = {
- show: false,
- inviteIds: [0],
- idCount: 0,
- emailErrors: {},
- firstNameErrors: {},
- lastNameErrors: {},
- emailEnabled: global.window.mm_config.SendEmailNotifications === 'true',
- userCreationEnabled: global.window.mm_config.EnableUserCreation === 'true',
- showConfirmModal: false,
- isSendingEmails: false
- };
- }
-
- componentDidMount() {
- ModalStore.addModalListener(ActionTypes.TOGGLE_INVITE_MEMBER_MODAL, this.handleToggle);
- }
-
- componentWillUnmount() {
- ModalStore.removeModalListener(ActionTypes.TOGGLE_INVITE_MEMBER_MODAL, this.handleToggle);
- }
-
- handleToggle(value) {
- this.setState({
- show: value
- });
- }
-
- handleSubmit() {
- if (!this.state.emailEnabled) {
- return;
- }
-
- var inviteIds = this.state.inviteIds;
- var count = inviteIds.length;
- var invites = [];
- var emailErrors = this.state.emailErrors;
- var firstNameErrors = this.state.firstNameErrors;
- var lastNameErrors = this.state.lastNameErrors;
- var valid = true;
-
- for (var i = 0; i < count; i++) {
- var index = inviteIds[i];
- var invite = {};
- invite.email = ReactDOM.findDOMNode(this.refs['email' + index]).value.trim();
- if (!invite.email || !utils.isEmail(invite.email)) {
- emailErrors[index] = this.props.intl.formatMessage(holders.emailError);
- valid = false;
- } else {
- emailErrors[index] = '';
- }
-
- invite.firstName = ReactDOM.findDOMNode(this.refs['first_name' + index]).value.trim();
-
- invite.lastName = ReactDOM.findDOMNode(this.refs['last_name' + index]).value.trim();
-
- invites.push(invite);
- }
-
- this.setState({emailErrors: emailErrors, firstNameErrors: firstNameErrors, lastNameErrors: lastNameErrors});
-
- if (!valid || invites.length === 0) {
- return;
- }
-
- var data = {};
- data.invites = invites;
-
- this.setState({isSendingEmails: true});
-
- Client.inviteMembers(
- data,
- () => {
- this.handleHide(false);
- this.setState({isSendingEmails: false});
- },
- (err) => {
- if (err.id === 'api.team.invite_members.already.app_error') {
- emailErrors[err.detailed_error] = err.message;
- this.setState({emailErrors: emailErrors});
- } else {
- this.setState({serverError: err.message});
- }
-
- this.setState({isSendingEmails: false});
- }
- );
- }
-
- handleHide(requireConfirm) {
- if (requireConfirm) {
- var notEmpty = false;
- for (var i = 0; i < this.state.inviteIds.length; i++) {
- var index = this.state.inviteIds[i];
- if (ReactDOM.findDOMNode(this.refs['email' + index]).value.trim() !== '') {
- notEmpty = true;
- break;
- }
- }
-
- if (notEmpty) {
- this.setState({
- showConfirmModal: true
- });
-
- return;
- }
- }
-
- this.clearFields();
-
- this.setState({
- show: false,
- showConfirmModal: false
- });
- }
-
- componentDidUpdate(prevProps, prevState) {
- if (!prevState.show && this.state.show) {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
- if ($(window).width() > 768) {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
- }
- }
- }
-
- addInviteFields() {
- var count = this.state.idCount + 1;
- var inviteIds = this.state.inviteIds;
- inviteIds.push(count);
- this.setState({inviteIds: inviteIds, idCount: count});
- }
-
- clearFields() {
- var inviteIds = this.state.inviteIds;
-
- for (var i = 0; i < inviteIds.length; i++) {
- var index = inviteIds[i];
- ReactDOM.findDOMNode(this.refs['email' + index]).value = '';
- ReactDOM.findDOMNode(this.refs['first_name' + index]).value = '';
- ReactDOM.findDOMNode(this.refs['last_name' + index]).value = '';
- }
-
- this.setState({
- inviteIds: [0],
- idCount: 0,
- emailErrors: {},
- firstNameErrors: {},
- lastNameErrors: {}
- });
- }
-
- removeInviteFields(index) {
- var count = this.state.idCount;
- var inviteIds = this.state.inviteIds;
- var i = inviteIds.indexOf(index);
- if (i > -1) {
- inviteIds.splice(i, 1);
- }
- if (!inviteIds.length) {
- inviteIds.push(++count);
- }
- this.setState({inviteIds: inviteIds, idCount: count});
- }
-
- showGetTeamInviteLinkModal() {
- this.handleHide(false);
-
- GlobalActions.showGetTeamInviteLinkModal();
- }
-
- render() {
- var currentUser = UserStore.getCurrentUser();
- const {formatMessage} = this.props.intl;
-
- if (currentUser != null) {
- var inviteSections = [];
- var inviteIds = this.state.inviteIds;
- for (var i = 0; i < inviteIds.length; i++) {
- var index = inviteIds[i];
- var emailError = null;
- if (this.state.emailErrors[index]) {
- emailError = <label className='control-label'>{this.state.emailErrors[index]}</label>;
- }
- var firstNameError = null;
- if (this.state.firstNameErrors[index]) {
- firstNameError = <label className='control-label'>{this.state.firstNameErrors[index]}</label>;
- }
- var lastNameError = null;
- if (this.state.lastNameErrors[index]) {
- lastNameError = <label className='control-label'>{this.state.lastNameErrors[index]}</label>;
- }
-
- var removeButton = null;
- if (index) {
- removeButton = (<div>
- <button
- type='button'
- className='btn btn-link remove__member'
- onClick={this.removeInviteFields.bind(this, index)}
- >
- <span className='fa fa-trash'></span>
- </button>
- </div>);
- }
- var emailClass = 'form-group invite';
- if (emailError) {
- emailClass += ' has-error';
- }
-
- var nameFields = null;
-
- var firstNameClass = 'form-group';
- if (firstNameError) {
- firstNameClass += ' has-error';
- }
- var lastNameClass = 'form-group';
- if (lastNameError) {
- lastNameClass += ' has-error';
- }
- nameFields = (<div className='row--invite'>
- <div className='col-sm-6'>
- <div className={firstNameClass}>
- <input
- type='text'
- className='form-control'
- ref={'first_name' + index}
- placeholder={formatMessage(holders.firstname)}
- maxLength='64'
- disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
- spellCheck='false'
- />
- {firstNameError}
- </div>
- </div>
- <div className='col-sm-6'>
- <div className={lastNameClass}>
- <input
- type='text'
- className='form-control'
- ref={'last_name' + index}
- placeholder={formatMessage(holders.lastname)}
- maxLength='64'
- disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
- spellCheck='false'
- />
- {lastNameError}
- </div>
- </div>
- </div>);
-
- inviteSections[index] = (
- <div key={'key' + index}>
- {removeButton}
- <div className={emailClass}>
- <input
- onKeyUp={this.displayNameKeyUp}
- type='text'
- ref={'email' + index}
- className='form-control'
- placeholder='email@domain.com'
- maxLength='64'
- disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
- spellCheck='false'
- />
- {emailError}
- </div>
- {nameFields}
- </div>
- );
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var content = null;
- var sendButton = null;
-
- var defaultChannelName = '';
- if (ChannelStore.getByName(Constants.DEFAULT_CHANNEL)) {
- defaultChannelName = ChannelStore.getByName(Constants.DEFAULT_CHANNEL).display_name;
- }
-
- if (this.state.emailEnabled && this.state.userCreationEnabled) {
- content = (
- <div>
- {serverError}
- <button
- type='button'
- className='btn btn-default'
- onClick={this.addInviteFields}
- >
- <FormattedMessage
- id='invite_member.addAnother'
- defaultMessage='Add another'
- />
- </button>
- <br/>
- <br/>
- <span>
- <FormattedHTMLMessage
- id='invite_member.autoJoin'
- defaultMessage='People invited automatically join the <strong>{channel}</strong> channel.'
- values={{
- channel: defaultChannelName
- }}
- />
- </span>
- </div>
- );
-
- var sendButtonLabel = (
- <FormattedMessage
- id='invite_member.send'
- defaultMessage='Send Invitation'
- />
- );
- if (this.state.isSendingEmails) {
- sendButtonLabel = (
- <span><i className='fa fa-spinner fa-spin'/>
- <FormattedMessage
- id='invite_member.sending'
- defaultMessage=' Sending'
- />
- </span>
- );
- } else if (this.state.inviteIds.length > 1) {
- sendButtonLabel = (
- <FormattedMessage
- id='invite_member.send2'
- defaultMessage='Send Invitations'
- />
- );
- }
-
- sendButton = (
- <button
- onClick={this.handleSubmit}
- type='button'
- className='btn btn-primary'
- disabled={this.state.isSendingEmails}
- >
- {sendButtonLabel}
- </button>
- );
- } else if (this.state.userCreationEnabled) {
- var teamInviteLink = null;
- if (currentUser && TeamStore.getCurrent().type === 'O') {
- var link = (
- <a
- href='#'
- onClick={this.showGetTeamInviteLinkModal}
- >
- <FormattedMessage
- id='invite_member.inviteLink'
- defaultMessage='Team Invite Link'
- />
- </a>
- );
-
- teamInviteLink = (
- <p>
- <FormattedMessage
- id='invite_member.teamInviteLink'
- defaultMessage='You can also invite people using the {link}.'
- values={{
- link: (link)
- }}
- />
- </p>
- );
- }
-
- content = (
- <div>
- <p>
- <FormattedMessage
- id='invite_member.content'
- defaultMessage='Email is currently disabled for your team, and email invitations cannot be sent. Contact your system administrator to enable email and email invitations.'
- />
- </p>
- {teamInviteLink}
- </div>
- );
- } else {
- content = (
- <div>
- <p>
- <FormattedMessage
- id='invite_member.disabled'
- defaultMessage='User creation has been disabled for your team. Please ask your team administrator for details.'
- />
- </p>
- </div>
- );
- }
-
- return (
- <div>
- <Modal
- dialogClassName='modal-invite-member'
- show={this.state.show}
- onHide={this.handleHide.bind(this, true)}
- enforceFocus={!this.state.showConfirmModal}
- backdrop={this.state.isSendingEmails ? 'static' : true}
- >
- <Modal.Header closeButton={!this.state.isSendingEmails}>
- <Modal.Title>
- <FormattedMessage
- id='invite_member.newMember'
- defaultMessage='Invite New Member'
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body ref='modalBody'>
- <form role='form'>
- {inviteSections}
- </form>
- {content}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.handleHide.bind(this, true)}
- disabled={this.state.isSendingEmails}
- >
- <FormattedMessage
- id='invite_member.cancel'
- defaultMessage='Cancel'
- />
- </button>
- {sendButton}
- </Modal.Footer>
- </Modal>
- <ConfirmModal
- title={formatMessage(holders.modalTitle)}
- message={formatMessage(holders.modalMessage)}
- confirmButton={formatMessage(holders.modalButton)}
- show={this.state.showConfirmModal}
- onConfirm={this.handleHide.bind(this, false)}
- onCancel={() => this.setState({showConfirmModal: false})}
- />
- </div>
- );
- }
-
- return null;
- }
-}
-
-InviteMemberModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(InviteMemberModal);
diff --git a/web/react/components/loading_screen.jsx b/web/react/components/loading_screen.jsx
deleted file mode 100644
index 143b94467..000000000
--- a/web/react/components/loading_screen.jsx
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class LoadingScreen extends React.Component {
- constructor(props) {
- super(props);
- this.state = {};
- }
- render() {
- return (
- <div
- className='loading-screen'
- style={{position: this.props.position}}
- >
- <div className='loading__content'>
- <h3>
- <FormattedMessage
- id='loading_screen.loading'
- defaultMessage='Loading'
- />
- </h3>
- <div className='round round-1'></div>
- <div className='round round-2'></div>
- <div className='round round-3'></div>
- </div>
- </div>
- );
- }
-}
-
-LoadingScreen.defaultProps = {
- position: 'relative'
-};
-LoadingScreen.propTypes = {
- position: React.PropTypes.oneOf(['absolute', 'fixed', 'relative', 'static', 'inherit'])
-};
diff --git a/web/react/components/logged_in.jsx b/web/react/components/logged_in.jsx
deleted file mode 100644
index 1ed3694e9..000000000
--- a/web/react/components/logged_in.jsx
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import UserStore from '../stores/user_store.jsx';
-import SocketStore from '../stores/socket_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-import Constants from '../utils/constants.jsx';
-import ErrorBar from '../components/error_bar.jsx';
-
-import {browserHistory} from 'react-router';
-
-import SidebarRight from '../components/sidebar_right.jsx';
-import SidebarRightMenu from '../components/sidebar_right_menu.jsx';
-
-// Modals
-import GetPostLinkModal from '../components/get_post_link_modal.jsx';
-import GetTeamInviteLinkModal from '../components/get_team_invite_link_modal.jsx';
-import EditPostModal from '../components/edit_post_modal.jsx';
-import DeletePostModal from '../components/delete_post_modal.jsx';
-import MoreChannelsModal from '../components/more_channels.jsx';
-import TeamSettingsModal from '../components/team_settings_modal.jsx';
-import RemovedFromChannelModal from '../components/removed_from_channel_modal.jsx';
-import RegisterAppModal from '../components/register_app_modal.jsx';
-import ImportThemeModal from '../components/user_settings/import_theme_modal.jsx';
-import InviteMemberModal from '../components/invite_member_modal.jsx';
-import SelectTeamModal from '../components/admin_console/select_team_modal.jsx';
-
-const CLIENT_STATUS_INTERVAL = 30000;
-const BACKSPACE_CHAR = 8;
-
-export default class LoggedIn extends React.Component {
- constructor(params) {
- super(params);
-
- this.onUserChanged = this.onUserChanged.bind(this);
- }
- onUserChanged() {
- // Grab the current user
- const user = UserStore.getCurrentUser();
-
- // Update segment indentify
- if (global.window.mm_config.SegmentDeveloperKey != null && global.window.mm_config.SegmentDeveloperKey !== '') {
- global.window.analytics.identify(user.id, {
- name: user.nickname,
- email: user.email,
- createdAt: user.create_at,
- username: user.username,
- team_id: user.team_id,
- id: user.id
- });
- }
-
- // Update CSS classes to match user theme
- if (user) {
- if ($.isPlainObject(user.theme_props) && !$.isEmptyObject(user.theme_props)) {
- Utils.applyTheme(user.theme_props);
- } else {
- Utils.applyTheme(Constants.THEMES.default);
- }
- }
- }
- onSocketChange(msg) {
- if (msg && msg.user_id && msg.user_id !== UserStore.getCurrentId()) {
- UserStore.setStatus(msg.user_id, 'online');
- }
- }
- componentWillMount() {
- // Emit view action
- GlobalActions.viewLoggedIn();
-
- // Listen for user
- UserStore.addChangeListener(this.onUserChanged);
-
- // Add listner for socker store
- SocketStore.addChangeListener(this.onSocketChange);
-
- // Get all statuses regularally. (Soon to be switched to websocket)
- this.intervalId = setInterval(() => AsyncClient.getStatuses(), CLIENT_STATUS_INTERVAL);
-
- // Force logout of all tabs if one tab is logged out
- $(window).bind('storage', (e) => {
- // when one tab on a browser logs out, it sets __logout__ in localStorage to trigger other tabs to log out
- if (e.originalEvent.key === '__logout__' && e.originalEvent.storageArea === localStorage && e.originalEvent.newValue) {
- // make sure it isn't this tab that is sending the logout signal (only necessary for IE11)
- if (window.BrowserStore.isSignallingLogout(e.originalEvent.newValue)) {
- return;
- }
-
- console.log('detected logout from a different tab'); //eslint-disable-line no-console
- browserHistory.push('/' + this.props.params.team);
- }
-
- if (e.originalEvent.key === '__login__' && e.originalEvent.storageArea === localStorage && e.originalEvent.newValue) {
- // make sure it isn't this tab that is sending the logout signal (only necessary for IE11)
- if (window.BrowserStore.isSignallingLogin(e.originalEvent.newValue)) {
- return;
- }
-
- console.log('detected login from a different tab'); //eslint-disable-line no-console
- location.reload();
- }
- });
-
- // Because current CSS requires the root tag to have specific stuff
- $('#root').attr('class', 'channel-view');
-
- // ???
- $('body').on('mouseenter mouseleave', '.post', function mouseOver(ev) {
- if (ev.type === 'mouseenter') {
- $(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--after');
- $(this).parent('div').next('.date-separator, .new-separator').addClass('hovered--before');
- } else {
- $(this).parent('div').prev('.date-separator, .new-separator').removeClass('hovered--after');
- $(this).parent('div').next('.date-separator, .new-separator').removeClass('hovered--before');
- }
- });
-
- $('body').on('mouseenter mouseleave', '.search-item__container .post', function mouseOver(ev) {
- if (ev.type === 'mouseenter') {
- $(this).closest('.search-item__container').find('.date-separator').addClass('hovered--after');
- $(this).closest('.search-item__container').next('div').find('.date-separator').addClass('hovered--before');
- } else {
- $(this).closest('.search-item__container').find('.date-separator').removeClass('hovered--after');
- $(this).closest('.search-item__container').next('div').find('.date-separator').removeClass('hovered--before');
- }
- });
-
- $('body').on('mouseenter mouseleave', '.post.post--comment.same--root', function mouseOver(ev) {
- if (ev.type === 'mouseenter') {
- $(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--comment');
- $(this).parent('div').next('.date-separator, .new-separator').addClass('hovered--comment');
- } else {
- $(this).parent('div').prev('.date-separator, .new-separator').removeClass('hovered--comment');
- $(this).parent('div').next('.date-separator, .new-separator').removeClass('hovered--comment');
- }
- });
-
- // Device tracking setup
- var iOS = (/(iPad|iPhone|iPod)/g).test(navigator.userAgent);
- if (iOS) {
- $('body').addClass('ios');
- }
-
- // Set up tracking for whether the window is active
- window.isActive = true;
- $(window).on('focus', () => {
- AsyncClient.updateLastViewedAt();
- ChannelStore.resetCounts(ChannelStore.getCurrentId());
- ChannelStore.emitChange();
- window.isActive = true;
- });
- $(window).on('blur', () => {
- window.isActive = false;
- });
-
- // if preferences have already been stored in local storage do not wait until preference store change is fired and handled in channel.jsx
- const selectedFont = PreferenceStore.get(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'selected_font', Constants.DEFAULT_FONT);
- Utils.applyFont(selectedFont);
-
- // Pervent backspace from navigating back a page
- $(window).on('keydown.preventBackspace', (e) => {
- if (e.which === BACKSPACE_CHAR && !$(e.target).is('input, textarea')) {
- e.preventDefault();
- }
- });
- }
- componentWillUnmount() {
- $('#root').attr('class', '');
- clearInterval(this.intervalId);
-
- $(window).off('focus');
- $(window).off('blur');
-
- SocketStore.removeChangeListener(this.onSocketChange);
- UserStore.removeChangeListener(this.onUserChanged);
-
- $('body').off('click.userpopover');
- $('body').off('mouseenter mouseleave', '.post');
- $('body').off('mouseenter mouseleave', '.post.post--comment.same--root');
-
- $('.modal').off('show.bs.modal');
-
- $(window).off('keydown.preventBackspace');
- }
- render() {
- return (
- <div className='channel-view'>
- <ErrorBar/>
- <div className='container-fluid'>
- <SidebarRight/>
- <SidebarRightMenu/>
- {this.props.sidebar}
- {this.props.center}
-
- <GetPostLinkModal/>
- <GetTeamInviteLinkModal/>
- <InviteMemberModal/>
- <ImportThemeModal/>
- <TeamSettingsModal/>
- <MoreChannelsModal/>
- <EditPostModal/>
- <DeletePostModal/>
- <RemovedFromChannelModal/>
- <RegisterAppModal/>
- <SelectTeamModal/>
- </div>
- </div>
- );
- }
-}
-
-LoggedIn.defaultProps = {
-};
-
-LoggedIn.propTypes = {
- children: React.PropTypes.object,
- sidebar: React.PropTypes.object,
- center: React.PropTypes.object,
- params: React.PropTypes.object
-};
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
deleted file mode 100644
index d3ee35082..000000000
--- a/web/react/components/login.jsx
+++ /dev/null
@@ -1,302 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import LoginEmail from './login_email.jsx';
-import LoginUsername from './login_username.jsx';
-import LoginLdap from './login_ldap.jsx';
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-import TeamStore from '../stores/team_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-import {browserHistory} from 'react-router';
-
-export default class Login extends React.Component {
- constructor(props) {
- super(props);
-
- this.getStateFromStores = this.getStateFromStores.bind(this);
- this.onTeamChange = this.onTeamChange.bind(this);
-
- this.state = this.getStateFromStores();
- }
- componentDidMount() {
- TeamStore.addChangeListener(this.onTeamChange);
- Client.getMeLoggedIn((data) => {
- if (data && data.logged_in !== 'false') {
- browserHistory.push('/' + this.props.params.team + '/channels/town-square');
- }
- });
- }
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.onTeamChange);
- }
- getStateFromStores() {
- return {
- currentTeam: TeamStore.getByName(this.props.params.team)
- };
- }
- onTeamChange() {
- this.setState(this.getStateFromStores());
- }
- render() {
- const currentTeam = this.state.currentTeam;
- if (currentTeam == null) {
- return <div/>;
- }
-
- const teamDisplayName = currentTeam.display_name;
- const teamName = currentTeam.name;
- const ldapEnabled = global.window.mm_config.EnableLdap === 'true';
- const usernameSigninEnabled = global.window.mm_config.EnableSignInWithUsername === 'true';
-
- let loginMessage = [];
- if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
- loginMessage.push(
- <a
- className='btn btn-custom-login gitlab'
- key='gitlab'
- href={'/api/v1/oauth/gitlab/login?team=' + encodeURIComponent(teamName)}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='login.gitlab'
- defaultMessage='with GitLab'
- />
- </span>
- </a>
- );
- }
-
- if (global.window.mm_config.EnableSignUpWithGoogle === 'true') {
- loginMessage.push(
- <a
- className='btn btn-custom-login google'
- key='google'
- href={'/api/v1/oauth/google/login?team=' + encodeURIComponent(teamName)}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='login.google'
- defaultMessage='with Google Apps'
- />
- </span>
- </a>
- );
- }
-
- const extraParam = Utils.getUrlParameter('extra');
- let extraBox = '';
- if (extraParam) {
- if (extraParam === Constants.SIGNIN_CHANGE) {
- extraBox = (
- <div className='alert alert-success'>
- <i className='fa fa-check'/>
- <FormattedMessage
- id='login.changed'
- defaultMessage=' Sign-in method changed successfully'
- />
- </div>
- );
- } else if (extraParam === Constants.SIGNIN_VERIFIED) {
- extraBox = (
- <div className='alert alert-success'>
- <i className='fa fa-check'/>
- <FormattedMessage
- id='login.verified'
- defaultMessage=' Email Verified'
- />
- </div>
- );
- } else if (extraParam === Constants.SESSION_EXPIRED) {
- extraBox = (
- <div className='alert alert-warning'>
- <i className='fa fa-exclamation-triangle'/>
- <FormattedMessage
- id='login.session_expired'
- defaultMessage=' Your session has expired. Please login again.'
- />
- </div>
- );
- }
- }
-
- let emailSignup;
- if (global.window.mm_config.EnableSignInWithEmail === 'true') {
- emailSignup = (
- <LoginEmail
- teamName={teamName}
- />
- );
- }
-
- if (loginMessage.length > 0 && emailSignup) {
- loginMessage = (
- <div>
- {loginMessage}
- <div className='or__container'>
- <FormattedMessage
- id='login.or'
- defaultMessage='or'
- />
- </div>
- </div>
- );
- }
-
- let forgotPassword;
- if (emailSignup) {
- forgotPassword = (
- <div className='form-group'>
- <a href={'/' + teamName + '/reset_password'}>
- <FormattedMessage
- id='login.forgot'
- defaultMessage='I forgot my password'
- />
- </a>
- </div>
- );
- }
-
- let userSignUp = null;
- if (currentTeam.allow_open_invite) {
- userSignUp = (
- <div>
- <span>
- <FormattedMessage
- id='login.noAccount'
- defaultMessage="Don't have an account? "
- />
- <a
- href={'/signup_user_complete/?id=' + currentTeam.invite_id}
- className='signup-team-login'
- >
- <FormattedMessage
- id='login.create'
- defaultMessage='Create one now'
- />
- </a>
- </span>
- </div>
- );
- }
-
- let teamSignUp = null;
- if (global.window.mm_config.EnableTeamCreation === 'true' && !Utils.isMobileApp()) {
- teamSignUp = (
- <div className='margin--extra'>
- <a
- href='/'
- className='signup-team-login'
- >
- <FormattedMessage
- id='login.createTeam'
- defaultMessage='Create a new team'
- />
- </a>
- </div>
- );
- }
-
- let ldapLogin = null;
- if (global.window.mm_config.EnableLdap === 'true') {
- ldapLogin = (
- <LoginLdap
- teamName={teamName}
- />
- );
- }
-
- if (ldapEnabled && (loginMessage.length > 0 || emailSignup || usernameSigninEnabled)) {
- ldapLogin = (
- <div>
- <div className='or__container'>
- <FormattedMessage
- id='login.or'
- defaultMessage='or'
- />
- </div>
- <LoginLdap
- teamName={teamName}
- />
- </div>
- );
- }
-
- let usernameLogin = null;
- if (global.window.mm_config.EnableSignInWithUsername === 'true') {
- usernameLogin = (
- <LoginUsername
- teamName={teamName}
- />
- );
- }
-
- if (usernameSigninEnabled && (loginMessage.length > 0 || emailSignup || ldapEnabled)) {
- usernameLogin = (
- <div>
- <div className='or__container'>
- <FormattedMessage
- id='login.or'
- defaultMessage='or'
- />
- </div>
- <LoginUsername
- teamName={teamName}
- />
- </div>
- );
- }
-
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <h5 className='margin--less'>
- <FormattedMessage
- id='login.signTo'
- defaultMessage='Sign in to:'
- />
- </h5>
- <h2 className='signup-team__name'>{teamDisplayName}</h2>
- <h2 className='signup-team__subdomain'>
- <FormattedMessage
- id='login.on'
- defaultMessage='on {siteName}'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </h2>
- {extraBox}
- {loginMessage}
- {emailSignup}
- {usernameLogin}
- {ldapLogin}
- {userSignUp}
- {forgotPassword}
- {teamSignUp}
- </div>
- </div>
- </div>
- );
- }
-}
-
-Login.defaultProps = {
-};
-Login.propTypes = {
- params: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/login_email.jsx b/web/react/components/login_email.jsx
deleted file mode 100644
index 3e0d8919d..000000000
--- a/web/react/components/login_email.jsx
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import UserStore from '../stores/user_store.jsx';
-import {browserHistory} from 'react-router';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-var holders = defineMessages({
- badTeam: {
- id: 'login_email.badTeam',
- defaultMessage: 'Bad team name'
- },
- emailReq: {
- id: 'login_email.emailReq',
- defaultMessage: 'An email is required'
- },
- pwdReq: {
- id: 'login_email.pwdReq',
- defaultMessage: 'A password is required'
- },
- email: {
- id: 'login_email.email',
- defaultMessage: 'Email'
- },
- pwd: {
- id: 'login_email.pwd',
- defaultMessage: 'Password'
- }
-});
-
-class LoginEmail extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- serverError: ''
- };
- }
- handleSubmit(e) {
- e.preventDefault();
- const {formatMessage} = this.props.intl;
- var state = {};
-
- const name = this.props.teamName;
- if (!name) {
- state.serverError = formatMessage(holders.badTeam);
- this.setState(state);
- return;
- }
-
- const email = this.refs.email.value.trim();
- if (!email) {
- state.serverError = formatMessage(holders.emailReq);
- this.setState(state);
- return;
- }
-
- const password = this.refs.password.value.trim();
- if (!password) {
- state.serverError = formatMessage(holders.pwdReq);
- this.setState(state);
- return;
- }
-
- state.serverError = '';
- this.setState(state);
-
- Client.loginByEmail(name, email, password,
- () => {
- UserStore.setLastEmail(email);
- browserHistory.push('/' + name + '/channels/town-square');
- },
- (err) => {
- if (err.id === 'api.user.login.not_verified.app_error') {
- window.location.href = '/verify_email?teamname=' + encodeURIComponent(name) + '&email=' + encodeURIComponent(email);
- return;
- }
- state.serverError = err.message;
- this.valid = false;
- this.setState(state);
- }
- );
- }
- render() {
- let serverError;
- let errorClass = '';
- if (this.state.serverError) {
- serverError = <label className='control-label'>{this.state.serverError}</label>;
- errorClass = ' has-error';
- }
-
- let priorEmail = UserStore.getLastEmail();
- let focusEmail = false;
- let focusPassword = false;
- if (priorEmail === '') {
- focusEmail = true;
- } else {
- focusPassword = true;
- }
-
- const emailParam = Utils.getUrlParameter('email');
- if (emailParam) {
- priorEmail = decodeURIComponent(emailParam);
- }
-
- const {formatMessage} = this.props.intl;
- return (
- <form onSubmit={this.handleSubmit}>
- <div className='signup__email-container'>
- <div className={'form-group' + errorClass}>
- {serverError}
- </div>
- <div className={'form-group' + errorClass}>
- <input
- autoFocus={focusEmail}
- type='email'
- className='form-control'
- name='email'
- defaultValue={priorEmail}
- ref='email'
- placeholder={formatMessage(holders.email)}
- spellCheck='false'
- />
- </div>
- <div className={'form-group' + errorClass}>
- <input
- autoFocus={focusPassword}
- type='password'
- className='form-control'
- name='password'
- ref='password'
- placeholder={formatMessage(holders.pwd)}
- spellCheck='false'
- />
- </div>
- <div className='form-group'>
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='login_email.signin'
- defaultMessage='Sign in'
- />
- </button>
- </div>
- </div>
- </form>
- );
- }
-}
-LoginEmail.defaultProps = {
-};
-
-LoginEmail.propTypes = {
- intl: intlShape.isRequired,
- teamName: React.PropTypes.string.isRequired
-};
-
-export default injectIntl(LoginEmail);
diff --git a/web/react/components/login_ldap.jsx b/web/react/components/login_ldap.jsx
deleted file mode 100644
index d67f15fa5..000000000
--- a/web/react/components/login_ldap.jsx
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- badTeam: {
- id: 'login_ldap.badTeam',
- defaultMessage: 'Bad team name'
- },
- idReq: {
- id: 'login_ldap.idlReq',
- defaultMessage: 'An LDAP ID is required'
- },
- pwdReq: {
- id: 'login_ldap.pwdReq',
- defaultMessage: 'An LDAP password is required'
- },
- username: {
- id: 'login_ldap.username',
- defaultMessage: 'LDAP Username'
- },
- pwd: {
- id: 'login_ldap.pwd',
- defaultMessage: 'LDAP Password'
- }
-});
-
-class LoginLdap extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- serverError: ''
- };
- }
- handleSubmit(e) {
- e.preventDefault();
- const {formatMessage} = this.props.intl;
- var state = {};
-
- const teamName = this.props.teamName;
- if (!teamName) {
- state.serverError = formatMessage(holders.badTeam);
- this.setState(state);
- return;
- }
-
- const id = this.refs.id.value.trim();
- if (!id) {
- state.serverError = formatMessage(holders.idReq);
- this.setState(state);
- return;
- }
-
- const password = this.refs.password.value.trim();
- if (!password) {
- state.serverError = formatMessage(holders.pwdReq);
- this.setState(state);
- return;
- }
-
- state.serverError = '';
- this.setState(state);
-
- Client.loginByLdap(teamName, id, password,
- () => {
- const redirect = Utils.getUrlParameter('redirect');
- if (redirect) {
- window.location.href = decodeURIComponent(redirect);
- } else {
- window.location.href = '/' + teamName + '/channels/town-square';
- }
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
- render() {
- let serverError;
- let errorClass = '';
- if (this.state.serverError) {
- serverError = <label className='control-label'>{this.state.serverError}</label>;
- errorClass = ' has-error';
- }
- const {formatMessage} = this.props.intl;
- return (
- <form onSubmit={this.handleSubmit}>
- <div className='signup__email-container'>
- <div className={'form-group' + errorClass}>
- {serverError}
- </div>
- <div className={'form-group' + errorClass}>
- <input
- autoFocus={true}
- className='form-control'
- ref='id'
- placeholder={formatMessage(holders.username)}
- spellCheck='false'
- />
- </div>
- <div className={'form-group' + errorClass}>
- <input
- type='password'
- className='form-control'
- ref='password'
- placeholder={formatMessage(holders.pwd)}
- spellCheck='false'
- />
- </div>
- <div className='form-group'>
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='login_ldap.signin'
- defaultMessage='Sign in'
- />
- </button>
- </div>
- </div>
- </form>
- );
- }
-}
-LoginLdap.defaultProps = {
-};
-
-LoginLdap.propTypes = {
- intl: intlShape.isRequired,
- teamName: React.PropTypes.string.isRequired
-};
-
-export default injectIntl(LoginLdap); \ No newline at end of file
diff --git a/web/react/components/login_username.jsx b/web/react/components/login_username.jsx
deleted file mode 100644
index 4bd9254c6..000000000
--- a/web/react/components/login_username.jsx
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-var holders = defineMessages({
- badTeam: {
- id: 'login_username.badTeam',
- defaultMessage: 'Bad team name'
- },
- usernameReq: {
- id: 'login_username.usernameReq',
- defaultMessage: 'A username is required'
- },
- pwdReq: {
- id: 'login_username.pwdReq',
- defaultMessage: 'A password is required'
- },
- verifyEmailError: {
- id: 'login_username.verifyEmailError',
- defaultMessage: 'Please verify your email address. Check your inbox for an email.'
- },
- userNotFoundError: {
- id: 'login_username.userNotFoundError',
- defaultMessage: "We couldn't find an existing account matching your username for this team."
- },
- username: {
- id: 'login_username.username',
- defaultMessage: 'Username'
- },
- pwd: {
- id: 'login_username.pwd',
- defaultMessage: 'Password'
- }
-});
-
-export default class LoginUsername extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- serverError: ''
- };
- }
- handleSubmit(e) {
- e.preventDefault();
- const {formatMessage} = this.props.intl;
- var state = {};
-
- const name = this.props.teamName;
- if (!name) {
- state.serverError = formatMessage(holders.badTeam);
- this.setState(state);
- return;
- }
-
- const username = this.refs.username.value.trim();
- if (!username) {
- state.serverError = formatMessage(holders.usernameReq);
- this.setState(state);
- return;
- }
-
- const password = this.refs.password.value.trim();
- if (!password) {
- state.serverError = formatMessage(holders.pwdReq);
- this.setState(state);
- return;
- }
-
- state.serverError = '';
- this.setState(state);
-
- Client.loginByUsername(name, username, password,
- () => {
- UserStore.setLastUsername(username);
-
- const redirect = Utils.getUrlParameter('redirect');
- if (redirect) {
- window.location.href = decodeURIComponent(redirect);
- } else {
- window.location.href = '/' + name + '/channels/town-square';
- }
- },
- (err) => {
- if (err.id === 'api.user.login.not_verified.app_error') {
- state.serverError = formatMessage(holders.verifyEmailError);
- } else if (err.id === 'store.sql_user.get_by_username.app_error') {
- state.serverError = formatMessage(holders.userNotFoundError);
- } else {
- state.serverError = err.message;
- }
-
- this.valid = false;
- this.setState(state);
- }
- );
- }
- render() {
- let serverError;
- let errorClass = '';
- if (this.state.serverError) {
- serverError = <label className='control-label'>{this.state.serverError}</label>;
- errorClass = ' has-error';
- }
-
- let priorUsername = UserStore.getLastUsername();
- let focusUsername = false;
- let focusPassword = false;
- if (priorUsername === '') {
- focusUsername = true;
- } else {
- focusPassword = true;
- }
-
- const emailParam = Utils.getUrlParameter('email');
- if (emailParam) {
- priorUsername = decodeURIComponent(emailParam);
- }
-
- const {formatMessage} = this.props.intl;
- return (
- <form onSubmit={this.handleSubmit}>
- <div className='signup__email-container'>
- <div className={'form-group' + errorClass}>
- {serverError}
- </div>
- <div className={'form-group' + errorClass}>
- <input
- autoFocus={focusUsername}
- type='username'
- className='form-control'
- name='username'
- defaultValue={priorUsername}
- ref='username'
- placeholder={formatMessage(holders.username)}
- spellCheck='false'
- />
- </div>
- <div className={'form-group' + errorClass}>
- <input
- autoFocus={focusPassword}
- type='password'
- className='form-control'
- name='password'
- ref='password'
- placeholder={formatMessage(holders.pwd)}
- spellCheck='false'
- />
- </div>
- <div className='form-group'>
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='login_username.signin'
- defaultMessage='Sign in'
- />
- </button>
- </div>
- </div>
- </form>
- );
- }
-}
-LoginUsername.defaultProps = {
-};
-
-LoginUsername.propTypes = {
- intl: intlShape.isRequired,
- teamName: React.PropTypes.string.isRequired
-};
-
-export default injectIntl(LoginUsername);
diff --git a/web/react/components/member_list_team.jsx b/web/react/components/member_list_team.jsx
deleted file mode 100644
index cfd5359b7..000000000
--- a/web/react/components/member_list_team.jsx
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import FilteredUserList from './filtered_user_list.jsx';
-import TeamMembersDropdown from './team_members_dropdown.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-export default class MemberListTeam extends React.Component {
- constructor(props) {
- super(props);
-
- this.getUsers = this.getUsers.bind(this);
- this.onChange = this.onChange.bind(this);
-
- this.state = {
- users: this.getUsers()
- };
- }
-
- componentDidMount() {
- UserStore.addChangeListener(this.onChange);
- }
-
- componentWillUnmount() {
- UserStore.removeChangeListener(this.onChange);
- }
-
- getUsers() {
- const profiles = UserStore.getProfiles();
- const users = [];
-
- for (const id of Object.keys(profiles)) {
- users.push(profiles[id]);
- }
-
- users.sort((a, b) => a.username.localeCompare(b.username));
-
- return users;
- }
-
- onChange() {
- this.setState({
- users: this.getUsers()
- });
- }
-
- render() {
- return (
- <FilteredUserList
- style={this.props.style}
- users={this.state.users}
- actions={[TeamMembersDropdown]}
- />
- );
- }
-}
-
-MemberListTeam.propTypes = {
- style: React.PropTypes.object
-};
diff --git a/web/react/components/message_wrapper.jsx b/web/react/components/message_wrapper.jsx
deleted file mode 100644
index 8e0380c06..000000000
--- a/web/react/components/message_wrapper.jsx
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as TextFormatting from '../utils/text_formatting.jsx';
-
-export default class MessageWrapper extends React.Component {
- constructor(props) {
- super(props);
- this.state = {};
- }
- render() {
- if (this.props.message) {
- return <div dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.message, this.props.options)}}/>;
- }
-
- return <div/>;
- }
-}
-
-MessageWrapper.defaultProps = {
- message: ''
-};
-MessageWrapper.propTypes = {
- message: React.PropTypes.string,
- options: React.PropTypes.object
-};
diff --git a/web/react/components/more_channels.jsx b/web/react/components/more_channels.jsx
deleted file mode 100644
index 2ba435449..000000000
--- a/web/react/components/more_channels.jsx
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import LoadingScreen from './loading_screen.jsx';
-import NewChannelFlow from './new_channel_flow.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-function getStateFromStores() {
- return {
- channels: ChannelStore.getMoreAll(),
- serverError: null
- };
-}
-
-export default class MoreChannels extends React.Component {
- constructor(props) {
- super(props);
-
- this.onListenerChange = this.onListenerChange.bind(this);
- this.handleJoin = this.handleJoin.bind(this);
- this.handleNewChannel = this.handleNewChannel.bind(this);
- this.createChannelRow = this.createChannelRow.bind(this);
-
- var initState = getStateFromStores();
- initState.channelType = '';
- initState.joiningChannel = -1;
- initState.showNewChannelModal = false;
- this.state = initState;
- }
- componentDidMount() {
- ChannelStore.addMoreChangeListener(this.onListenerChange);
- $(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', () => {
- AsyncClient.getMoreChannels(true);
- });
-
- var self = this;
- $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', (e) => {
- var button = e.relatedTarget;
- self.setState({channelType: $(button).attr('data-channeltype')});
- });
- }
- componentWillUnmount() {
- ChannelStore.removeMoreChangeListener(this.onListenerChange);
- }
- onListenerChange() {
- var newState = getStateFromStores();
- if (!Utils.areObjectsEqual(newState.channels, this.state.channels)) {
- this.setState(newState);
- }
- }
- handleJoin(channel, channelIndex) {
- this.setState({joiningChannel: channelIndex});
- client.joinChannel(channel.id,
- () => {
- $(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
- AsyncClient.getChannel(channel.id);
- Utils.switchChannel(channel);
- this.setState({joiningChannel: -1});
- },
- (err) => {
- this.setState({joiningChannel: -1, serverError: err.message});
- }
- );
- }
- handleNewChannel() {
- $(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
- this.setState({showNewChannelModal: true});
- }
- createChannelRow(channel, index) {
- let joinButton;
- if (this.state.joiningChannel === index) {
- joinButton = (
- <img
- className='join-channel-loading-gif'
- src='/static/images/load.gif'
- />
- );
- } else {
- joinButton = (
- <button
- onClick={this.handleJoin.bind(self, channel, index)}
- className='btn btn-primary'
- >
- <FormattedMessage
- id='more_channels.join'
- defaultMessage='Join'
- />
- </button>
- );
- }
-
- return (
- <div
- className='more-modal__row'
- key={channel.id}
- >
- <div className='more-modal__details'>
- <p className='more-modal__name'>{channel.display_name}</p>
- <p className='more-modal__description'>{channel.purpose}</p>
- </div>
- <div className='more-modal__actions'>
- {joinButton}
- </div>
- </div>
- );
- }
- render() {
- let maxHeight = 1000;
- if (Utils.windowHeight() <= 1200) {
- maxHeight = Utils.windowHeight() - 300;
- }
-
- var serverError;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var moreChannels;
-
- if (this.state.channels != null) {
- var channels = this.state.channels;
- if (channels.loading) {
- moreChannels = <LoadingScreen/>;
- } else if (channels.length) {
- moreChannels = (
- <div className='more-modal__list'>
- {channels.map(this.createChannelRow)}
- </div>
- );
- } else {
- moreChannels = (
- <div className='no-channel-message'>
- <p className='primary-message'>
- <FormattedMessage
- id='more_channels.noMore'
- defaultMessage='No more channels to join'
- />
- </p>
- <p className='secondary-message'>
- <FormattedMessage
- id='more_channels.createClick'
- defaultMessage="Click 'Create New Channel' to make a new one"
- />
- </p>
- </div>
- );
- }
- }
-
- return (
- <div
- className='modal fade'
- id='more_channels'
- ref='modal'
- tabIndex='-1'
- role='dialog'
- aria-hidden='true'
- >
- <div className='modal-dialog'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- >
- <span aria-hidden='true'>{'×'}</span>
- <span className='sr-only'>
- <FormattedMessage
- id='more_channels.close'
- defaultMessage='Close'
- />
- </span>
- </button>
- <h4 className='modal-title'>
- <FormattedMessage
- id='more_channels.title'
- defaultMessage='More Channels'
- />
- </h4>
- <button
- type='button'
- className='btn btn-primary channel-create-btn'
- onClick={this.handleNewChannel}
- >
- <FormattedMessage
- id='more_channels.create'
- defaultMessage='Create New Channel'
- />
- </button>
- <NewChannelFlow
- show={this.state.showNewChannelModal}
- channelType={this.state.channelType}
- onModalDismissed={() => this.setState({showNewChannelModal: false})}
- />
- </div>
- <div
- className='modal-body'
- style={{maxHeight}}
- >
- {moreChannels}
- {serverError}
- </div>
- <div className='modal-footer'>
- <button
- type='button'
- className='btn btn-default'
- data-dismiss='modal'
- >
- <FormattedMessage
- id='more_channels.close'
- defaultMessage='Close'
- />
- </button>
- </div>
- </div>
- </div>
- </div>
-
- );
- }
-}
diff --git a/web/react/components/more_direct_channels.jsx b/web/react/components/more_direct_channels.jsx
deleted file mode 100644
index 0814ac1b3..000000000
--- a/web/react/components/more_direct_channels.jsx
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-const Modal = ReactBootstrap.Modal;
-import FilteredUserList from './filtered_user_list.jsx';
-import UserStore from '../stores/user_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class MoreDirectChannels extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleHide = this.handleHide.bind(this);
- this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this);
- this.handleUserChange = this.handleUserChange.bind(this);
-
- this.createJoinDirectChannelButton = this.createJoinDirectChannelButton.bind(this);
-
- this.state = {
- users: this.getUsersFromStore(),
- loadingDMChannel: -1
- };
- }
-
- getUsersFromStore() {
- const currentId = UserStore.getCurrentId();
- const profiles = UserStore.getActiveOnlyProfiles();
- const users = [];
-
- for (const id in profiles) {
- if (id !== currentId) {
- users.push(profiles[id]);
- }
- }
-
- users.sort((a, b) => a.username.localeCompare(b.username));
-
- return users;
- }
-
- componentDidMount() {
- UserStore.addChangeListener(this.handleUserChange);
- }
-
- componentWillUnmount() {
- UserStore.removeChangeListener(this.handleUserChange);
- }
-
- handleHide() {
- if (this.props.onModalDismissed) {
- this.props.onModalDismissed();
- }
- }
-
- handleShowDirectChannel(teammate, e) {
- e.preventDefault();
-
- if (this.state.loadingDMChannel !== -1) {
- return;
- }
-
- this.setState({loadingDMChannel: teammate.id});
- Utils.openDirectChannelToUser(
- teammate,
- (channel) => {
- Utils.switchChannel(channel);
- this.setState({loadingDMChannel: -1});
- this.handleHide();
- },
- () => {
- this.setState({loadingDMChannel: -1});
- }
- );
- }
-
- handleUserChange() {
- this.setState({users: this.getUsersFromStore()});
- }
-
- createJoinDirectChannelButton({user}) {
- if (this.state.loadingDMChannel === user.id) {
- return (
- <img
- className='channel-loading-gif'
- src='/static/images/load.gif'
- />
- );
- }
-
- return (
- <button
- type='button'
- className='btn btn-primary btn-message'
- onClick={this.handleShowDirectChannel.bind(this, user)}
- >
- <FormattedMessage
- id='more_direct_channels.message'
- defaultMessage='Message'
- />
- </button>
- );
- }
-
- render() {
- let maxHeight = 1000;
- if (Utils.windowHeight() <= 1200) {
- maxHeight = Utils.windowHeight() - 300;
- }
-
- return (
- <Modal
- dialogClassName='more-modal more-direct-channels'
- show={this.props.show}
- onHide={this.handleHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='more_direct_channels.title'
- defaultMessage='Direct Messages'
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <FilteredUserList
- style={{maxHeight}}
- users={this.state.users}
- actions={[this.createJoinDirectChannelButton]}
- />
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.handleHide}
- >
- <FormattedMessage
- id='more_direct_channels.close'
- defaultMessage='Close'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-MoreDirectChannels.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onModalDismissed: React.PropTypes.func
-};
diff --git a/web/react/components/msg_typing.jsx b/web/react/components/msg_typing.jsx
deleted file mode 100644
index f7a40b54e..000000000
--- a/web/react/components/msg_typing.jsx
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SocketStore from '../stores/socket_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const SocketEvents = Constants.SocketEvents;
-
-const holders = defineMessages({
- someone: {
- id: 'msg_typing.someone',
- defaultMessage: 'Someone'
- }
-});
-
-class MsgTyping extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
- this.updateTypingText = this.updateTypingText.bind(this);
- this.componentWillReceiveProps = this.componentWillReceiveProps.bind(this);
-
- this.typingUsers = {};
- this.state = {
- text: ''
- };
- }
-
- componentDidMount() {
- SocketStore.addChangeListener(this.onChange);
- }
-
- componentWillReceiveProps(nextProps) {
- if (this.props.channelId !== nextProps.channelId) {
- for (const u in this.typingUsers) {
- if (!this.typingUsers.hasOwnProperty(u)) {
- continue;
- }
-
- clearTimeout(this.typingUsers[u]);
- }
- this.typingUsers = {};
- this.setState({text: ''});
- }
- }
-
- componentWillUnmount() {
- SocketStore.removeChangeListener(this.onChange);
- }
-
- onChange(msg) {
- let username = this.props.intl.formatMessage(holders.someone);
- if (msg.action === SocketEvents.TYPING &&
- this.props.channelId === msg.channel_id &&
- this.props.parentId === msg.props.parent_id) {
- if (UserStore.hasProfile(msg.user_id)) {
- username = UserStore.getProfile(msg.user_id).username;
- }
-
- if (this.typingUsers[username]) {
- clearTimeout(this.typingUsers[username]);
- }
-
- this.typingUsers[username] = setTimeout(function myTimer(user) {
- delete this.typingUsers[user];
- this.updateTypingText();
- }.bind(this, username), Constants.UPDATE_TYPING_MS);
-
- this.updateTypingText();
- } else if (msg.action === SocketEvents.POSTED && msg.channel_id === this.props.channelId) {
- if (UserStore.hasProfile(msg.user_id)) {
- username = UserStore.getProfile(msg.user_id).username;
- }
- clearTimeout(this.typingUsers[username]);
- delete this.typingUsers[username];
- this.updateTypingText();
- }
- }
-
- updateTypingText() {
- const users = Object.keys(this.typingUsers);
- let text = '';
- switch (users.length) {
- case 0:
- text = '';
- break;
- case 1:
- text = (
- <FormattedMessage
- id='msg_typing.isTyping'
- defaultMessage='{user} is typing...'
- values={{
- user: users[0]
- }}
- />
- );
- break;
- default: {
- const last = users.pop();
- text = (
- <FormattedMessage
- id='msg_typing.areTyping'
- defaultMessage='{users} and {last} are typing...'
- values={{
- users: (users.join(', ')),
- last: (last)
- }}
- />
- );
- break;
- }
- }
-
- this.setState({text});
- }
-
- render() {
- return (
- <span className='msg-typing'>{this.state.text}</span>
- );
- }
-}
-
-MsgTyping.propTypes = {
- intl: intlShape.isRequired,
- channelId: React.PropTypes.string,
- parentId: React.PropTypes.string
-};
-
-export default injectIntl(MsgTyping); \ No newline at end of file
diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx
deleted file mode 100644
index 8382cb47a..000000000
--- a/web/react/components/navbar.jsx
+++ /dev/null
@@ -1,570 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import EditChannelHeaderModal from './edit_channel_header_modal.jsx';
-import EditChannelPurposeModal from './edit_channel_purpose_modal.jsx';
-import MessageWrapper from './message_wrapper.jsx';
-import NotifyCounts from './notify_counts.jsx';
-import ChannelInfoModal from './channel_info_modal.jsx';
-import ChannelInviteModal from './channel_invite_modal.jsx';
-import ChannelNotificationsModal from './channel_notifications_modal.jsx';
-import DeleteChannelModal from './delete_channel_modal.jsx';
-import RenameChannelModal from './rename_channel_modal.jsx';
-import ToggleModalButton from './toggle_modal_button.jsx';
-
-import UserStore from '../stores/user_store.jsx';
-import ChannelStore from '../stores/channel_store.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';
-const ActionTypes = Constants.ActionTypes;
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-import attachFastClick from 'fastclick';
-
-const Popover = ReactBootstrap.Popover;
-const OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-export default class Navbar extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
- this.handleLeave = this.handleLeave.bind(this);
- this.showSearch = this.showSearch.bind(this);
-
- this.showEditChannelHeaderModal = this.showEditChannelHeaderModal.bind(this);
- this.showRenameChannelModal = this.showRenameChannelModal.bind(this);
- this.hideRenameChannelModal = this.hideRenameChannelModal.bind(this);
-
- this.createCollapseButtons = this.createCollapseButtons.bind(this);
- this.createDropdown = this.createDropdown.bind(this);
-
- const state = this.getStateFromStores();
- state.showEditChannelPurposeModal = false;
- state.showEditChannelHeaderModal = false;
- state.showMembersModal = false;
- state.showRenameChannelModal = false;
- this.state = state;
- }
- getStateFromStores() {
- return {
- channel: ChannelStore.getCurrent(),
- member: ChannelStore.getCurrentMember(),
- users: ChannelStore.getCurrentExtraInfo().members,
- currentUser: UserStore.getCurrentUser()
- };
- }
- stateValid() {
- return this.state.channel && this.state.member && this.state.users && this.state.currentUser;
- }
- componentDidMount() {
- ChannelStore.addChangeListener(this.onChange);
- ChannelStore.addExtraInfoChangeListener(this.onChange);
- $('.inner-wrap').click(this.hideSidebars);
- attachFastClick(document.body);
- }
- componentWillUnmount() {
- ChannelStore.removeChangeListener(this.onChange);
- ChannelStore.removeExtraInfoChangeListener(this.onChange);
- }
- handleSubmit(e) {
- e.preventDefault();
- }
- handleLeave() {
- Client.leaveChannel(this.state.channel.id,
- () => {
- AsyncClient.getChannels(true);
- window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/town-square';
- },
- (err) => {
- AsyncClient.dispatchError(err, 'handleLeave');
- }
- );
- }
- hideSidebars(e) {
- var windowWidth = $(window).outerWidth();
- if (windowWidth <= 768) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
-
- if (e.target.className !== 'navbar-toggle' && e.target.className !== 'icon-bar') {
- $('.inner-wrap').removeClass('move--right move--left move--left-small');
- $('.sidebar--left').removeClass('move--right');
- $('.sidebar--right').removeClass('move--left');
- $('.sidebar--menu').removeClass('move--left');
- }
- }
- }
- toggleLeftSidebar() {
- $('.inner-wrap').toggleClass('move--right');
- $('.sidebar--left').toggleClass('move--right');
- }
- toggleRightSidebar() {
- $('.inner-wrap').toggleClass('move--left-small');
- $('.sidebar--menu').toggleClass('move--left');
- }
- showSearch() {
- AppDispatcher.handleServerAction({
- type: ActionTypes.SHOW_SEARCH
- });
- }
- onChange() {
- this.setState(this.getStateFromStores());
- $('#navbar .navbar-brand .description').popover({placement: 'bottom', trigger: 'click', html: true});
- }
- showEditChannelHeaderModal() {
- // this can't be done using a ToggleModalButton because we can't use one inside an OverlayTrigger
- if (this.refs.headerOverlay) {
- this.refs.headerOverlay.hide();
- }
-
- this.setState({
- showEditChannelHeaderModal: true
- });
- }
- showRenameChannelModal(e) {
- e.preventDefault();
-
- this.setState({
- showRenameChannelModal: true
- });
- }
- hideRenameChannelModal() {
- this.setState({
- showRenameChannelModal: false
- });
- }
- createDropdown(channel, channelTitle, isAdmin, isDirect, popoverContent) {
- if (channel) {
- var viewInfoOption = (
- <li role='presentation'>
- <ToggleModalButton
- role='menuitem'
- dialogType={ChannelInfoModal}
- dialogProps={{channel}}
- >
- <FormattedMessage
- id='navbar.viewInfo'
- defaultMessage='View Info'
- />
- </ToggleModalButton>
- </li>
- );
-
- var setChannelHeaderOption = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.showEditChannelHeaderModal}
- >
- <FormattedMessage
- id='navbar.setHeader'
- defaultMessage='Set Channel Header...'
- />
- </a>
- </li>
- );
-
- var setChannelPurposeOption = null;
- if (!isDirect) {
- setChannelPurposeOption = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={() => this.setState({showEditChannelPurposeModal: true})}
- >
- <FormattedMessage
- id='navbar.setPurpose'
- defaultMessage='Set Channel Purpose...'
- />
- </a>
- </li>
- );
- }
-
- var addMembersOption;
- var leaveChannelOption;
- if (!isDirect && !ChannelStore.isDefault(channel)) {
- addMembersOption = (
- <li role='presentation'>
- <ToggleModalButton
- role='menuitem'
- dialogType={ChannelInviteModal}
- dialogProps={{channel, currentUser: this.state.currentUser}}
- >
- <FormattedMessage
- id='navbar.addMembers'
- defaultMessage='Add Members'
- />
- </ToggleModalButton>
- </li>
- );
-
- leaveChannelOption = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleLeave}
- >
- <FormattedMessage
- id='navbar.leave'
- defaultMessage='Leave Channel'
- />
- </a>
- </li>
- );
- }
-
- var manageMembersOption;
- var renameChannelOption;
- var deleteChannelOption;
- if (!isDirect && isAdmin) {
- if (!ChannelStore.isDefault(channel)) {
- manageMembersOption = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={() => this.setState({showMembersModal: true})}
- >
- <FormattedMessage
- id='navbar.manageMembers'
- defaultMessage='Manage Members'
- />
- </a>
- </li>
- );
-
- deleteChannelOption = (
- <li role='presentation'>
- <ToggleModalButton
- role='menuitem'
- dialogType={DeleteChannelModal}
- dialogProps={{channel}}
- >
- <FormattedMessage
- id='navbar.delete'
- defaultMessage='Delete Channel...'
- />
- </ToggleModalButton>
- </li>
- );
- }
-
- renameChannelOption = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.showRenameChannelModal}
- >
- <FormattedMessage
- id='navbar.rename'
- defaultMessage='Rename Channel...'
- />
- </a>
- </li>
- );
- }
-
- var notificationPreferenceOption;
- if (!isDirect) {
- notificationPreferenceOption = (
- <li role='presentation'>
- <ToggleModalButton
- role='menuitem'
- dialogType={ChannelNotificationsModal}
- dialogProps={{
- channel,
- channelMember: this.state.member,
- currentUser: this.state.currentUser
- }}
- >
- <FormattedMessage
- id='navbar.preferences'
- defaultMessage='Notification Preferences'
- />
- </ToggleModalButton>
- </li>
- );
- }
-
- return (
- <div className='navbar-brand'>
- <div className='dropdown'>
- <OverlayTrigger
- ref='headerOverlay'
- trigger='click'
- placement='bottom'
- overlay={popoverContent}
- className='description'
- rootClose={true}
- >
- <div className='description info-popover'/>
- </OverlayTrigger>
- <a
- href='#'
- className='dropdown-toggle theme'
- type='button'
- data-toggle='dropdown'
- aria-expanded='true'
- >
- <span className='heading'>{channelTitle} </span>
- <span className='glyphicon glyphicon-chevron-down header-dropdown__icon'></span>
- </a>
- <ul
- className='dropdown-menu'
- role='menu'
- >
- {viewInfoOption}
- {addMembersOption}
- {manageMembersOption}
- {setChannelHeaderOption}
- {setChannelPurposeOption}
- {notificationPreferenceOption}
- {renameChannelOption}
- {deleteChannelOption}
- {leaveChannelOption}
- </ul>
- </div>
- </div>
- );
- }
-
- return (
- <div className='navbar-brand'>
- <a
- href={TeamStore.getCurrentTeamUrl() + '/channels/town-square'}
- className='heading'
- >
- {channelTitle}
- </a>
- </div>
- );
- }
- createCollapseButtons(currentId) {
- var buttons = [];
- if (currentId == null) {
- buttons.push(
- <button
- key='navbar-toggle-collapse'
- type='button'
- className='navbar-toggle'
- data-toggle='collapse'
- data-target='#navbar-collapse-1'
- >
- <span className='sr-only'>
- <FormattedMessage
- id='navbar.toggle1'
- defaultMessage='Toggle sidebar'
- />
- </span>
- <span className='icon-bar'></span>
- <span className='icon-bar'></span>
- <span className='icon-bar'></span>
- </button>
- );
- } else {
- buttons.push(
- <button
- key='navbar-toggle-sidebar'
- type='button'
- className='navbar-toggle'
- data-toggle='collapse'
- data-target='#sidebar-nav'
- onClick={this.toggleLeftSidebar}
- >
- <span className='sr-only'>
- <FormattedMessage
- id='navbar.toggle2'
- defaultMessage='Toggle sidebar'
- />
- </span>
- <span className='icon-bar'></span>
- <span className='icon-bar'></span>
- <span className='icon-bar'></span>
- <NotifyCounts/>
- </button>
- );
-
- buttons.push(
- <button
- key='navbar-toggle-menu'
- type='button'
- className='navbar-toggle menu-toggle pull-right'
- data-toggle='collapse'
- data-target='#sidebar-nav'
- onClick={this.toggleRightSidebar}
- >
- <span dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}}/>
- </button>
- );
- }
-
- return buttons;
- }
- render() {
- if (!this.stateValid()) {
- return null;
- }
-
- var currentId = this.state.currentUser.id;
- var channel = this.state.channel;
- var channelTitle = this.props.teamDisplayName;
- var popoverContent;
- var isAdmin = false;
- var isDirect = false;
-
- var editChannelHeaderModal = null;
- var editChannelPurposeModal = null;
- let renameChannelModal = null;
-
- if (channel) {
- popoverContent = (
- <Popover
- bsStyle='info'
- placement='bottom'
- id='header-popover'
- >
- <MessageWrapper
- message={channel.header}
- options={{singleline: true, mentionHighlight: false}}
- />
- </Popover>
- );
- isAdmin = Utils.isAdmin(this.state.member.roles);
-
- if (channel.type === 'O') {
- channelTitle = channel.display_name;
- } else if (channel.type === 'P') {
- channelTitle = channel.display_name;
- } else if (channel.type === 'D') {
- isDirect = true;
- if (this.state.users.length > 1) {
- let p;
- if (this.state.users[0].id === currentId) {
- p = UserStore.getProfile(this.state.users[1].id);
- } else {
- p = UserStore.getProfile(this.state.users[0].id);
- }
- if (p != null) {
- channelTitle = p.username;
- }
- }
- }
-
- if (channel.header.length === 0) {
- const link = (
- <a
- href='#'
- onClick={this.showEditChannelHeaderModal}
- >
- <FormattedMessage
- id='navbar.click'
- defaultMessage='Click here'
- />
- </a>
- );
- popoverContent = (
- <Popover
- bsStyle='info'
- placement='bottom'
- id='header-popover'
- >
- <div>
- <FormattedMessage
- id='navbar.noHeader'
- defaultMessage='No channel header yet.{newline}{link} to add one.'
- values={{
- newline: (<br/>),
- link: (link)
- }}
- />
- </div>
- </Popover>
- );
- }
-
- editChannelHeaderModal = (
- <EditChannelHeaderModal
- show={this.state.showEditChannelHeaderModal}
- onHide={() => this.setState({showEditChannelHeaderModal: false})}
- channel={channel}
- />
- );
-
- editChannelPurposeModal = (
- <EditChannelPurposeModal
- show={this.state.showEditChannelPurposeModal}
- onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})}
- channel={channel}
- />
- );
-
- renameChannelModal = (
- <RenameChannelModal
- show={this.state.showRenameChannelModal}
- onHide={this.hideRenameChannelModal}
- channel={channel}
- />
- );
- }
-
- var collapseButtons = this.createCollapseButtons(currentId);
-
- const searchButton = (
- <button
- type='button'
- className='navbar-toggle pull-right'
- onClick={this.showSearch}
- >
- <span className='glyphicon glyphicon-search icon--white'/>
- </button>
- );
-
- var channelMenuDropdown = this.createDropdown(channel, channelTitle, isAdmin, isDirect, popoverContent);
-
- return (
- <div>
- <nav
- className='navbar navbar-default navbar-fixed-top'
- role='navigation'
- >
- <div className='container-fluid theme'>
- <div className='navbar-header'>
- {collapseButtons}
- {searchButton}
- {channelMenuDropdown}
- </div>
- </div>
- </nav>
- {editChannelHeaderModal}
- {editChannelPurposeModal}
- {renameChannelModal}
- </div>
- );
- }
-}
-
-Navbar.defaultProps = {
- teamDisplayName: ''
-};
-Navbar.propTypes = {
- teamDisplayName: React.PropTypes.string
-};
diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx
deleted file mode 100644
index 12227fd13..000000000
--- a/web/react/components/navbar_dropdown.jsx
+++ /dev/null
@@ -1,277 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import AboutBuildModal from './about_build_modal.jsx';
-import TeamMembersModal from './team_members_modal.jsx';
-import ToggleModalButton from './toggle_modal_button.jsx';
-import UserSettingsModal from './user_settings/user_settings_modal.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-import {Link} from 'react-router';
-
-export default class NavbarDropdown extends React.Component {
- constructor(props) {
- super(props);
- this.blockToggle = false;
-
- this.handleAboutModal = this.handleAboutModal.bind(this);
- this.aboutModalDismissed = this.aboutModalDismissed.bind(this);
-
- this.state = {
- showUserSettingsModal: false,
- showAboutModal: false
- };
- }
- handleAboutModal() {
- this.setState({showAboutModal: true});
- }
- aboutModalDismissed() {
- this.setState({showAboutModal: false});
- }
- componentDidMount() {
- $(ReactDOM.findDOMNode(this.refs.dropdown)).on('hide.bs.dropdown', () => {
- $('.sidebar--left .dropdown-menu').scrollTop(0);
- this.blockToggle = true;
- setTimeout(() => {
- this.blockToggle = false;
- }, 100);
- });
- }
- componentWillUnmount() {
- $(ReactDOM.findDOMNode(this.refs.dropdown)).off('hide.bs.dropdown');
- }
- render() {
- var teamLink = '';
- var inviteLink = '';
- var manageLink = '';
- var sysAdminLink = '';
- var adminDivider = '';
- var currentUser = this.props.currentUser;
- var isAdmin = false;
- var isSystemAdmin = false;
- var teamSettings = null;
-
- if (currentUser != null) {
- isAdmin = Utils.isAdmin(currentUser.roles);
- isSystemAdmin = Utils.isSystemAdmin(currentUser.roles);
-
- inviteLink = (
- <li>
- <a
- href='#'
- onClick={GlobalActions.showInviteMemberModal}
- >
- <FormattedMessage
- id='navbar_dropdown.inviteMember'
- defaultMessage='Invite New Member'
- />
- </a>
- </li>
- );
-
- if (this.props.teamType === Constants.OPEN_TEAM && global.window.mm_config.EnableUserCreation === 'true') {
- teamLink = (
- <li>
- <a
- href='#'
- onClick={GlobalActions.showGetTeamInviteLinkModal}
- >
- <FormattedMessage
- id='navbar_dropdown.teamLink'
- defaultMessage='Get Team Invite Link'
- />
- </a>
- </li>
- );
- }
- }
-
- if (isAdmin) {
- manageLink = (
- <li>
- <ToggleModalButton dialogType={TeamMembersModal}>
- <FormattedMessage
- id='navbar_dropdown.manageMembers'
- defaultMessage='Manage Members'
- />
- </ToggleModalButton>
- </li>
- );
-
- adminDivider = (<li className='divider'></li>);
-
- teamSettings = (
- <li>
- <a
- href='#'
- data-toggle='modal'
- data-target='#team_settings'
- >
- <FormattedMessage
- id='navbar_dropdown.teamSettings'
- defaultMessage='Team Settings'
- />
- </a>
- </li>
- );
- }
-
- if (isSystemAdmin) {
- sysAdminLink = (
- <li>
- <a
- href={'/admin_console'}
- >
- <FormattedMessage
- id='navbar_dropdown.console'
- defaultMessage='System Console'
- />
- </a>
- </li>
- );
- }
-
- var teams = [];
-
- if (global.window.mm_config.EnableTeamCreation === 'true') {
- teams.push(
- <li key='newTeam_li'>
- <a
- key='newTeam_a'
- target='_blank'
- href={Utils.getWindowLocationOrigin() + '/signup_team'}
- >
- <FormattedMessage
- id='navbar_dropdown.create'
- defaultMessage='Create a New Team'
- />
- </a>
- </li>
- );
- }
-
- let helpLink = null;
- if (global.window.mm_config.HelpLink) {
- helpLink = (
- <li>
- <a
- target='_blank'
- href={global.window.mm_config.HelpLink}
- >
- <FormattedMessage
- id='navbar_dropdown.help'
- defaultMessage='Help'
- />
- </a>
- </li>
- );
- }
-
- let reportLink = null;
- if (global.window.mm_config.ReportAProblemLink) {
- reportLink = (
- <li>
- <a
- target='_blank'
- href={global.window.mm_config.ReportAProblemLink}
- >
- <FormattedMessage
- id='navbar_dropdown.report'
- defaultMessage='Report a Problem'
- />
- </a>
- </li>
- );
- }
-
- return (
- <ul className='nav navbar-nav navbar-right'>
- <li
- ref='dropdown'
- className='dropdown'
- >
- <a
- href='#'
- className='dropdown-toggle'
- data-toggle='dropdown'
- role='button'
- aria-expanded='false'
- >
- <span
- className='dropdown__icon'
- dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}}
- />
- </a>
- <ul
- className='dropdown-menu'
- role='menu'
- >
- <li>
- <a
- href='#'
- onClick={() => this.setState({showUserSettingsModal: true})}
- >
- <FormattedMessage
- id='navbar_dropdown.accountSettings'
- defaultMessage='Account Settings'
- />
- </a>
- </li>
- {inviteLink}
- {teamLink}
- <li>
- <Link to={'/' + this.props.teamName + '/logout'}>
- <FormattedMessage
- id='navbar_dropdown.logout'
- defaultMessage='Logout'
- />
- </Link>
- </li>
- {adminDivider}
- {teamSettings}
- {manageLink}
- {sysAdminLink}
- {teams}
- <li className='divider'></li>
- {helpLink}
- {reportLink}
- <li>
- <a
- href='#'
- onClick={this.handleAboutModal}
- >
- <FormattedMessage
- id='navbar_dropdown.about'
- defaultMessage='About Mattermost'
- />
- </a>
- </li>
- <UserSettingsModal
- show={this.state.showUserSettingsModal}
- onModalDismissed={() => this.setState({showUserSettingsModal: false})}
- />
- <AboutBuildModal
- show={this.state.showAboutModal}
- onModalDismissed={this.aboutModalDismissed}
- />
- </ul>
- </li>
- </ul>
- );
- }
-}
-
-NavbarDropdown.defaultProps = {
- teamType: ''
-};
-NavbarDropdown.propTypes = {
- teamType: React.PropTypes.string,
- teamDisplayName: React.PropTypes.string,
- teamName: React.PropTypes.string,
- currentUser: React.PropTypes.object
-};
diff --git a/web/react/components/needs_team.jsx b/web/react/components/needs_team.jsx
deleted file mode 100644
index 33b9cd37e..000000000
--- a/web/react/components/needs_team.jsx
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-export default class NeedsTeam extends React.Component {
- componentWillMount() {
- GlobalActions.loadTeamRequiredPage();
- }
- render() {
- return this.props.children;
- }
-}
-
-NeedsTeam.defaultProps = {
-};
-
-NeedsTeam.propTypes = {
- children: React.PropTypes.object
-};
diff --git a/web/react/components/new_channel_flow.jsx b/web/react/components/new_channel_flow.jsx
deleted file mode 100644
index a0bb14e8f..000000000
--- a/web/react/components/new_channel_flow.jsx
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-import NewChannelModal from './new_channel_modal.jsx';
-import ChangeURLModal from './change_url_modal.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const SHOW_NEW_CHANNEL = 1;
-const SHOW_EDIT_URL = 2;
-const SHOW_EDIT_URL_THEN_COMPLETE = 3;
-const messages = defineMessages({
- invalidName: {
- id: 'channel_flow.invalidName',
- defaultMessage: 'Invalid Channel Name'
- },
- alreadyExist: {
- id: 'channel_flow.alreadyExist',
- defaultMessage: 'A channel with that URL already exists'
- },
- channel: {
- id: 'channel_flow.channel',
- defaultMessage: 'Channel'
- },
- group: {
- id: 'channel_flow.group',
- defaultMessage: 'Group'
- },
- change: {
- id: 'channel_flow.changeUrlTitle',
- defaultMessage: 'Change {term} URL'
- },
- set: {
- id: 'channel_flow.set_url_title',
- defaultMessage: 'Set {term} URL'
- },
- create: {
- id: 'channel_flow.create',
- defaultMessage: 'Create {term}'
- },
- changeUrlDescription: {
- id: 'channel_flow.changeUrlDescription',
- defaultMessage: 'Some characters are not allowed in URLs and may be removed.'
- }
-});
-
-class NewChannelFlow extends React.Component {
- constructor(props) {
- super(props);
-
- this.doSubmit = this.doSubmit.bind(this);
- this.typeSwitched = this.typeSwitched.bind(this);
- this.urlChangeRequested = this.urlChangeRequested.bind(this);
- this.urlChangeSubmitted = this.urlChangeSubmitted.bind(this);
- this.urlChangeDismissed = this.urlChangeDismissed.bind(this);
- this.channelDataChanged = this.channelDataChanged.bind(this);
-
- this.state = {
- serverError: '',
- channelType: 'O',
- flowState: SHOW_NEW_CHANNEL,
- channelDisplayName: '',
- channelName: '',
- channelPurpose: '',
- nameModified: false
- };
- }
- componentWillReceiveProps(nextProps) {
- // If we are being shown, grab channel type from props and clear
- if (nextProps.show === true && this.props.show === false) {
- this.setState({
- serverError: '',
- channelType: nextProps.channelType,
- flowState: SHOW_NEW_CHANNEL,
- channelDisplayName: '',
- channelName: '',
- channelPurpose: '',
- nameModified: false
- });
- }
- }
- doSubmit() {
- var channel = {};
-
- const {formatMessage} = this.props.intl;
- channel.display_name = this.state.channelDisplayName;
- if (!channel.display_name) {
- this.setState({serverError: formatMessage(messages.invalidName)});
- return;
- }
-
- channel.name = this.state.channelName;
- if (channel.name.length < 2) {
- this.setState({flowState: SHOW_EDIT_URL_THEN_COMPLETE});
- return;
- }
-
- const cu = UserStore.getCurrentUser();
- channel.team_id = cu.team_id;
- channel.purpose = this.state.channelPurpose;
- channel.type = this.state.channelType;
-
- Client.createChannel(channel,
- (data) => {
- this.props.onModalDismissed();
- AsyncClient.getChannel(data.id);
- Utils.switchChannel(data);
- },
- (err) => {
- if (err.id === 'model.channel.is_valid.2_or_more.app_error') {
- this.setState({flowState: SHOW_EDIT_URL_THEN_COMPLETE});
- }
- if (err.id === 'store.sql_channel.update.exists.app_error') {
- this.setState({serverError: formatMessage(messages.alreadyExist)});
- return;
- }
- this.setState({serverError: err.message});
- }
- );
- }
- typeSwitched() {
- if (this.state.channelType === 'P') {
- this.setState({channelType: 'O'});
- } else {
- this.setState({channelType: 'P'});
- }
- }
- urlChangeRequested() {
- this.setState({flowState: SHOW_EDIT_URL});
- }
- urlChangeSubmitted(newURL) {
- if (this.state.flowState === SHOW_EDIT_URL_THEN_COMPLETE) {
- this.setState({channelName: newURL, nameModified: true}, this.doSubmit);
- } else {
- this.setState({flowState: SHOW_NEW_CHANNEL, serverError: '', channelName: newURL, nameModified: true});
- }
- }
- urlChangeDismissed() {
- this.setState({flowState: SHOW_NEW_CHANNEL});
- }
- channelDataChanged(data) {
- this.setState({
- channelDisplayName: data.displayName,
- channelPurpose: data.purpose
- });
- if (!this.state.nameModified) {
- this.setState({channelName: Utils.cleanUpUrlable(data.displayName.trim())});
- }
- }
- render() {
- const channelData = {
- name: this.state.channelName,
- displayName: this.state.channelDisplayName,
- purpose: this.state.channelPurpose
- };
-
- let showChannelModal = false;
- let showGroupModal = false;
- let showChangeURLModal = false;
-
- let changeURLTitle = '';
- let changeURLSubmitButtonText = '';
- let channelTerm = '';
-
- const {formatMessage} = this.props.intl;
-
- // Only listen to flow state if we are being shown
- if (this.props.show) {
- switch (this.state.flowState) {
- case SHOW_NEW_CHANNEL:
- if (this.state.channelType === 'O') {
- showChannelModal = true;
- channelTerm = formatMessage(messages.channel);
- } else {
- showGroupModal = true;
- channelTerm = formatMessage(messages.group);
- }
- break;
- case SHOW_EDIT_URL:
- showChangeURLModal = true;
- changeURLTitle = formatMessage(messages.change, {term: channelTerm});
- changeURLSubmitButtonText = formatMessage(messages.change, {term: channelTerm});
- break;
- case SHOW_EDIT_URL_THEN_COMPLETE:
- showChangeURLModal = true;
- changeURLTitle = formatMessage(messages.set, {term: channelTerm});
- changeURLSubmitButtonText = formatMessage(messages.create, {term: channelTerm});
- break;
- }
- }
- return (
- <span>
- <NewChannelModal
- show={showChannelModal}
- channelType={'O'}
- channelData={channelData}
- serverError={this.state.serverError}
- onSubmitChannel={this.doSubmit}
- onModalDismissed={this.props.onModalDismissed}
- onTypeSwitched={this.typeSwitched}
- onChangeURLPressed={this.urlChangeRequested}
- onDataChanged={this.channelDataChanged}
- />
- <NewChannelModal
- show={showGroupModal}
- channelType={'P'}
- channelData={channelData}
- serverError={this.state.serverError}
- onSubmitChannel={this.doSubmit}
- onModalDismissed={this.props.onModalDismissed}
- onTypeSwitched={this.typeSwitched}
- onChangeURLPressed={this.urlChangeRequested}
- onDataChanged={this.channelDataChanged}
- />
- <ChangeURLModal
- show={showChangeURLModal}
- title={changeURLTitle}
- description={formatMessage(messages.changeUrlDescription)}
- urlLabel={channelTerm + ' URL'}
- submitButtonText={changeURLSubmitButtonText}
- currentURL={this.state.channelName}
- serverError={this.state.serverError}
- onModalSubmit={this.urlChangeSubmitted}
- onModalDismissed={this.urlChangeDismissed}
- />
- </span>
- );
- }
-}
-
-NewChannelFlow.defaultProps = {
- show: false,
- channelType: 'O'
-};
-
-NewChannelFlow.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- channelType: React.PropTypes.string.isRequired,
- onModalDismissed: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(NewChannelFlow); \ No newline at end of file
diff --git a/web/react/components/new_channel_modal.jsx b/web/react/components/new_channel_modal.jsx
deleted file mode 100644
index 788e6dc1b..000000000
--- a/web/react/components/new_channel_modal.jsx
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-var Modal = ReactBootstrap.Modal;
-
-const holders = defineMessages({
- nameEx: {
- id: 'channel_modal.nameEx',
- defaultMessage: 'E.g.: "Bugs", "Marketing", "办公室恋情"'
- }
-});
-
-class NewChannelModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleChange = this.handleChange.bind(this);
-
- this.state = {
- displayNameError: ''
- };
- }
- componentWillReceiveProps(nextProps) {
- if (nextProps.show === true && this.props.show === false) {
- this.setState({
- displayNameError: ''
- });
- }
- }
- componentDidMount() {
- if (Utils.isBrowserIE()) {
- $('body').addClass('browser--IE');
- }
- }
- handleSubmit(e) {
- e.preventDefault();
-
- const displayName = ReactDOM.findDOMNode(this.refs.display_name).value.trim();
- if (displayName.length < 1) {
- this.setState({displayNameError: true});
- return;
- }
-
- this.props.onSubmitChannel();
- }
- handleChange() {
- const newData = {
- displayName: ReactDOM.findDOMNode(this.refs.display_name).value,
- purpose: ReactDOM.findDOMNode(this.refs.channel_purpose).value
- };
- this.props.onDataChanged(newData);
- }
- render() {
- var displayNameError = null;
- var serverError = null;
- var displayNameClass = 'form-group';
-
- if (this.state.displayNameError) {
- displayNameError = (
- <p className='input__help error'>
- <FormattedMessage
- id='channel_modal.displayNameError'
- defaultMessage='This field is required'
- />
- {this.state.displayNameError}
- </p>
- );
- displayNameClass += ' has-error';
- }
-
- if (this.props.serverError) {
- serverError = <div className='form-group has-error'><p className='input__help error'>{this.props.serverError}</p></div>;
- }
-
- var channelTerm = '';
- var channelSwitchText = '';
- switch (this.props.channelType) {
- case 'P':
- channelTerm = (
- <FormattedMessage
- id='channel_modal.group'
- defaultMessage='Group'
- />
- );
- channelSwitchText = (
- <div className='modal-intro'>
- <FormattedMessage
- id='channel_modal.privateGroup1'
- defaultMessage='Create a new private group with restricted membership. '
- />
- <a
- href='#'
- onClick={this.props.onTypeSwitched}
- >
- <FormattedMessage
- id='channel_modal.publicChannel1'
- defaultMessage='Create a public channel'
- />
- </a>
- </div>
- );
- break;
- case 'O':
- channelTerm = (
- <FormattedMessage
- id='channel_modal.channel'
- defaultMessage='Channel'
- />
- );
- channelSwitchText = (
- <div className='modal-intro'>
- <FormattedMessage
- id='channel_modal.publicChannel2'
- defaultMessage='Create a new public channel anyone can join. '
- />
- <a
- href='#'
- onClick={this.props.onTypeSwitched}
- >
- <FormattedMessage
- id='channel_modal.privateGroup2'
- defaultMessage='Create a private group'
- />
- </a>
- </div>
- );
- break;
- }
-
- const prettyTeamURL = Utils.getShortenedTeamURL();
-
- return (
- <span>
- <Modal
- show={this.props.show}
- bsSize='large'
- onHide={this.props.onModalDismissed}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='channel_modal.modalTitle'
- defaultMessage='New '
- />
- {channelTerm}
- </Modal.Title>
- </Modal.Header>
- <form
- role='form'
- className='form-horizontal'
- >
- <Modal.Body>
- <div>
- {channelSwitchText}
- </div>
- <div className={displayNameClass}>
- <label className='col-sm-3 form__label control-label'>
- <FormattedMessage
- id='channel_modal.name'
- defaultMessage='Name'
- />
- </label>
- <div className='col-sm-9'>
- <input
- onChange={this.handleChange}
- type='text'
- ref='display_name'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.nameEx)}
- maxLength='22'
- value={this.props.channelData.displayName}
- autoFocus={true}
- tabIndex='1'
- />
- {displayNameError}
- <p className='input__help dark'>
- {'URL: ' + prettyTeamURL + this.props.channelData.name + ' ('}
- <a
- href='#'
- onClick={this.props.onChangeURLPressed}
- >
- <FormattedMessage
- id='channel_modal.edit'
- defaultMessage='Edit'
- />
- </a>
- {')'}
- </p>
- </div>
- </div>
- <div className='form-group less'>
- <div className='col-sm-3'>
- <label className='form__label control-label'>
- <FormattedMessage
- id='channel_modal.purpose'
- defaultMessage='Purpose'
- />
- </label>
- <label className='form__label light'>
- <FormattedMessage
- id='channel_modal.optional'
- defaultMessage='(optional)'
- />
- </label>
- </div>
- <div className='col-sm-9'>
- <textarea
- className='form-control no-resize'
- ref='channel_purpose'
- rows='4'
- placeholder={this.props.intl.formatMessage({id: 'channel_modal.purpose'})}
- maxLength='128'
- value={this.props.channelData.purpose}
- onChange={this.handleChange}
- tabIndex='2'
- />
- <p className='input__help'>
- <FormattedMessage
- id='channel_modal.descriptionHelp'
- defaultMessage='Describe how this {term} should be used.'
- values={{
- term: (channelTerm)
- }}
- />
- </p>
- {serverError}
- </div>
- </div>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onModalDismissed}
- >
- <FormattedMessage
- id='channel_modal.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- onClick={this.handleSubmit}
- type='submit'
- className='btn btn-primary'
- tabIndex='3'
- >
- <FormattedMessage
- id='channel_modal.createNew'
- defaultMessage='Create New '
- />
- {channelTerm}
- </button>
- </Modal.Footer>
- </form>
- </Modal>
- </span>
- );
- }
-}
-
-NewChannelModal.defaultProps = {
- show: false,
- channelType: 'O',
- serverError: ''
-};
-NewChannelModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- channelType: React.PropTypes.string.isRequired,
- channelData: React.PropTypes.object.isRequired,
- serverError: React.PropTypes.string,
- onSubmitChannel: React.PropTypes.func.isRequired,
- onModalDismissed: React.PropTypes.func.isRequired,
- onTypeSwitched: React.PropTypes.func.isRequired,
- onChangeURLPressed: React.PropTypes.func.isRequired,
- onDataChanged: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(NewChannelModal); \ No newline at end of file
diff --git a/web/react/components/not_logged_in.jsx b/web/react/components/not_logged_in.jsx
deleted file mode 100644
index cf3effcd5..000000000
--- a/web/react/components/not_logged_in.jsx
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class NotLoggedIn extends React.Component {
- componentDidMount() {
- $('body').attr('class', 'sticky');
- $('#root').attr('class', 'container-fluid');
- }
- componentWillUnmount() {
- $('body').attr('class', '');
- $('#root').attr('class', '');
- }
- render() {
- return (
- <div className='inner-wrap'>
- <div className='row content'>
- {this.props.children}
- <div className='footer-push'></div>
- </div>
- <div className='row footer'>
- <div className='footer-pane col-xs-12'>
- <div className='col-xs-12'>
- <span className='pull-right footer-site-name'>{global.window.mm_config.SiteName}</span>
- </div>
- <div className='col-xs-12'>
- <span className='pull-right footer-link copyright'>{'© 2015 Mattermost, Inc.'}</span>
- <a
- id='help_link'
- className='pull-right footer-link'
- href={global.window.mm_config.HelpLink}
- >
- <FormattedMessage id='web.footer.help'/>
- </a>
- <a
- id='terms_link'
- className='pull-right footer-link'
- href={global.window.mm_config.TermsOfServiceLink}
- >
- <FormattedMessage id='web.footer.terms'/>
- </a>
- <a
- id='privacy_link'
- className='pull-right footer-link'
- href={global.window.mm_config.PrivacyPolicyLink}
- >
- <FormattedMessage id='web.footer.privacy'/>
- </a>
- <a
- id='about_link'
- className='pull-right footer-link'
- href={global.window.mm_config.AboutLink}
- >
- <FormattedMessage id='web.footer.about'/>
- </a>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-NotLoggedIn.defaultProps = {
-};
-
-NotLoggedIn.propTypes = {
- children: React.PropTypes.object
-};
diff --git a/web/react/components/notify_counts.jsx b/web/react/components/notify_counts.jsx
deleted file mode 100644
index 19b81556b..000000000
--- a/web/react/components/notify_counts.jsx
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as utils from '../utils/utils.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-
-function getCountsStateFromStores() {
- var count = 0;
- var channels = ChannelStore.getAll();
- var members = ChannelStore.getAllMembers();
-
- channels.forEach(function setChannelInfo(channel) {
- var channelMember = members[channel.id];
- if (channel.type === 'D') {
- count += channel.total_msg_count - channelMember.msg_count;
- } else if (channelMember.mention_count > 0) {
- count += channelMember.mention_count;
- } else if (channelMember.notify_props.mark_unread !== 'mention' && channel.total_msg_count - channelMember.msg_count > 0) {
- count += 1;
- }
- });
-
- return {count: count};
-}
-
-export default class NotifyCounts extends React.Component {
- constructor(props) {
- super(props);
-
- this.onListenerChange = this.onListenerChange.bind(this);
-
- this.state = getCountsStateFromStores();
- }
- componentDidMount() {
- ChannelStore.addChangeListener(this.onListenerChange);
- }
- componentWillUnmount() {
- ChannelStore.removeChangeListener(this.onListenerChange);
- }
- onListenerChange() {
- var newState = getCountsStateFromStores();
- if (!utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- }
- render() {
- if (this.state.count) {
- return <span className='badge badge-notify'>{this.state.count}</span>;
- }
- return null;
- }
-}
diff --git a/web/react/components/password_reset_form.jsx b/web/react/components/password_reset_form.jsx
deleted file mode 100644
index cfd39e440..000000000
--- a/web/react/components/password_reset_form.jsx
+++ /dev/null
@@ -1,129 +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 {FormattedMessage} from 'mm-intl';
-import {browserHistory} from 'react-router';
-
-class PasswordResetForm extends React.Component {
- constructor(props) {
- super(props);
-
- this.handlePasswordReset = this.handlePasswordReset.bind(this);
-
- this.state = {};
- }
- handlePasswordReset(e) {
- e.preventDefault();
-
- const password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
- this.setState({
- error: (
- <FormattedMessage
- id='password_form.error'
- defaultMessage='Please enter at least {chars} characters.'
- chars={Constants.MIN_PASSWORD_LENGTH}
- />
- )
- });
- return;
- }
-
- this.setState({
- error: null
- });
-
- const data = {};
- data.new_password = password;
- data.hash = this.props.location.query.h;
- data.data = this.props.location.query.d;
- data.name = this.props.params.team;
-
- Client.resetPassword(data,
- () => {
- this.setState({error: null});
- browserHistory.push('/' + this.props.params.team + '/login');
- },
- (err) => {
- this.setState({error: err.message});
- }
- );
- }
- render() {
- var error = null;
- if (this.state.error) {
- error = (
- <div className='form-group has-error'>
- <label className='control-label'>
- {this.state.error}
- </label>
- </div>
- );
- }
-
- var formClass = 'form-group';
- if (error) {
- formClass += ' has-error';
- }
-
- return (
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <h3>
- <FormattedMessage
- id='password_form.title'
- defaultMessage='Password Reset'
- />
- </h3>
- <form onSubmit={this.handlePasswordReset}>
- <p>
- <FormattedMessage
- id='password_form.enter'
- defaultMessage='Enter a new password for your {siteName} account.'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </p>
- <div className={formClass}>
- <input
- type='password'
- className='form-control'
- name='password'
- ref='password'
- placeholder={Utils.localizeMessage(
- 'password_form.pwd',
- 'Password'
- )}
- spellCheck='false'
- />
- </div>
- {error}
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='password_form.change'
- defaultMessage='Change my password'
- />
- </button>
- </form>
- </div>
- </div>
- );
- }
-}
-
-PasswordResetForm.defaultProps = {
-};
-PasswordResetForm.propTypes = {
- params: React.PropTypes.object.isRequired,
- location: React.PropTypes.object.isRequired
-};
-
-export default PasswordResetForm;
diff --git a/web/react/components/password_reset_send_link.jsx b/web/react/components/password_reset_send_link.jsx
deleted file mode 100644
index ce6253e16..000000000
--- a/web/react/components/password_reset_send_link.jsx
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as client from '../utils/client.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-class PasswordResetSendLink extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSendLink = this.handleSendLink.bind(this);
-
- this.state = {
- error: '',
- updateText: ''
- };
- }
- handleSendLink(e) {
- e.preventDefault();
-
- var email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
- if (!email || !Utils.isEmail(email)) {
- this.setState({
- error: (
- <FormattedMessage
- id={'password_send.error'}
- defaultMessage={'Please enter a valid email address.'}
- />
- )
- });
- return;
- }
-
- // End of error checking clear error
- this.setState({
- error: ''
- });
-
- var data = {};
- data.email = email;
- data.name = this.props.params.team;
- client.sendPasswordReset(data,
- () => {
- this.setState({
- error: null,
- updateText: (
- <div className='reset-form alert alert-success'>
- <FormattedHTMLMessage
- id='password_send.link'
- defaultMessage='<p>A password reset link has been sent to <b>{email}</b></p>'
- email={email}
- />
- <FormattedMessage
- id={'password_send.checkInbox'}
- defaultMessage={'Please check your inbox.'}
- />
- </div>
- )
- });
- $(ReactDOM.findDOMNode(this.refs.reset_form)).hide();
- },
- (err) => {
- this.setState({
- error: err.message,
- update_text: null
- });
- }
- );
- }
- render() {
- var error = null;
- if (this.state.error) {
- error = <div className='form-group has-error'><label className='control-label'>{this.state.error}</label></div>;
- }
-
- var formClass = 'form-group';
- if (error) {
- formClass += ' has-error';
- }
-
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <h3>
- <FormattedMessage
- id='password_send.title'
- defaultMessage='Password Reset'
- />
- </h3>
- {this.state.updateText}
- <form
- onSubmit={this.handleSendLink}
- ref='reset_form'
- >
- <p>
- <FormattedMessage
- id='password_send.description'
- defaultMessage='To reset your password, enter the email address you used to sign up'
- />
- </p>
- <div className={formClass}>
- <input
- type='email'
- className='form-control'
- name='email'
- ref='email'
- placeholder={Utils.localizeMessage(
- 'password_send.email',
- 'Email'
- )}
- spellCheck='false'
- />
- </div>
- {error}
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='password_send.reset'
- defaultMessage='Reset my password'
- />
- </button>
- </form>
- </div>
- </div>
- </div>
- );
- }
-}
-
-PasswordResetSendLink.defaultProps = {
-};
-PasswordResetSendLink.propTypes = {
- params: React.PropTypes.object.isRequired
-};
-
-export default PasswordResetSendLink;
diff --git a/web/react/components/popover_list_members.jsx b/web/react/components/popover_list_members.jsx
deleted file mode 100644
index 8f6948ee4..000000000
--- a/web/react/components/popover_list_members.jsx
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../stores/user_store.jsx';
-var Popover = ReactBootstrap.Popover;
-var Overlay = ReactBootstrap.Overlay;
-import * as Utils from '../utils/utils.jsx';
-import Constants from '../utils/constants.jsx';
-
-import ChannelStore from '../stores/channel_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class PopoverListMembers extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this);
- this.closePopover = this.closePopover.bind(this);
- }
-
- componentWillMount() {
- this.setState({showPopover: false});
- }
-
- componentDidMount() {
- const originalLeave = $.fn.popover.Constructor.prototype.leave;
- $.fn.popover.Constructor.prototype.leave = function onLeave(obj) {
- let selfObj;
- if (obj instanceof this.constructor) {
- selfObj = obj;
- } else {
- selfObj = $(obj.currentTarget)[this.type](this.getDelegateOptions()).data(`bs.${this.type}`);
- }
- originalLeave.call(this, obj);
-
- if (obj.currentTarget && selfObj.$tip) {
- selfObj.$tip.one('mouseenter', function onMouseEnter() {
- clearTimeout(selfObj.timeout);
- selfObj.$tip.one('mouseleave', function onMouseLeave() {
- $.fn.popover.Constructor.prototype.leave.call(selfObj, selfObj);
- });
- });
- }
- };
- }
-
- componentDidUpdate() {
- $(ReactDOM.findDOMNode(this.refs.memebersPopover)).find('.popover-content').perfectScrollbar();
- }
-
- handleShowDirectChannel(teammate, e) {
- e.preventDefault();
-
- Utils.openDirectChannelToUser(
- teammate,
- (channel, channelAlreadyExisted) => {
- Utils.switchChannel(channel);
- if (channelAlreadyExisted) {
- this.closePopover();
- }
- },
- () => {
- this.closePopover();
- }
- );
- }
-
- closePopover() {
- this.setState({showPopover: false});
- }
-
- render() {
- const popoverHtml = [];
- const members = this.props.members;
- const teamMembers = UserStore.getProfilesUsernameMap();
- const currentUserId = UserStore.getCurrentId();
- const ch = ChannelStore.getCurrent();
-
- if (members && teamMembers) {
- members.sort((a, b) => {
- const aName = Utils.displayUsername(a.id);
- const bName = Utils.displayUsername(b.id);
-
- return aName.localeCompare(bName);
- });
-
- members.forEach((m, i) => {
- let button = '';
- if (currentUserId !== m.id && ch.type !== 'D') {
- button = (
- <a
- href='#'
- className='btn-message'
- onClick={(e) => this.handleShowDirectChannel(m, e)}
- >
- <FormattedMessage
- id='members_popover.msg'
- defaultMessage='Message'
- />
- </a>
- );
- }
-
- let name = '';
- if (teamMembers[m.username]) {
- name = Utils.displayUsername(teamMembers[m.username].id);
- }
-
- if (name) {
- popoverHtml.push(
- <div
- className='more-modal__row'
- key={'popover-member-' + i}
- >
-
- <img
- className='more-modal__image'
- width='26px'
- height='26px'
- src={`/api/v1/users/${m.id}/image?time=${m.update_at}`}
- />
- <div className='more-modal__details'>
- <div
- className='more-modal__name'
- >
- {name}
- </div>
- </div>
- <div
- className='more-modal__actions'
- >
- {button}
- </div>
- </div>
- );
- }
- });
- }
-
- let count = this.props.memberCount;
- let countText = '-';
-
- // fall back to checking the length of the member list if the count isn't set
- if (!count && members) {
- count = members.length;
- }
-
- if (count > Constants.MAX_CHANNEL_POPOVER_COUNT) {
- countText = Constants.MAX_CHANNEL_POPOVER_COUNT + '+';
- } else if (count > 0) {
- countText = count.toString();
- }
-
- const title = (
- <FormattedMessage
- id='members_popover.title'
- defaultMessage='Members'
- />
- );
- return (
- <div>
- <div
- id='member_popover'
- ref='member_popover_target'
- onClick={(e) => this.setState({popoverTarget: e.target, showPopover: !this.state.showPopover})}
- >
- <div>
- {countText}
- <span
- className='fa fa-user'
- aria-hidden='true'
- />
- </div>
- </div>
- <Overlay
- rootClose={true}
- onHide={this.closePopover}
- show={this.state.showPopover}
- target={() => this.state.popoverTarget}
- placement='bottom'
- >
- <Popover
- ref='memebersPopover'
- title={title}
- id='member-list-popover'
- >
- <div className='more-modal__list'>{popoverHtml}</div>
- </Popover>
- </Overlay>
- </div>
- );
- }
-}
-
-PopoverListMembers.propTypes = {
- members: React.PropTypes.array.isRequired,
- memberCount: React.PropTypes.number,
- channelId: React.PropTypes.string.isRequired
-};
diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx
deleted file mode 100644
index 3a855edf2..000000000
--- a/web/react/components/post.jsx
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostHeader from './post_header.jsx';
-import PostBody from './post_body.jsx';
-
-import PostStore from '../stores/post_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Utils from '../utils/utils.jsx';
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-
-export default class Post extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleCommentClick = this.handleCommentClick.bind(this);
- this.forceUpdateInfo = this.forceUpdateInfo.bind(this);
- this.retryPost = this.retryPost.bind(this);
-
- this.state = {};
- }
- handleCommentClick(e) {
- e.preventDefault();
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: Utils.getRootId(this.props.post)
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null
- });
- }
- forceUpdateInfo() {
- this.refs.info.forceUpdate();
- this.refs.header.forceUpdate();
- }
- retryPost(e) {
- e.preventDefault();
-
- var post = this.props.post;
- Client.createPost(post, post.channel_id,
- (data) => {
- AsyncClient.getPosts();
-
- var channel = ChannelStore.get(post.channel_id);
- var member = ChannelStore.getMember(post.channel_id);
- member.msg_count = channel.total_msg_count;
- member.last_viewed_at = Utils.getTimestamp();
- ChannelStore.setChannelMember(member);
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST,
- post: data
- });
- },
- () => {
- post.state = Constants.POST_FAILED;
- PostStore.updatePendingPost(post);
- this.forceUpdate();
- }
- );
-
- post.state = Constants.POST_LOADING;
- PostStore.updatePendingPost(post);
- this.forceUpdate();
- }
- shouldComponentUpdate(nextProps) {
- if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
- return true;
- }
-
- if (nextProps.sameRoot !== this.props.sameRoot) {
- return true;
- }
-
- if (nextProps.sameUser !== this.props.sameUser) {
- return true;
- }
-
- if (nextProps.displayNameType !== this.props.displayNameType) {
- return true;
- }
-
- if (this.getCommentCount(nextProps) !== this.getCommentCount(this.props)) {
- return true;
- }
-
- if (nextProps.shouldHighlight !== this.props.shouldHighlight) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(nextProps.user, this.props.user)) {
- return true;
- }
-
- return false;
- }
- getCommentCount(props) {
- const post = props.post;
- const parentPost = props.parentPost;
- const posts = props.posts;
-
- let commentCount = 0;
- let commentRootId;
- if (parentPost) {
- commentRootId = post.root_id;
- } else {
- commentRootId = post.id;
- }
- for (const postId in posts) {
- if (posts[postId].root_id === commentRootId) {
- commentCount += 1;
- }
- }
-
- return commentCount;
- }
- render() {
- const post = this.props.post;
- const parentPost = this.props.parentPost;
- const posts = this.props.posts;
-
- if (!post.props) {
- post.props = {};
- }
-
- let type = 'Post';
- if (post.root_id && post.root_id.length > 0) {
- type = 'Comment';
- }
-
- const commentCount = this.getCommentCount(this.props);
-
- let rootUser;
- if (this.props.sameRoot) {
- rootUser = 'same--root';
- } else {
- rootUser = 'other--root';
- }
-
- let postType = '';
- if (type !== 'Post') {
- postType = 'post--comment';
- } else if (commentCount > 0) {
- postType = 'post--root';
- }
-
- let currentUserCss = '';
- if (this.props.currentUser.id === post.user_id && !post.props.from_webhook && !Utils.isSystemMessage(post)) {
- currentUserCss = 'current--user';
- }
-
- let timestamp = 0;
- if (!this.props.user || this.props.user.update_at == null) {
- timestamp = this.props.currentUser.update_at;
- } else {
- timestamp = this.props.user.update_at;
- }
-
- let sameUserClass = '';
- if (this.props.sameUser) {
- sameUserClass = 'same--user';
- }
-
- let shouldHighlightClass = '';
- if (this.props.shouldHighlight) {
- shouldHighlightClass = 'post--highlight';
- }
-
- let systemMessageClass = '';
- if (Utils.isSystemMessage(post)) {
- systemMessageClass = 'post--system';
- }
-
- let profilePic = null;
- if (!this.props.hideProfilePic) {
- let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp;
- if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
- if (post.props.override_icon_url) {
- src = post.props.override_icon_url;
- }
- } else if (Utils.isSystemMessage(post)) {
- src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
- }
-
- profilePic = (
- <img
- src={src}
- height='36'
- width='36'
- />
- );
- }
-
- return (
- <div>
- <div
- id={'post_' + post.id}
- className={'post ' + sameUserClass + ' ' + rootUser + ' ' + postType + ' ' + currentUserCss + ' ' + shouldHighlightClass + ' ' + systemMessageClass}
- >
- <div className='post__content'>
- <div className='post__img'>{profilePic}</div>
- <div>
- <PostHeader
- ref='header'
- post={post}
- sameRoot={this.props.sameRoot}
- commentCount={commentCount}
- handleCommentClick={this.handleCommentClick}
- isLastComment={this.props.isLastComment}
- sameUser={this.props.sameUser}
- user={this.props.user}
- currentUser={this.props.currentUser}
- />
- <PostBody
- post={post}
- sameRoot={this.props.sameRoot}
- parentPost={parentPost}
- posts={posts}
- handleCommentClick={this.handleCommentClick}
- retryPost={this.retryPost}
- />
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-Post.propTypes = {
- post: React.PropTypes.object.isRequired,
- posts: React.PropTypes.object,
- parentPost: React.PropTypes.object,
- user: React.PropTypes.object,
- sameUser: React.PropTypes.bool,
- sameRoot: React.PropTypes.bool,
- hideProfilePic: React.PropTypes.bool,
- isLastComment: React.PropTypes.bool,
- shouldHighlight: React.PropTypes.bool,
- displayNameType: React.PropTypes.string,
- hasProfiles: React.PropTypes.bool,
- currentUser: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/post_attachment.jsx b/web/react/components/post_attachment.jsx
deleted file mode 100644
index 2eedfb7c1..000000000
--- a/web/react/components/post_attachment.jsx
+++ /dev/null
@@ -1,312 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as TextFormatting from '../utils/text_formatting.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- collapse: {
- id: 'post_attachment.collapse',
- defaultMessage: '▲ collapse text'
- },
- more: {
- id: 'post_attachment.more',
- defaultMessage: '▼ read more'
- }
-});
-
-class PostAttachment extends React.Component {
- constructor(props) {
- super(props);
-
- this.getFieldsTable = this.getFieldsTable.bind(this);
- this.getInitState = this.getInitState.bind(this);
- this.shouldCollapse = this.shouldCollapse.bind(this);
- this.toggleCollapseState = this.toggleCollapseState.bind(this);
- }
-
- componentDidMount() {
- $(this.refs.attachment).on('click', '.attachment-link-more', this.toggleCollapseState);
- }
-
- componentWillUnmount() {
- $(this.refs.attachment).off('click', '.attachment-link-more', this.toggleCollapseState);
- }
-
- componentWillMount() {
- this.setState(this.getInitState());
- }
-
- getInitState() {
- const shouldCollapse = this.shouldCollapse();
- const text = TextFormatting.formatText(this.props.attachment.text || '');
- const uncollapsedText = text + (shouldCollapse ? `<a class="attachment-link-more" href="#">${this.props.intl.formatMessage(holders.collapse)}</a>` : '');
- const collapsedText = shouldCollapse ? this.getCollapsedText() : text;
-
- return {
- shouldCollapse,
- collapsedText,
- uncollapsedText,
- text: shouldCollapse ? collapsedText : uncollapsedText,
- collapsed: shouldCollapse
- };
- }
-
- toggleCollapseState(e) {
- e.preventDefault();
-
- let state = this.state;
- state.text = state.collapsed ? state.uncollapsedText : state.collapsedText;
- state.collapsed = !state.collapsed;
- this.setState(state);
- }
-
- shouldCollapse() {
- const text = this.props.attachment.text || '';
- return (text.match(/\n/g) || []).length >= 5 || text.length > 700;
- }
-
- getCollapsedText() {
- let text = this.props.attachment.text || '';
- if ((text.match(/\n/g) || []).length >= 5) {
- text = text.split('\n').splice(0, 5).join('\n');
- } else if (text.length > 700) {
- text = text.substr(0, 700);
- }
-
- return TextFormatting.formatText(text) + `<a class="attachment-link-more" href="#">${this.props.intl.formatMessage(holders.more)}</a>`;
- }
-
- getFieldsTable() {
- const fields = this.props.attachment.fields;
- if (!fields || !fields.length) {
- return '';
- }
-
- const compactTable = fields.filter((field) => field.short).length > 0;
- let tHead;
- let tBody;
-
- if (compactTable) {
- let headerCols = [];
- let bodyCols = [];
-
- fields.forEach((field, i) => {
- headerCols.push(
- <th
- className='attachment___field-caption'
- key={'attachment__field-caption-' + i}
- >
- {field.title}
- </th>
- );
- bodyCols.push(
- <td
- className='attachment___field'
- key={'attachment__field-' + i}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(field.value || '')}}
- >
- </td>
- );
- });
-
- tHead = (
- <tr>
- {headerCols}
- </tr>
- );
- tBody = (
- <tr>
- {bodyCols}
- </tr>
- );
- } else {
- tBody = [];
-
- fields.forEach((field, i) => {
- tBody.push(
- <tr key={'attachment__field-' + i}>
- <td
- className='attachment___field-caption'
- >
- {field.title}
- </td>
- <td
- className='attachment___field'
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(field.value || '')}}
- >
- </td>
- </tr>
- );
- });
- }
-
- return (
- <table
- className='attachment___fields'
- >
- <thead>
- {tHead}
- </thead>
- <tbody>
- {tBody}
- </tbody>
- </table>
- );
- }
-
- render() {
- const data = this.props.attachment;
-
- let preText;
- if (data.pretext) {
- preText = (
- <div
- className='attachment__thumb-pretext'
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(data.pretext)}}
- >
- </div>
- );
- }
-
- let author = [];
- if (data.author_name || data.author_icon) {
- if (data.author_icon) {
- author.push(
- <img
- className='attachment__author-icon'
- src={data.author_icon}
- key={'attachment__author-icon'}
- height='14'
- width='14'
- />
- );
- }
- if (data.author_name) {
- author.push(
- <span
- className='attachment__author-name'
- key={'attachment__author-name'}
- >
- {data.author_name}
- </span>
- );
- }
- }
- if (data.author_link) {
- author = (
- <a
- href={data.author_link}
- target='_blank'
- >
- {author}
- </a>
- );
- }
-
- let title;
- if (data.title) {
- if (data.title_link) {
- title = (
- <h1
- className='attachment__title'
- >
- <a
- className='attachment__title-link'
- href={data.title_link}
- target='_blank'
- >
- {data.title}
- </a>
- </h1>
- );
- } else {
- title = (
- <h1
- className='attachment__title'
- >
- {data.title}
- </h1>
- );
- }
- }
-
- let text;
- if (data.text) {
- text = (
- <div
- className='attachment__text'
- dangerouslySetInnerHTML={{__html: this.state.text}}
- >
- </div>
- );
- }
-
- let image;
- if (data.image_url) {
- image = (
- <img
- className='attachment__image'
- src={data.image_url}
- />
- );
- }
-
- let thumb;
- if (data.thumb_url) {
- thumb = (
- <div
- className='attachment__thumb-container'
- >
- <img
- src={data.thumb_url}
- />
- </div>
- );
- }
-
- const fields = this.getFieldsTable();
-
- let useBorderStyle;
- if (data.color && data.color[0] === '#') {
- useBorderStyle = {borderLeftColor: data.color};
- }
-
- return (
- <div
- className='attachment'
- ref='attachment'
- >
- {preText}
- <div className='attachment__content'>
- <div
- className={useBorderStyle ? 'clearfix attachment__container' : 'clearfix attachment__container attachment__container--' + data.color}
- style={useBorderStyle}
- >
- {author}
- {title}
- <div>
- <div
- className={thumb ? 'attachment__body' : 'attachment__body attachment__body--no_thumb'}
- >
- {text}
- {image}
- {fields}
- </div>
- {thumb}
- <div style={{clear: 'both'}}></div>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-PostAttachment.propTypes = {
- intl: intlShape.isRequired,
- attachment: React.PropTypes.object.isRequired
-};
-
-export default injectIntl(PostAttachment);
diff --git a/web/react/components/post_attachment_list.jsx b/web/react/components/post_attachment_list.jsx
deleted file mode 100644
index ae03e15d3..000000000
--- a/web/react/components/post_attachment_list.jsx
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostAttachment from './post_attachment.jsx';
-
-export default class PostAttachmentList extends React.Component {
- render() {
- let content = [];
- this.props.attachments.forEach((attachment, i) => {
- content.push(
- <PostAttachment
- attachment={attachment}
- key={'att_' + i}
- />
- );
- });
-
- return (
- <div className='attachment_list'>
- {content}
- </div>
- );
- }
-}
-
-PostAttachmentList.propTypes = {
- attachments: React.PropTypes.array.isRequired
-};
diff --git a/web/react/components/post_attachment_oembed.jsx b/web/react/components/post_attachment_oembed.jsx
deleted file mode 100644
index d76e5f02e..000000000
--- a/web/react/components/post_attachment_oembed.jsx
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-export default class PostAttachmentOEmbed extends React.Component {
- constructor(props) {
- super(props);
- this.fetchData = this.fetchData.bind(this);
-
- this.isLoading = false;
- }
-
- componentWillMount() {
- this.setState({data: {}});
- }
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.link !== this.props.link) {
- this.isLoading = false;
- this.fetchData(nextProps.link);
- }
- }
-
- componentDidMount() {
- this.fetchData(this.props.link);
- }
-
- fetchData(link) {
- if (!this.isLoading) {
- this.isLoading = true;
- let url = 'https://noembed.com/embed?nowrap=on';
- url += '&url=' + encodeURIComponent(link);
- url += '&maxheight=' + this.props.provider.height;
- return $.ajax({
- url,
- dataType: 'jsonp',
- success: (result) => {
- this.isLoading = false;
- if (result.error) {
- this.setState({data: {}});
- } else {
- this.setState({data: result});
- }
- },
- error: () => {
- this.setState({data: {}});
- }
- });
- }
- return null;
- }
-
- render() {
- let data = {};
- let content;
- if ($.isEmptyObject(this.state.data)) {
- content = <div style={{height: this.props.provider.height}}/>;
- } else {
- data = this.state.data;
- content = (
- <div
- style={{height: this.props.provider.height}}
- dangerouslySetInnerHTML={{__html: data.html}}
- />
- );
- }
-
- return (
- <div
- className='attachment attachment--oembed'
- ref='attachment'
- >
- <div className='attachment__content'>
- <div
- className={'clearfix attachment__container'}
- >
- <h1
- className='attachment__title'
- >
- <a
- className='attachment__title-link'
- href={data.url}
- target='_blank'
- >
- {data.title}
- </a>
- </h1>
- <div >
- <div
- className={'attachment__body attachment__body--no_thumb'}
- >
- {content}
- </div>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-PostAttachmentOEmbed.propTypes = {
- link: React.PropTypes.string.isRequired,
- provider: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
deleted file mode 100644
index 2fa4cebfe..000000000
--- a/web/react/components/post_body.jsx
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import FileAttachmentList from './file_attachment_list.jsx';
-import UserStore from '../stores/user_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-import * as Emoji from '../utils/emoticons.jsx';
-import Constants from '../utils/constants.jsx';
-import * as TextFormatting from '../utils/text_formatting.jsx';
-import twemoji from 'twemoji';
-import PostBodyAdditionalContent from './post_body_additional_content.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- plusOne: {
- id: 'post_body.plusOne',
- defaultMessage: ' plus 1 other file'
- },
- plusMore: {
- id: 'post_body.plusMore',
- defaultMessage: ' plus {count} other files'
- }
-});
-
-class PostBody extends React.Component {
- constructor(props) {
- super(props);
-
- this.parseEmojis = this.parseEmojis.bind(this);
- }
-
- getAllChildNodes(nodeIn) {
- var textNodes = [];
-
- function getTextNodes(node) {
- textNodes.push(node);
-
- for (var i = 0, len = node.childNodes.length; i < len; ++i) {
- getTextNodes(node.childNodes[i]);
- }
- }
-
- getTextNodes(nodeIn);
- return textNodes;
- }
-
- parseEmojis() {
- twemoji.parse(ReactDOM.findDOMNode(this), {
- className: 'emoji twemoji',
- base: '',
- folder: Emoji.getImagePathForEmoticon()
- });
- }
-
- componentDidMount() {
- this.parseEmojis();
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- const post = this.props.post;
- const filenames = this.props.post.filenames;
- const parentPost = this.props.parentPost;
-
- let comment = '';
- let postClass = '';
-
- if (parentPost) {
- const profile = UserStore.getProfile(parentPost.user_id);
-
- let apostrophe = '';
- let name = '...';
- if (profile != null) {
- let username = profile.username;
- if (parentPost.props &&
- parentPost.props.from_webhook &&
- parentPost.props.override_username &&
- global.window.mm_config.EnablePostUsernameOverride === 'true') {
- username = parentPost.props.override_username;
- }
-
- if (username.slice(-1) === 's') {
- apostrophe = '\'';
- } else {
- apostrophe = '\'s';
- }
- name = (
- <a
- className='theme'
- onClick={Utils.searchForTerm.bind(null, username)}
- >
- {username}
- </a>
- );
- }
-
- let message = '';
- if (parentPost.message) {
- message = Utils.replaceHtmlEntities(parentPost.message);
- } else if (parentPost.filenames.length) {
- message = parentPost.filenames[0].split('/').pop();
-
- if (parentPost.filenames.length === 2) {
- message += formatMessage(holders.plusOne);
- } else if (parentPost.filenames.length > 2) {
- message += formatMessage(holders.plusMore, {count: (parentPost.filenames.length - 1)});
- }
- }
-
- comment = (
- <div className='post__link'>
- <span>
- <FormattedMessage
- id='post_body.commentedOn'
- defaultMessage='Commented on {name}{apostrophe} message: '
- values={{
- name: (name),
- apostrophe: apostrophe
- }}
- />
- <a
- className='theme'
- onClick={this.props.handleCommentClick}
- >
- {message}
- </a>
- </span>
- </div>
- );
- }
-
- let loading;
- if (post.state === Constants.POST_FAILED) {
- postClass += ' post--fail';
- loading = (
- <a
- className='theme post-retry pull-right'
- href='#'
- onClick={this.props.retryPost}
- >
- <FormattedMessage
- id='post_body.retry'
- defaultMessage='Retry'
- />
- </a>
- );
- } else if (post.state === Constants.POST_LOADING) {
- postClass += ' post-waiting';
- loading = (
- <img
- className='post-loading-gif pull-right'
- src='/static/images/load.gif'
- />
- );
- }
-
- let fileAttachmentHolder = '';
- if (filenames && filenames.length > 0) {
- fileAttachmentHolder = (
- <FileAttachmentList
- filenames={filenames}
- channelId={post.channel_id}
- userId={post.user_id}
- />
- );
- }
-
- let message;
- let additionalContent = null;
- if (this.props.post.state === Constants.POST_DELETED) {
- message = (
- <FormattedMessage
- id='post_body.deleted'
- defaultMessage='(message deleted)'
- />
- );
- } else {
- message = (
- <span
- onClick={TextFormatting.handleClick}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message)}}
- />
- );
-
- additionalContent = (
- <PostBodyAdditionalContent post={this.props.post}/>
- );
- }
-
- return (
- <div>
- {comment}
- <div className='post__body'>
- <div
- key={`${post.id}_message`}
- id={`${post.id}_message`}
- className={postClass}
- >
- {loading}
- {message}
- </div>
- {fileAttachmentHolder}
- {additionalContent}
- </div>
- </div>
- );
- }
-}
-
-PostBody.propTypes = {
- intl: intlShape.isRequired,
- post: React.PropTypes.object.isRequired,
- parentPost: React.PropTypes.object,
- retryPost: React.PropTypes.func.isRequired,
- handleCommentClick: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(PostBody);
diff --git a/web/react/components/post_body_additional_content.jsx b/web/react/components/post_body_additional_content.jsx
deleted file mode 100644
index 70b3c8dbf..000000000
--- a/web/react/components/post_body_additional_content.jsx
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostAttachmentList from './post_attachment_list.jsx';
-import PostAttachmentOEmbed from './post_attachment_oembed.jsx';
-import PostImage from './post_image.jsx';
-import YoutubeVideo from './youtube_video.jsx';
-
-import Constants from '../utils/constants.jsx';
-import OEmbedProviders from './providers.json';
-import * as Utils from '../utils/utils.jsx';
-
-export default class PostBodyAdditionalContent extends React.Component {
- constructor(props) {
- super(props);
-
- this.getSlackAttachment = this.getSlackAttachment.bind(this);
- this.getOEmbedProvider = this.getOEmbedProvider.bind(this);
- this.generateEmbed = this.generateEmbed.bind(this);
- this.toggleEmbedVisibility = this.toggleEmbedVisibility.bind(this);
-
- this.state = {
- embedVisible: true
- };
- }
-
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
- return true;
- }
- if (nextState.embedVisible !== this.state.embedVisible) {
- return true;
- }
- return false;
- }
-
- toggleEmbedVisibility() {
- this.setState({embedVisible: !this.state.embedVisible});
- }
-
- getSlackAttachment() {
- let attachments = [];
- if (this.props.post.props && this.props.post.props.attachments) {
- attachments = this.props.post.props.attachments;
- }
-
- return (
- <PostAttachmentList
- attachments={attachments}
- />
- );
- }
-
- getOEmbedProvider(link) {
- for (let i = 0; i < OEmbedProviders.length; i++) {
- for (let j = 0; j < OEmbedProviders[i].patterns.length; j++) {
- if (link.match(OEmbedProviders[i].patterns[j])) {
- return OEmbedProviders[i];
- }
- }
- }
-
- return null;
- }
-
- generateEmbed() {
- if (this.props.post.type === 'slack_attachment') {
- return this.getSlackAttachment();
- }
-
- const link = Utils.extractLinks(this.props.post.message)[0];
- if (!link) {
- return null;
- }
-
- if (Utils.isFeatureEnabled(Constants.PRE_RELEASE_FEATURES.EMBED_PREVIEW)) {
- const provider = this.getOEmbedProvider(link);
-
- if (provider) {
- return (
- <PostAttachmentOEmbed
- provider={provider}
- link={link}
- />
- );
- }
- }
-
- if (YoutubeVideo.isYoutubeLink(link)) {
- return (
- <YoutubeVideo
- channelId={this.props.post.channel_id}
- link={link}
- />
- );
- }
-
- for (let i = 0; i < Constants.IMAGE_TYPES.length; i++) {
- const imageType = Constants.IMAGE_TYPES[i];
- const suffix = link.substring(link.length - (imageType.length + 1));
- if (suffix === '.' + imageType || suffix === '=' + imageType) {
- return (
- <PostImage
- channelId={this.props.post.channel_id}
- link={link}
- />
- );
- }
- }
-
- return null;
- }
-
- render() {
- const generateEmbed = this.generateEmbed();
-
- if (generateEmbed) {
- let toggle;
- if (Utils.isFeatureEnabled(Constants.PRE_RELEASE_FEATURES.EMBED_TOGGLE)) {
- toggle = (
- <a className='post__embed-visibility'
- data-expanded={this.state.embedVisible}
- aria-label='Toggle Embed Visibility'
- onClick={this.toggleEmbedVisibility}
- />
- );
- }
-
- return (
- <div>
- {toggle}
- <div className='post__embed-container'
- hidden={!this.state.embedVisible}
- >
- {generateEmbed}
- </div>
- </div>
- );
- }
-
- return null;
- }
-}
-
-PostBodyAdditionalContent.propTypes = {
- post: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/post_deleted_modal.jsx b/web/react/components/post_deleted_modal.jsx
deleted file mode 100644
index 1f5c15aa9..000000000
--- a/web/react/components/post_deleted_modal.jsx
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-var ActionTypes = Constants.ActionTypes;
-
-const Modal = ReactBootstrap.Modal;
-
-export default class PostDeletedModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleHide = this.handleHide.bind(this);
- }
-
- shouldComponentUpdate(nextProps) {
- return nextProps.show !== this.props.show;
- }
-
- handleHide(e) {
- e.preventDefault();
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH_TERM,
- term: null,
- do_search: false,
- is_mention_search: false
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
-
- this.props.onHide();
- }
-
- render() {
- return (
- <Modal
- show={this.props.show}
- onHide={this.handleHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='post_delete.notPosted'
- defaultMessage='Comment could not be posted'
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <p>
- <FormattedMessage
- id='post_delete.someone'
- defaultMessage='Someone deleted the message on which you tried to post a comment.'
- />
- </p>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-primary'
- onClick={this.handleHide}
- >
- <FormattedMessage
- id='post_delete.okay'
- defaultMessage='Okay'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-PostDeletedModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/post_focus_view.jsx b/web/react/components/post_focus_view.jsx
deleted file mode 100644
index fd654f502..000000000
--- a/web/react/components/post_focus_view.jsx
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostsView from './posts_view.jsx';
-
-import PostStore from '../stores/post_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class PostFocusView extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChannelChange = this.onChannelChange.bind(this);
- this.onPostsChange = this.onPostsChange.bind(this);
- this.onUserChange = this.onUserChange.bind(this);
- this.handlePostsViewScroll = this.handlePostsViewScroll.bind(this);
- this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
- this.loadMorePostsBottom = this.loadMorePostsBottom.bind(this);
-
- const focusedPostId = PostStore.getFocusedPostId();
-
- this.state = {
- scrollType: PostsView.SCROLL_TYPE_POST,
- scrollPostId: focusedPostId,
- postList: PostStore.getVisiblePosts(focusedPostId),
- atTop: PostStore.getVisibilityAtTop(focusedPostId),
- atBottom: PostStore.getVisibilityAtBottom(focusedPostId),
- currentUser: UserStore.getCurrentUser()
- };
- }
-
- componentDidMount() {
- ChannelStore.addChangeListener(this.onChannelChange);
- PostStore.addChangeListener(this.onPostsChange);
- UserStore.addChangeListener(this.onUserChange);
- }
-
- componentWillUnmount() {
- ChannelStore.removeChangeListener(this.onChannelChange);
- PostStore.removeChangeListener(this.onPostsChange);
- UserStore.removeChangeListener(this.onUserChange);
- }
-
- onChannelChange() {
- this.setState({
- scrollType: PostsView.SCROLL_TYPE_POST
- });
- }
-
- onUserChange() {
- this.setState({currentUser: UserStore.getCurrentUser()});
- }
-
- onPostsChange() {
- const focusedPostId = PostStore.getFocusedPostId();
- if (focusedPostId == null) {
- return;
- }
-
- this.setState({
- scrollPostId: focusedPostId,
- postList: PostStore.getVisiblePosts(focusedPostId),
- atTop: PostStore.getVisibilityAtTop(focusedPostId),
- atBottom: PostStore.getVisibilityAtBottom(focusedPostId)
- });
- }
-
- handlePostsViewScroll() {
- this.setState({scrollType: PostsView.SCROLL_TYPE_FREE});
- }
-
- loadMorePostsTop() {
- GlobalActions.emitLoadMorePostsFocusedTopEvent();
- }
-
- loadMorePostsBottom() {
- GlobalActions.emitLoadMorePostsFocusedBottomEvent();
- }
-
- getIntroMessage() {
- return (
- <div className='channel-intro'>
- <h4 className='channel-intro__title'>
- <FormattedMessage
- id='post_focus_view.beginning'
- defaultMessage='Beginning of Channel Archives'
- />
- </h4>
- </div>
- );
- }
-
- render() {
- const postsToHighlight = {};
- postsToHighlight[this.state.scrollPostId] = true;
-
- if (!this.state.currentUser || !this.state.postList) {
- return null;
- }
-
- return (
- <div id='post-list'>
- <PostsView
- key={'postfocusview'}
- isActive={true}
- postList={this.state.postList}
- scrollType={this.state.scrollType}
- scrollPostId={this.state.scrollPostId}
- postViewScrolled={this.handlePostsViewScroll}
- loadMorePostsTopClicked={this.loadMorePostsTop}
- loadMorePostsBottomClicked={this.loadMorePostsBottom}
- showMoreMessagesTop={!this.state.atTop}
- showMoreMessagesBottom={!this.state.atBottom}
- introText={this.getIntroMessage()}
- messageSeparatorTime={0}
- postsToHighlight={postsToHighlight}
- profiles={this.props.profiles}
- currentUser={this.state.currentUser}
- />
- </div>
- );
- }
-}
-PostFocusView.defaultProps = {
-};
-
-PostFocusView.propTypes = {
- profiles: React.PropTypes.object
-};
diff --git a/web/react/components/post_header.jsx b/web/react/components/post_header.jsx
deleted file mode 100644
index 966775dad..000000000
--- a/web/react/components/post_header.jsx
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserProfile from './user_profile.jsx';
-import PostInfo from './post_info.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-export default class PostHeader extends React.Component {
- constructor(props) {
- super(props);
- this.state = {};
- }
- render() {
- const post = this.props.post;
-
- let userProfile = <UserProfile user={this.props.user}/>;
- let botIndicator;
-
- if (post.props && post.props.from_webhook) {
- if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') {
- userProfile = (
- <UserProfile
- user={this.props.user}
- overwriteName={post.props.override_username}
- disablePopover={true}
- />
- );
- }
-
- botIndicator = <li className='col col__name bot-indicator'>{'BOT'}</li>;
- } else if (Utils.isSystemMessage(post)) {
- userProfile = (
- <UserProfile
- user={{}}
- overwriteName={Constants.SYSTEM_MESSAGE_PROFILE_NAME}
- overwriteImage={Constants.SYSTEM_MESSAGE_PROFILE_IMAGE}
- disablePopover={true}
- />
- );
- }
-
- return (
- <ul className='post__header'>
- <li className='col col__name'>{userProfile}</li>
- {botIndicator}
- <li className='col'>
- <PostInfo
- post={post}
- commentCount={this.props.commentCount}
- handleCommentClick={this.props.handleCommentClick}
- allowReply='true'
- isLastComment={this.props.isLastComment}
- sameUser={this.props.sameUser}
- currentUser={this.props.currentUser}
- />
- </li>
- </ul>
- );
- }
-}
-
-PostHeader.defaultProps = {
- post: null,
- commentCount: 0,
- isLastComment: false,
- sameUser: false
-};
-PostHeader.propTypes = {
- post: React.PropTypes.object.isRequired,
- user: React.PropTypes.object,
- currentUser: React.PropTypes.object.isRequired,
- commentCount: React.PropTypes.number.isRequired,
- isLastComment: React.PropTypes.bool.isRequired,
- handleCommentClick: React.PropTypes.func.isRequired,
- sameUser: React.PropTypes.bool.isRequired
-};
diff --git a/web/react/components/post_image.jsx b/web/react/components/post_image.jsx
deleted file mode 100644
index da4a25794..000000000
--- a/web/react/components/post_image.jsx
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-export default class PostImageEmbed extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleLoadComplete = this.handleLoadComplete.bind(this);
- this.handleLoadError = this.handleLoadError.bind(this);
-
- this.state = {
- loaded: false,
- errored: false
- };
- }
-
- componentWillMount() {
- this.loadImg(this.props.link);
- }
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.link !== this.props.link) {
- this.setState({
- loaded: false,
- errored: false
- });
- }
- }
-
- componentDidUpdate(prevProps) {
- if (!this.state.loaded && prevProps.link !== this.props.link) {
- this.loadImg(this.props.link);
- }
- }
-
- loadImg(src) {
- const img = new Image();
- img.onload = this.handleLoadComplete;
- img.onerror = this.handleLoadError;
- img.src = src;
- }
-
- handleLoadComplete() {
- this.setState({
- loaded: true
- });
- }
-
- handleLoadError() {
- this.setState({
- errored: true,
- loaded: true
- });
- }
-
- render() {
- if (this.state.errored) {
- return null;
- }
-
- if (!this.state.loaded) {
- return (
- <img
- className='img-div placeholder'
- height='500px'
- />
- );
- }
-
- return (
- <img
- className='img-div'
- src={this.props.link}
- />
- );
- }
-}
-
-PostImageEmbed.propTypes = {
- link: React.PropTypes.string.isRequired
-};
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
deleted file mode 100644
index d0a4c828e..000000000
--- a/web/react/components/post_info.jsx
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import TimeSince from './time_since.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class PostInfo extends React.Component {
- constructor(props) {
- super(props);
-
- this.dropdownPosition = this.dropdownPosition.bind(this);
- this.handlePermalink = this.handlePermalink.bind(this);
- this.removePost = this.removePost.bind(this);
- }
- dropdownPosition(e) {
- var position = $('#post-list').height() - $(e.target).offset().top;
- var dropdown = $(e.target).next('.dropdown-menu');
- if (position < dropdown.height()) {
- dropdown.addClass('bottom');
- }
- }
- createDropdown() {
- var post = this.props.post;
- var isOwner = this.props.currentUser.id === post.user_id;
- var isAdmin = Utils.isAdmin(this.props.currentUser.roles);
-
- if (post.state === Constants.POST_FAILED || post.state === Constants.POST_LOADING || Utils.isPostEphemeral(post)) {
- return '';
- }
-
- var type = 'Post';
- if (post.root_id && post.root_id.length > 0) {
- type = 'Comment';
- }
-
- var dropdownContents = [];
- var dataComments = 0;
- if (type === 'Post') {
- dataComments = this.props.commentCount;
- }
-
- if (this.props.allowReply === 'true') {
- dropdownContents.push(
- <li
- key='replyLink'
- role='presentation'
- >
- <a
- className='link__reply theme'
- href='#'
- onClick={this.props.handleCommentClick}
- >
- <FormattedMessage
- id='post_info.reply'
- defaultMessage='Reply'
- />
- </a>
- </li>
- );
- }
-
- if (!Utils.isMobile()) {
- dropdownContents.push(
- <li
- key='copyLink'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.handlePermalink}
- >
- <FormattedMessage
- id='post_info.permalink'
- defaultMessage='Permalink'
- />
- </a>
- </li>
- );
- }
-
- if (isOwner || isAdmin) {
- dropdownContents.push(
- <li
- key='deletePost'
- role='presentation'
- >
- <a
- href='#'
- role='menuitem'
- onClick={() => GlobalActions.showDeletePostModal(post, dataComments)}
- >
- <FormattedMessage
- id='post_info.del'
- defaultMessage='Delete'
- />
- </a>
- </li>
- );
- }
-
- if (isOwner) {
- dropdownContents.push(
- <li
- key='editPost'
- role='presentation'
- >
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#edit_post'
- data-refocusid='#post_textbox'
- data-title={type}
- data-message={post.message}
- data-postid={post.id}
- data-channelid={post.channel_id}
- data-comments={dataComments}
- >
- <FormattedMessage
- id='post_info.edit'
- defaultMessage='Edit'
- />
- </a>
- </li>
- );
- }
-
- if (dropdownContents.length === 0) {
- return '';
- }
-
- return (
- <div>
- <a
- href='#'
- className='dropdown-toggle post__dropdown theme'
- type='button'
- data-toggle='dropdown'
- aria-expanded='false'
- onClick={this.dropdownPosition}
- />
- <ul
- className='dropdown-menu'
- role='menu'
- >
- {dropdownContents}
- </ul>
- </div>
- );
- }
-
- handlePermalink(e) {
- e.preventDefault();
- GlobalActions.showGetPostLinkModal(this.props.post);
- }
-
- removePost() {
- GlobalActions.emitRemovePost(this.props.post);
- }
- createRemovePostButton(post) {
- if (!Utils.isPostEphemeral(post)) {
- return null;
- }
-
- return (
- <a
- href='#'
- className='post__remove theme'
- type='button'
- onClick={this.removePost}
- >
- {'×'}
- </a>
- );
- }
- render() {
- var post = this.props.post;
- var comments = '';
- var showCommentClass = '';
- var commentCountText = this.props.commentCount;
-
- if (this.props.commentCount >= 1) {
- showCommentClass = ' icon--show';
- } else {
- commentCountText = '';
- }
-
- if (post.state !== Constants.POST_FAILED && post.state !== Constants.POST_LOADING && !Utils.isPostEphemeral(post)) {
- comments = (
- <a
- href='#'
- className={'comment-icon__container' + showCommentClass}
- onClick={this.props.handleCommentClick}
- >
- <span
- className='comment-icon'
- dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
- />
- {commentCountText}
- </a>
- );
- }
-
- var dropdown = this.createDropdown();
-
- return (
- <ul className='post__header post__header--info'>
- <li className='col'>
- <TimeSince
- eventTime={post.create_at}
- sameUser={this.props.sameUser}
- />
- </li>
- <li className='col col__reply'>
- <div
- className='dropdown'
- ref='dotMenu'
- >
- {dropdown}
- </div>
- {comments}
- {this.createRemovePostButton(post)}
- </li>
- </ul>
- );
- }
-}
-
-PostInfo.defaultProps = {
- post: null,
- commentCount: 0,
- isLastComment: false,
- allowReply: false,
- sameUser: false
-};
-PostInfo.propTypes = {
- post: React.PropTypes.object.isRequired,
- commentCount: React.PropTypes.number.isRequired,
- isLastComment: React.PropTypes.bool.isRequired,
- allowReply: React.PropTypes.string.isRequired,
- handleCommentClick: React.PropTypes.func.isRequired,
- sameUser: React.PropTypes.bool.isRequired,
- currentUser: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx
deleted file mode 100644
index 0a9232850..000000000
--- a/web/react/components/posts_view.jsx
+++ /dev/null
@@ -1,604 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PreferenceStore from '../stores/preference_store.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import * as Utils from '../utils/utils.jsx';
-import Post from './post.jsx';
-import Constants from '../utils/constants.jsx';
-import DelayedAction from '../utils/delayed_action.jsx';
-
-import {FormattedDate, FormattedMessage} from 'mm-intl';
-
-const Preferences = Constants.Preferences;
-
-export default class PostsView extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateState = this.updateState.bind(this);
- this.handleScroll = this.handleScroll.bind(this);
- this.handleScrollStop = this.handleScrollStop.bind(this);
- this.isAtBottom = this.isAtBottom.bind(this);
- this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
- this.loadMorePostsBottom = this.loadMorePostsBottom.bind(this);
- this.createPosts = this.createPosts.bind(this);
- this.updateScrolling = this.updateScrolling.bind(this);
- this.handleResize = this.handleResize.bind(this);
- this.scrollToBottom = this.scrollToBottom.bind(this);
- this.scrollToBottomAnimated = this.scrollToBottomAnimated.bind(this);
-
- this.jumpToPostNode = null;
- this.wasAtBottom = true;
- this.scrollHeight = 0;
-
- this.scrollStopAction = new DelayedAction(this.handleScrollStop);
-
- this.state = {
- displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false'),
- isScrolling: false,
- topPostId: null
- };
- }
- static get SCROLL_TYPE_FREE() {
- return 1;
- }
- static get SCROLL_TYPE_BOTTOM() {
- return 2;
- }
- static get SCROLL_TYPE_SIDEBAR_OPEN() {
- return 3;
- }
- static get SCROLL_TYPE_NEW_MESSAGE() {
- return 4;
- }
- static get SCROLL_TYPE_POST() {
- return 5;
- }
- updateState() {
- this.setState({displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false')});
- }
- isAtBottom() {
- // consider the view to be at the bottom if it's within this many pixels of the bottom
- const atBottomMargin = 10;
-
- return this.refs.postlist.clientHeight + this.refs.postlist.scrollTop >= this.refs.postlist.scrollHeight - atBottomMargin;
- }
- handleScroll() {
- // HACK FOR RHS -- REMOVE WHEN RHS DIES
- const childNodes = this.refs.postlistcontent.childNodes;
- for (let i = 0; i < childNodes.length; i++) {
- // If the node is 1/3 down the page
- if (childNodes[i].offsetTop > (this.refs.postlist.scrollTop + (this.refs.postlist.offsetHeight / 3))) {
- this.jumpToPostNode = childNodes[i];
- break;
- }
- }
- this.wasAtBottom = this.isAtBottom();
- if (!this.jumpToPostNode && childNodes.length > 0) {
- this.jumpToPostNode = childNodes[childNodes.length - 1];
- }
-
- // --- --------
-
- this.props.postViewScrolled(this.isAtBottom());
- this.prevScrollHeight = this.refs.postlist.scrollHeight;
- this.prevOffsetTop = this.jumpToPostNode.offsetTop;
-
- this.updateFloatingTimestamp();
-
- if (!this.state.isScrolling) {
- this.setState({
- isScrolling: true
- });
- }
-
- this.scrollStopAction.fireAfter(2000);
- }
- handleScrollStop() {
- this.setState({
- isScrolling: false
- });
- }
- updateFloatingTimestamp() {
- // skip this in non-mobile view since that's when the timestamp is visible
- if ($(window).width() > 768) {
- return;
- }
-
- if (this.props.postList) {
- // iterate through posts starting at the bottom since users are more likely to be viewing newer posts
- for (let i = 0; i < this.props.postList.order.length; i++) {
- const id = this.props.postList.order[i];
- const element = ReactDOM.findDOMNode(this.refs[id]);
-
- if (!element || element.offsetTop + element.clientHeight <= this.refs.postlist.scrollTop) {
- // this post is off the top of the screen so the last one is at the top of the screen
- let topPostId;
-
- if (i > 0) {
- topPostId = this.props.postList.order[i - 1];
- } else {
- // the first post we look at should always be on the screen, but handle that case anyway
- topPostId = id;
- }
-
- if (topPostId !== this.state.topPostId) {
- this.setState({
- topPostId
- });
- }
-
- break;
- }
- }
- }
- }
- loadMorePostsTop() {
- this.props.loadMorePostsTopClicked();
- }
- loadMorePostsBottom() {
- this.props.loadMorePostsBottomClicked();
- }
- createPosts(posts, order) {
- const postCtls = [];
- let previousPostDay = new Date(0);
- const userId = this.props.currentUser.id;
- const profiles = this.props.profiles || {};
-
- let renderedLastViewed = false;
-
- for (let i = order.length - 1; i >= 0; i--) {
- const post = posts[order[i]];
- const parentPost = posts[post.parent_id];
- const prevPost = posts[order[i + 1]];
- const postUserId = Utils.isSystemMessage(post) ? '' : post.user_id;
-
- // If the post is a comment whose parent has been deleted, don't add it to the list.
- if (parentPost && parentPost.state === Constants.POST_DELETED) {
- continue;
- }
-
- let sameUser = false;
- let sameRoot = false;
- let hideProfilePic = false;
-
- if (prevPost) {
- const postIsComment = Utils.isComment(post);
- const prevPostIsComment = Utils.isComment(prevPost);
- const postFromWebhook = Boolean(post.props && post.props.from_webhook);
- const prevPostFromWebhook = Boolean(prevPost.props && prevPost.props.from_webhook);
- const prevPostUserId = Utils.isSystemMessage(prevPost) ? '' : prevPost.user_id;
- let prevWebhookName = '';
- if (prevPost.props && prevPost.props.override_username) {
- prevWebhookName = prevPost.props.override_username;
- }
- let curWebhookName = '';
- if (post.props && post.props.override_username) {
- curWebhookName = post.props.override_username;
- }
-
- // consider posts from the same user if:
- // the previous post was made by the same user as the current post,
- // the previous post was made within 5 minutes of the current post,
- // the previous post and current post are both from webhooks or both not,
- // the previous post and current post have the same webhook usernames
- if (prevPostUserId === postUserId &&
- post.create_at - prevPost.create_at <= 1000 * 60 * 5 &&
- postFromWebhook === prevPostFromWebhook &&
- prevWebhookName === curWebhookName) {
- sameUser = true;
- }
-
- // consider posts from the same root if:
- // the current post is a comment,
- // the current post has the same root as the previous post
- if (postIsComment && (prevPost.id === post.root_id || prevPost.root_id === post.root_id)) {
- sameRoot = true;
- }
-
- // consider posts from the same root if:
- // the current post is not a comment,
- // the previous post is not a comment,
- // the previous post is from the same user
- if (!postIsComment && !prevPostIsComment && sameUser) {
- sameRoot = true;
- }
-
- // hide the profile pic if:
- // the previous post was made by the same user as the current post,
- // the previous post is not a comment,
- // the current post is not a comment,
- // the previous post and current post are both from webhooks or both not,
- // the previous post and current post have the same webhook usernames
- if (prevPostUserId === postUserId &&
- !prevPostIsComment &&
- !postIsComment &&
- postFromWebhook === prevPostFromWebhook &&
- prevWebhookName === curWebhookName) {
- hideProfilePic = true;
- }
- }
-
- // check if it's the last comment in a consecutive string of comments on the same post
- // it is the last comment if it is last post in the channel or the next post has a different root post
- const isLastComment = Utils.isComment(post) && (i === 0 || posts[order[i - 1]].root_id !== post.root_id);
-
- const keyPrefix = post.id ? post.id : i;
-
- const shouldHighlight = this.props.postsToHighlight && this.props.postsToHighlight.hasOwnProperty(post.id);
-
- let profile;
- if (this.props.currentUser.id === post.user_id) {
- profile = this.props.currentUser;
- } else {
- profile = profiles[post.user_id];
- }
-
- const postCtl = (
- <Post
- key={keyPrefix + 'postKey'}
- ref={post.id}
- sameUser={sameUser}
- sameRoot={sameRoot}
- post={post}
- parentPost={parentPost}
- posts={posts}
- hideProfilePic={hideProfilePic}
- isLastComment={isLastComment}
- shouldHighlight={shouldHighlight}
- onClick={() => GlobalActions.emitPostFocusEvent(post.id)} //eslint-disable-line no-loop-func
- displayNameType={this.state.displayNameType}
- user={profile}
- currentUser={this.props.currentUser}
- />
- );
-
- const currentPostDay = Utils.getDateForUnixTicks(post.create_at);
- if (currentPostDay.toDateString() !== previousPostDay.toDateString()) {
- postCtls.push(
- <div
- key={currentPostDay.toDateString()}
- className='date-separator'
- >
- <hr className='separator__hr'/>
- <div className='separator__text'>
- <FormattedDate
- value={currentPostDay}
- weekday='short'
- month='short'
- day='2-digit'
- year='numeric'
- />
- </div>
- </div>
- );
- }
-
- if (postUserId !== userId &&
- this.props.messageSeparatorTime !== 0 &&
- post.create_at > this.props.messageSeparatorTime &&
- !renderedLastViewed) {
- renderedLastViewed = true;
-
- // Temporary fix to solve ie11 rendering issue
- let newSeparatorId = '';
- if (!Utils.isBrowserIE()) {
- newSeparatorId = 'new_message_' + post.id;
- }
- postCtls.push(
- <div
- id={newSeparatorId}
- key='unviewed'
- ref='newMessageSeparator'
- className='new-separator'
- >
- <hr
- className='separator__hr'
- />
- <div className='separator__text'>
- <FormattedMessage
- id='posts_view.newMsg'
- defaultMessage='New Messages'
- />
- </div>
- </div>
- );
- }
- postCtls.push(postCtl);
- previousPostDay = currentPostDay;
- }
-
- return postCtls;
- }
- updateScrolling() {
- if (this.props.scrollType === PostsView.SCROLL_TYPE_BOTTOM) {
- this.scrollToBottom();
- } else if (this.props.scrollType === PostsView.SCROLL_TYPE_NEW_MESSAGE) {
- window.requestAnimationFrame(() => {
- // If separator exists scroll to it. Otherwise scroll to bottom.
- if (this.refs.newMessageSeparator) {
- var objDiv = this.refs.postlist;
- objDiv.scrollTop = this.refs.newMessageSeparator.offsetTop; //scrolls node to top of Div
- } else if (this.refs.postlist) {
- this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
- }
- });
- } else if (this.props.scrollType === PostsView.SCROLL_TYPE_POST && this.props.scrollPostId) {
- window.requestAnimationFrame(() => {
- const postNode = ReactDOM.findDOMNode(this.refs[this.props.scrollPostId]);
- if (postNode == null) {
- return;
- }
- postNode.scrollIntoView();
- if (this.refs.postlist.scrollTop === postNode.offsetTop) {
- this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3);
- } else {
- this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3) + (this.refs.postlist.scrollTop - postNode.offsetTop);
- }
- });
- } else if (this.props.scrollType === PostsView.SCROLL_TYPE_SIDEBAR_OPEN) {
- // If we are at the bottom then stay there
- if (this.wasAtBottom) {
- this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
- } else {
- window.requestAnimationFrame(() => {
- this.jumpToPostNode.scrollIntoView();
- if (this.refs.postlist.scrollTop === this.jumpToPostNode.offsetTop) {
- this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3);
- } else {
- this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3) + (this.refs.postlist.scrollTop - this.jumpToPostNode.offsetTop);
- }
- });
- }
- } else if (this.refs.postlist.scrollHeight !== this.prevScrollHeight) {
- window.requestAnimationFrame(() => {
- // Only need to jump if we added posts to the top.
- if (this.jumpToPostNode && (this.jumpToPostNode.offsetTop !== this.prevOffsetTop)) {
- this.refs.postlist.scrollTop += (this.refs.postlist.scrollHeight - this.prevScrollHeight);
- }
- });
- }
- }
- handleResize() {
- this.updateScrolling();
- }
- scrollToBottom() {
- window.requestAnimationFrame(() => {
- this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
- });
- }
- scrollToBottomAnimated() {
- var postList = $(this.refs.postlist);
- postList.animate({scrollTop: this.refs.postlist.scrollHeight}, '500');
- }
- componentDidMount() {
- if (this.props.postList != null) {
- this.updateScrolling();
- }
- window.addEventListener('resize', this.handleResize);
- }
- componentWillUnmount() {
- window.removeEventListener('resize', this.handleResize);
- }
- componentDidUpdate() {
- if (this.props.postList != null) {
- this.updateScrolling();
- }
- }
- componentWillReceiveProps(nextProps) {
- if (!this.props.isActive && nextProps.isActive) {
- this.updateState();
- PreferenceStore.addChangeListener(this.updateState);
- } else if (this.props.isActive && !nextProps.isActive) {
- PreferenceStore.removeChangeListener(this.updateState);
- }
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (this.props.isActive !== nextProps.isActive) {
- return true;
- }
- if (this.props.postList !== nextProps.postList) {
- return true;
- }
- if (this.props.scrollPostId !== nextProps.scrollPostId) {
- return true;
- }
- if (this.props.scrollType !== nextProps.scrollType && nextProps.scrollType !== PostsView.SCROLL_TYPE_FREE) {
- return true;
- }
- if (this.props.messageSeparatorTime !== nextProps.messageSeparatorTime) {
- return true;
- }
- if (!Utils.areObjectsEqual(this.props.postList, nextProps.postList)) {
- return true;
- }
- if (nextState.displayNameType !== this.state.displayNameType) {
- return true;
- }
- if (this.state.topPostId !== nextState.topPostId) {
- return true;
- }
- if (this.state.isScrolling !== nextState.isScrolling) {
- return true;
- }
- if (!Utils.areObjectsEqual(this.props.profiles, nextProps.profiles)) {
- return true;
- }
-
- return false;
- }
- render() {
- let posts = [];
- let order = [];
- let moreMessagesTop;
- let moreMessagesBottom;
- let postElements;
- let activeClass = 'inactive';
- if (this.props.postList != null) {
- posts = this.props.postList.posts;
- order = this.props.postList.order;
-
- // Create intro message or top loadmore link
- if (this.props.showMoreMessagesTop) {
- moreMessagesTop = (
- <a
- ref='loadmoretop'
- className='more-messages-text theme'
- href='#'
- onClick={this.loadMorePostsTop}
- >
- <FormattedMessage
- id='posts_view.loadMore'
- defaultMessage='Load more messages'
- />
- </a>
- );
- } else {
- moreMessagesTop = this.props.introText;
- }
-
- // Give option to load more posts at bottom if nessisary
- if (this.props.showMoreMessagesBottom) {
- moreMessagesBottom = (
- <a
- ref='loadmorebottom'
- className='more-messages-text theme'
- href='#'
- onClick={this.loadMorePostsBottom}
- >
- <FormattedMessage id='posts_view.loadMore'/>
- </a>
- );
- } else {
- moreMessagesBottom = null;
- }
-
- // Create post elements
- postElements = this.createPosts(posts, order);
-
- // Show ourselves if we are marked active
- if (this.props.isActive) {
- activeClass = '';
- }
- }
-
- let topPost = null;
- if (this.state.topPostId) {
- topPost = this.props.postList.posts[this.state.topPostId];
- }
-
- return (
- <div className={activeClass}>
- <FloatingTimestamp
- isScrolling={this.state.isScrolling}
- post={topPost}
- />
- <ScrollToBottomArrows
- isScrolling={this.state.isScrolling}
- atBottom={this.wasAtBottom}
- onClick={this.scrollToBottomAnimated}
- />
- <div
- ref='postlist'
- className='post-list-holder-by-time'
- onScroll={this.handleScroll}
- >
- <div className='post-list__table'>
- <div
- ref='postlistcontent'
- className='post-list__content'
- >
- {moreMessagesTop}
- {postElements}
- {moreMessagesBottom}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-PostsView.defaultProps = {
-};
-
-PostsView.propTypes = {
- isActive: React.PropTypes.bool,
- postList: React.PropTypes.object,
- profiles: React.PropTypes.object.isRequired,
- scrollPostId: React.PropTypes.string,
- scrollType: React.PropTypes.number,
- postViewScrolled: React.PropTypes.func.isRequired,
- loadMorePostsTopClicked: React.PropTypes.func.isRequired,
- loadMorePostsBottomClicked: React.PropTypes.func.isRequired,
- showMoreMessagesTop: React.PropTypes.bool,
- showMoreMessagesBottom: React.PropTypes.bool,
- introText: React.PropTypes.element,
- messageSeparatorTime: React.PropTypes.number,
- postsToHighlight: React.PropTypes.object,
- currentUser: React.PropTypes.object.isRequired
-};
-
-function FloatingTimestamp({isScrolling, post}) {
- // only show on mobile
- if ($(window).width() > 768) {
- return <noscript/>;
- }
-
- if (!post) {
- return <noscript/>;
- }
-
- const dateString = (
- <FormattedDate
- value={post.create_at}
- weekday='short'
- day='2-digit'
- month='short'
- year='numeric'
- />
- );
-
- let className = 'post-list__timestamp';
- if (isScrolling) {
- className += ' scrolling';
- }
-
- return (
- <div className={className}>
- <span>{dateString}</span>
- </div>
- );
-}
-
-FloatingTimestamp.propTypes = {
- isScrolling: React.PropTypes.bool.isRequired,
- post: React.PropTypes.object
-};
-
-function ScrollToBottomArrows({isScrolling, atBottom, onClick}) {
- // only show on mobile
- if ($(window).width() > 768) {
- return <noscript/>;
- }
-
- let className = 'post-list__arrows';
- if (isScrolling && !atBottom) {
- className += ' scrolling';
- }
-
- return (
- <div
- className={className}
- onClick={onClick}
- >
- <span dangerouslySetInnerHTML={{__html: Constants.SCROLL_BOTTOM_ICON}}/>
- </div>
- );
-}
-
-ScrollToBottomArrows.propTypes = {
- isScrolling: React.PropTypes.bool.isRequired,
- atBottom: React.PropTypes.bool.isRequired,
- onClick: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/posts_view_container.jsx b/web/react/components/posts_view_container.jsx
deleted file mode 100644
index b361779d2..000000000
--- a/web/react/components/posts_view_container.jsx
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostsView from './posts_view.jsx';
-import LoadingScreen from './loading_screen.jsx';
-
-import ChannelStore from '../stores/channel_store.jsx';
-import PostStore from '../stores/post_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-import * as Utils from '../utils/utils.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {createChannelIntroMessage} from '../utils/channel_intro_messages.jsx';
-
-export default class PostsViewContainer extends React.Component {
- constructor() {
- super();
-
- this.onChannelChange = this.onChannelChange.bind(this);
- this.onChannelLeave = this.onChannelLeave.bind(this);
- this.onPostsChange = this.onPostsChange.bind(this);
- this.onUserChange = this.onUserChange.bind(this);
- this.handlePostsViewScroll = this.handlePostsViewScroll.bind(this);
- this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
- this.handlePostsViewJumpRequest = this.handlePostsViewJumpRequest.bind(this);
-
- const currentChannelId = ChannelStore.getCurrentId();
- const state = {
- scrollType: PostsView.SCROLL_TYPE_BOTTOM,
- scrollPost: null,
- currentUser: UserStore.getCurrentUser()
- };
- if (currentChannelId) {
- Object.assign(state, {
- currentChannelIndex: 0,
- channels: [currentChannelId],
- postLists: [this.getChannelPosts(currentChannelId)],
- atTop: [PostStore.getVisibilityAtTop(currentChannelId)]
- });
- } else {
- Object.assign(state, {
- currentChannelIndex: null,
- channels: [],
- postLists: [],
- atTop: []
- });
- }
-
- state.showInviteModal = false;
- this.state = state;
- }
- componentDidMount() {
- ChannelStore.addChangeListener(this.onChannelChange);
- ChannelStore.addLeaveListener(this.onChannelLeave);
- PostStore.addChangeListener(this.onPostsChange);
- PostStore.addPostsViewJumpListener(this.handlePostsViewJumpRequest);
- UserStore.addChangeListener(this.onUserChange);
- }
- componentWillUnmount() {
- ChannelStore.removeChangeListener(this.onChannelChange);
- ChannelStore.removeLeaveListener(this.onChannelLeave);
- PostStore.removeChangeListener(this.onPostsChange);
- PostStore.removePostsViewJumpListener(this.handlePostsViewJumpRequest);
- UserStore.removeChangeListener(this.onUserChange);
- }
- onUserChange() {
- this.setState({currentUser: UserStore.getCurrentUser()});
- }
- handlePostsViewJumpRequest(type, post) {
- switch (type) {
- case Constants.PostsViewJumpTypes.BOTTOM:
- this.setState({scrollType: PostsView.SCROLL_TYPE_BOTTOM});
- break;
- case Constants.PostsViewJumpTypes.POST:
- this.setState({
- scrollType: PostsView.SCROLL_TYPE_POST,
- scrollPost: post
- });
- break;
- case Constants.PostsViewJumpTypes.SIDEBAR_OPEN:
- this.setState({scrollType: PostsView.SCROLL_TYPE_SIDEBAR_OPEN});
- break;
- }
- }
- onChannelChange() {
- const postLists = this.state.postLists.slice();
- const atTop = this.state.atTop.slice();
- const channels = this.state.channels.slice();
- const channelId = ChannelStore.getCurrentId();
-
- // Has the channel really changed?
- if (channelId === channels[this.state.currentChannelIndex]) {
- return;
- }
-
- let lastViewed = Number.MAX_VALUE;
- const member = ChannelStore.getMember(channelId);
- if (member != null) {
- lastViewed = member.last_viewed_at;
- }
-
- let newIndex = channels.indexOf(channelId);
- if (newIndex === -1) {
- newIndex = channels.length;
- channels.push(channelId);
- atTop[newIndex] = PostStore.getVisibilityAtTop(channelId);
- }
-
- // make sure we have the latest posts from the store
- postLists[newIndex] = this.getChannelPosts(channelId);
-
- this.setState({
- currentChannelIndex: newIndex,
- currentLastViewed: lastViewed,
- scrollType: PostsView.SCROLL_TYPE_NEW_MESSAGE,
- channels,
- postLists,
- atTop});
- }
- onChannelLeave(id) {
- const postLists = this.state.postLists.slice();
- const channels = this.state.channels.slice();
- const atTop = this.state.atTop.slice();
- const index = channels.indexOf(id);
- if (index !== -1) {
- postLists.splice(index, 1);
- channels.splice(index, 1);
- atTop.splice(index, 1);
- }
- this.setState({channels, postLists, atTop});
- }
- onPostsChange() {
- const channels = this.state.channels;
- const postLists = this.state.postLists.slice();
- const atTop = this.state.atTop.slice();
- const currentChannelId = channels[this.state.currentChannelIndex];
- const newPostsView = this.getChannelPosts(currentChannelId);
-
- postLists[this.state.currentChannelIndex] = newPostsView;
- atTop[this.state.currentChannelIndex] = PostStore.getVisibilityAtTop(currentChannelId);
- this.setState({postLists, atTop});
- }
- getChannelPosts(id) {
- return PostStore.getVisiblePosts(id);
- }
- loadMorePostsTop() {
- GlobalActions.emitLoadMorePostsEvent();
- }
- handlePostsViewScroll(atBottom) {
- if (atBottom) {
- this.setState({scrollType: PostsView.SCROLL_TYPE_BOTTOM});
- } else {
- this.setState({scrollType: PostsView.SCROLL_TYPE_FREE});
- }
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(this.state, nextState)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(this.props, nextProps)) {
- return true;
- }
-
- return false;
- }
- render() {
- const postLists = this.state.postLists;
- const channels = this.state.channels;
- const currentChannelId = channels[this.state.currentChannelIndex];
- const channel = ChannelStore.get(currentChannelId);
-
- if (!this.state.currentUser || !channel) {
- return null;
- }
-
- const postListCtls = [];
- for (let i = 0; i < channels.length; i++) {
- const isActive = (channels[i] === currentChannelId);
- postListCtls.push(
- <PostsView
- key={'postsviewkey' + i}
- isActive={isActive}
- postList={postLists[i]}
- scrollType={this.state.scrollType}
- scrollPostId={this.state.scrollPost}
- postViewScrolled={this.handlePostsViewScroll}
- loadMorePostsTopClicked={this.loadMorePostsTop}
- loadMorePostsBottomClicked={() => {
- // Do Nothing
- }}
- showMoreMessagesTop={!this.state.atTop[this.state.currentChannelIndex]}
- showMoreMessagesBottom={false}
- introText={channel ? createChannelIntroMessage(channel) : null}
- messageSeparatorTime={this.state.currentLastViewed}
- profiles={this.props.profiles}
- currentUser={this.state.currentUser}
- />
- );
- if (!postLists[i] && isActive) {
- postListCtls.push(
- <LoadingScreen
- position='absolute'
- key='loading'
- />
- );
- }
- }
-
- return (
- <div id='post-list'>
- {postListCtls}
- </div>
- );
- }
-}
-
-PostsViewContainer.propTypes = {
- profiles: React.PropTypes.object
-};
diff --git a/web/react/components/providers.json b/web/react/components/providers.json
deleted file mode 100644
index b5899c225..000000000
--- a/web/react/components/providers.json
+++ /dev/null
@@ -1,376 +0,0 @@
-[
- {
- "patterns": [
- "http://(?:www\\.)?xkcd\\.com/\\d+/?"
- ],
- "name": "XKCD",
- "height": 110
- },
- {
- "patterns": [
- "https?://soundcloud.com/.*/.*"
- ],
- "name": "SoundCloud",
- "height": 140
- },
- {
- "patterns": [
- "https?://(?:www\\.)?flickr\\.com/.*",
- "https?://flic\\.kr/p/[a-zA-Z0-9]+"
- ],
- "name": "Flickr",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.ted\\.com/talks/.+\\.html"
- ],
- "name": "TED",
- "height": 110
- },
- {
- "patterns": [
- "http://(?:www\\.)?theverge\\.com/\\d{4}/\\d{1,2}/\\d{1,2}/\\d+/[^/]+/?$"
- ],
- "name": "The Verge",
- "height": 110
- },
- {
- "patterns": [
- "http://.*\\.viddler\\.com/.*"
- ],
- "name": "Viddler",
- "height": 110
- },
- {
- "patterns": [
- "https?://(?:www\\.)?avclub\\.com/article/[^/]+/?$"
- ],
- "name": "The AV Club",
- "height": 110
- },
- {
- "patterns": [
- "https?://(?:www\\.)?wired\\.com/([^/]+/)?\\d+/\\d+/[^/]+/?$"
- ],
- "name": "Wired",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.theonion\\.com/articles/[^/]+/?"
- ],
- "name": "The Onion",
- "height": 110
- },
- {
- "patterns": [
- "http://yfrog\\.com/[0-9a-zA-Z]+/?$"
- ],
- "name": "YFrog",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.duffelblog\\.com/\\d{4}/\\d{1,2}/[^/]+/?$"
- ],
- "name": "The Duffel Blog",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.clickhole\\.com/article/[^/]+/?"
- ],
- "name": "Clickhole",
- "height": 110
- },
- {
- "patterns": [
- "https?://(?:www.)?skitch.com/([^/]+)/[^/]+/.+",
- "http://skit.ch/[^/]+"
- ],
- "name": "Skitch",
- "height": 110
- },
- {
- "patterns": [
- "https?://(alpha|posts|photos)\\.app\\.net/.*"
- ],
- "name": "ADN",
- "height": 110
- },
- {
- "patterns": [
- "https?://gist\\.github\\.com/(?:[-0-9a-zA-Z]+/)?([0-9a-fA-f]+)"
- ],
- "name": "Gist",
- "height": 110
- },
- {
- "patterns": [
- "https?://www\\.(dropbox\\.com/s/.+\\.(?:jpg|png|gif))",
- "https?://db\\.tt/[a-zA-Z0-9]+"
- ],
- "name": "Dropbox",
- "height": 110
- },
- {
- "patterns": [
- "https?://[^\\.]+\\.wikipedia\\.org/wiki/(?!Talk:)[^#]+(?:#(.+))?"
- ],
- "name": "Wikipedia",
- "height": 110
- },
- {
- "patterns": [
- "http://www.traileraddict.com/trailer/[^/]+/trailer"
- ],
- "name": "TrailerAddict",
- "height": 110
- },
- {
- "patterns": [
- "http://lockerz\\.com/[sd]/\\d+"
- ],
- "name": "Lockerz",
- "height": 110
- },
- {
- "patterns": [
- "http://gifuk\\.com/s/[0-9a-f]{16}"
- ],
- "name": "GIFUK",
- "height": 110
- },
- {
- "patterns": [
- "http://trailers\\.apple\\.com/trailers/[^/]+/[^/]+"
- ],
- "name": "iTunes Movie Trailers",
- "height": 110
- },
- {
- "patterns": [
- "http://gfycat\\.com/([a-zA-Z]+)"
- ],
- "name": "Gfycat",
- "height": 110
- },
- {
- "patterns": [
- "http://bash\\.org/\\?(\\d+)"
- ],
- "name": "Bash.org",
- "height": 110
- },
- {
- "patterns": [
- "http://arstechnica\\.com/[^/]+/\\d+/\\d+/[^/]+/?$"
- ],
- "name": "Ars Technica",
- "height": 110
- },
- {
- "patterns": [
- "http://imgur\\.com/gallery/[0-9a-zA-Z]+"
- ],
- "name": "Imgur",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.asciiartfarts\\.com/[0-9]+\\.html"
- ],
- "name": "ASCII Art Farts",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.monoprice\\.com/products/product\\.asp\\?.*p_id=\\d+"
- ],
- "name": "Monoprice",
- "height": 110
- },
- {
- "patterns": [
- "http://boingboing\\.net/\\d{4}/\\d{2}/\\d{2}/[^/]+\\.html"
- ],
- "name": "Boing Boing",
- "height": 110
- },
- {
- "patterns": [
- "https?://github\\.com/([^/]+)/([^/]+)/commit/(.+)",
- "http://git\\.io/[_0-9a-zA-Z]+"
- ],
- "name": "Github Commit",
- "height": 110
- },
- {
- "patterns": [
- "https?://open\\.spotify\\.com/(track|album)/([0-9a-zA-Z]{22})"
- ],
- "name": "Spotify",
- "height": 110
- },
- {
- "patterns": [
- "https?://path\\.com/p/([0-9a-zA-Z]+)$"
- ],
- "name": "Path",
- "height": 110
- },
- {
- "patterns": [
- "http://www.funnyordie.com/videos/[^/]+/.+"
- ],
- "name": "Funny or Die",
- "height": 110
- },
- {
- "patterns": [
- "http://(?:www\\.)?twitpic\\.com/([^/]+)"
- ],
- "name": "Twitpic",
- "height": 110
- },
- {
- "patterns": [
- "https?://www\\.giantbomb\\.com/videos/[^/]+/\\d+-\\d+/?"
- ],
- "name": "GiantBomb",
- "height": 110
- },
- {
- "patterns": [
- "http://(?:www\\.)?beeradvocate\\.com/beer/profile/\\d+/\\d+"
- ],
- "name": "Beer Advocate",
- "height": 110
- },
- {
- "patterns": [
- "http://(?:www\\.)?imdb.com/title/(tt\\d+)"
- ],
- "name": "IMDB",
- "height": 110
- },
- {
- "patterns": [
- "http://cl\\.ly/(?:image/)?[0-9a-zA-Z]+/?$"
- ],
- "name": "CloudApp",
- "height": 110
- },
- {
- "patterns": [
- "http://clyp\\.it/.*"
- ],
- "name": "Clyp",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.hulu\\.com/watch/.*"
- ],
- "name": "Hulu",
- "height": 110
- },
- {
- "patterns": [
- "https?://(?:www|mobile\\.)?twitter\\.com/(?:#!/)?[^/]+/status(?:es)?/(\\d+)/?$",
- "https?://t\\.co/[a-zA-Z0-9]+"
- ],
- "name": "Twitter",
- "height": 110
- },
- {
- "patterns": [
- "https?://(?:www\\.)?vimeo\\.com/.+"
- ],
- "name": "Vimeo",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.amazon\\.com/(?:.+/)?[gd]p/(?:product/)?(?:tags-on-product/)?([a-zA-Z0-9]+)",
- "http://amzn\\.com/([^/]+)"
- ],
- "name": "Amazon",
- "height": 110
- },
- {
- "patterns": [
- "http://qik\\.com/video/.*"
- ],
- "name": "Qik",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.rdio\\.com/artist/[^/]+/album/[^/]+/?",
- "http://www\\.rdio\\.com/artist/[^/]+/album/[^/]+/track/[^/]+/?",
- "http://www\\.rdio\\.com/people/[^/]+/playlists/\\d+/[^/]+"
- ],
- "name": "Rdio",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.slideshare\\.net/.*/.*"
- ],
- "name": "SlideShare",
- "height": 110
- },
- {
- "patterns": [
- "http://imgur\\.com/([0-9a-zA-Z]+)$"
- ],
- "name": "Imgur",
- "height": 110
- },
- {
- "patterns": [
- "https?://instagr(?:\\.am|am\\.com)/p/.+"
- ],
- "name": "Instagram",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.twitlonger\\.com/show/[a-zA-Z0-9]+",
- "http://tl\\.gd/[^/]+"
- ],
- "name": "Twitlonger",
- "height": 110
- },
- {
- "patterns": [
- "https?://vine.co/v/[a-zA-Z0-9]+"
- ],
- "name": "Vine",
- "height": 490
- },
- {
- "patterns": [
- "http://www\\.urbandictionary\\.com/define\\.php\\?term=.+"
- ],
- "name": "Urban Dictionary",
- "height": 110
- },
- {
- "patterns": [
- "http://picplz\\.com/user/[^/]+/pic/[^/]+"
- ],
- "name": "Picplz",
- "height": 110
- },
- {
- "patterns": [
- "https?://(?:www\\.)?twitter\\.com/(?:#!/)?[^/]+/status(?:es)?/(\\d+)/photo/\\d+(?:/large|/)?$",
- "https?://pic\\.twitter\\.com/.+"
- ],
- "name": "Twitter",
- "height": 110
- }
-]
diff --git a/web/react/components/register_app_modal.jsx b/web/react/components/register_app_modal.jsx
deleted file mode 100644
index e6d13863b..000000000
--- a/web/react/components/register_app_modal.jsx
+++ /dev/null
@@ -1,409 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-import ModalStore from '../stores/modal_store.jsx';
-
-const Modal = ReactBootstrap.Modal;
-
-import Constants from '../utils/constants.jsx';
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const ActionTypes = Constants.ActionTypes;
-
-const holders = defineMessages({
- required: {
- id: 'register_app.required',
- defaultMessage: 'Required'
- },
- optional: {
- id: 'register_app.optional',
- defaultMessage: 'Optional'
- }
-});
-
-class RegisterAppModal extends React.Component {
- constructor() {
- super();
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.onHide = this.onHide.bind(this);
- this.save = this.save.bind(this);
- this.updateShow = this.updateShow.bind(this);
-
- this.state = {
- clientId: '',
- clientSecret: '',
- saved: false,
- show: false
- };
- }
- componentDidMount() {
- ModalStore.addModalListener(ActionTypes.TOGGLE_REGISTER_APP_MODAL, this.updateShow);
- }
- componentWillUnmount() {
- ModalStore.removeModalListener(ActionTypes.TOGGLE_REGISTER_APP_MODAL, this.updateShow);
- }
- updateShow(show) {
- if (!show) {
- if (this.state.clientId !== '' && !this.state.saved) {
- return;
- }
-
- this.setState({
- clientId: '',
- clientSecret: '',
- saved: false,
- homepageError: null,
- callbackError: null,
- serverError: null,
- nameError: null
- });
- }
-
- this.setState({show});
- }
- handleSubmit(e) {
- e.preventDefault();
-
- var state = this.state;
- state.serverError = null;
-
- var app = {};
-
- var name = this.refs.name.value;
- if (!name || name.length === 0) {
- state.nameError = true;
- this.setState(state);
- return;
- }
- state.nameError = null;
- app.name = name;
-
- var homepage = this.refs.homepage.value;
- if (!homepage || homepage.length === 0) {
- state.homepageError = true;
- this.setState(state);
- return;
- }
- state.homepageError = null;
- app.homepage = homepage;
-
- var desc = this.refs.desc.value;
- app.description = desc;
-
- var rawCallbacks = this.refs.callback.value.trim();
- if (!rawCallbacks || rawCallbacks.length === 0) {
- state.callbackError = true;
- this.setState(state);
- return;
- }
- state.callbackError = null;
- app.callback_urls = rawCallbacks.split('\n');
-
- Client.registerOAuthApp(app,
- (data) => {
- state.clientId = data.id;
- state.clientSecret = data.client_secret;
- this.setState(state);
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
- onHide(e) {
- if (!this.state.saved && this.state.clientId !== '') {
- e.preventDefault();
- return;
- }
-
- this.setState({clientId: '', clientSecret: '', saved: false});
- }
- save() {
- this.setState({saved: this.refs.save.checked});
- }
- render() {
- const {formatMessage} = this.props.intl;
- var nameError;
- if (this.state.nameError) {
- nameError = (
- <div className='form-group has-error'>
- <label className='control-label'>
- <FormattedMessage
- id='register_app.nameError'
- defaultMessage='Application name must be filled in.'
- />
- </label>
- </div>
- );
- }
- var homepageError;
- if (this.state.homepageError) {
- homepageError = (
- <div className='form-group has-error'>
- <label className='control-label'>
- <FormattedMessage
- id='register_app.homepageError'
- defaultMessage='Homepage must be filled in.'
- />
- </label>
- </div>
- );
- }
- var callbackError;
- if (this.state.callbackError) {
- callbackError = (
- <div className='form-group has-error'>
- <label className='control-label'>
- <FormattedMessage
- id='register_app.callbackError'
- defaultMessage='At least one callback URL must be filled in.'
- />
- </label>
- </div>
- );
- }
- var serverError;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var body = '';
- var footer = '';
- if (this.state.clientId === '') {
- body = (
- <div className='settings-modal'>
- <div className='form-horizontal user-settings'>
- <h4 className='padding-bottom x3'>
- <FormattedMessage
- id='register_app.title'
- defaultMessage='Register a New Application'
- />
- </h4>
- <div className='row'>
- <label className='col-sm-4 control-label'>
- <FormattedMessage
- id='register_app.name'
- defaultMessage='Application Name'
- />
- </label>
- <div className='col-sm-7'>
- <input
- ref='name'
- className='form-control'
- type='text'
- placeholder={formatMessage(holders.required)}
- />
- {nameError}
- </div>
- </div>
- <div className='row padding-top x2'>
- <label className='col-sm-4 control-label'>
- <FormattedMessage
- id='register_app.homepage'
- defaultMessage='Homepage URL'
- />
- </label>
- <div className='col-sm-7'>
- <input
- ref='homepage'
- className='form-control'
- type='text'
- placeholder={formatMessage(holders.required)}
- />
- {homepageError}
- </div>
- </div>
- <div className='row padding-top x2'>
- <label className='col-sm-4 control-label'>
- <FormattedMessage
- id='register_app.description'
- defaultMessage='Description'
- />
- </label>
- <div className='col-sm-7'>
- <input
- ref='desc'
- className='form-control'
- type='text'
- placeholder={formatMessage(holders.optional)}
- />
- </div>
- </div>
- <div className='row padding-top padding-bottom x2'>
- <label className='col-sm-4 control-label'>
- <FormattedMessage
- id='register_app.callback'
- defaultMessage='Callback URL'
- />
- </label>
- <div className='col-sm-7'>
- <textarea
- ref='callback'
- className='form-control'
- type='text'
- placeholder={formatMessage(holders.required)}
- rows='5'
- />
- {callbackError}
- </div>
- </div>
- {serverError}
- </div>
- </div>
- );
-
- footer = (
- <div>
- <button
- type='button'
- className='btn btn-default'
- onClick={() => this.updateShow(false)}
- >
- <FormattedMessage
- id='register_app.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- onClick={this.handleSubmit}
- type='submit'
- className='btn btn-primary'
- tabIndex='3'
- >
- <FormattedMessage
- id='register_app.register'
- defaultMessage='Register'
- />
- </button>
- </div>
- );
- } else {
- var btnClass = ' disabled';
- if (this.state.saved) {
- btnClass = '';
- }
-
- body = (
- <div className='form-horizontal user-settings'>
- <h4 className='padding-bottom x3'>
- <FormattedMessage
- id='register_app.credentialsTitle'
- defaultMessage='Your Application Credentials'
- />
- </h4>
- <br/>
- <div className='row'>
- <label className='col-sm-4 control-label'>
- <FormattedMessage
- id='register_app.clientId'
- defaultMessage='Client ID'
- />
- </label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='text'
- value={this.state.clientId}
- readOnly='true'
- />
- </div>
- </div>
- <br/>
- <div className='row padding-top x2'>
- <label className='col-sm-4 control-label'>
- <FormattedMessage
- id='register_app.clientSecret'
- defaultMessage='Client Secret'
- /></label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='text'
- value={this.state.clientSecret}
- readOnly='true'
- />
- </div>
- </div>
- <br/>
- <br/>
- <strong>
- <FormattedMessage
- id='register_app.credentialsDescription'
- defaultMessage="Save these somewhere SAFE and SECURE. Treat your Client ID as your app's username and your Client Secret as the app's password."
- />
- </strong>
- <br/>
- <br/>
- <div className='checkbox'>
- <label>
- <input
- ref='save'
- type='checkbox'
- checked={this.state.saved}
- onChange={this.save}
- />
- <FormattedMessage
- id='register_app.credentialsSave'
- defaultMessage='I have saved both my Client Id and Client Secret somewhere safe'
- />
- </label>
- </div>
- </div>
- );
-
- footer = (
- <a
- className={'btn btn-sm btn-primary pull-right' + btnClass}
- href='#'
- onClick={(e) => {
- e.preventDefault();
- this.updateShow(false);
- }}
- >
- <FormattedMessage
- id='register_app.close'
- defaultMessage='Close'
- />
- </a>
- );
- }
-
- return (
- <span>
- <Modal
- show={this.state.show}
- onHide={() => this.updateShow(false)}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='register_app.dev'
- defaultMessage='Developer Applications'
- />
- </Modal.Title>
- </Modal.Header>
- <form
- role='form'
- className='form-horizontal'
- >
- <Modal.Body>
- {body}
- </Modal.Body>
- <Modal.Footer>
- {footer}
- </Modal.Footer>
- </form>
- </Modal>
- </span>
- );
- }
-}
-
-RegisterAppModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(RegisterAppModal);
diff --git a/web/react/components/removed_from_channel_modal.jsx b/web/react/components/removed_from_channel_modal.jsx
deleted file mode 100644
index 748baa32b..000000000
--- a/web/react/components/removed_from_channel_modal.jsx
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import BrowserStore from '../stores/browser_store.jsx';
-import * as utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class RemovedFromChannelModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleShow = this.handleShow.bind(this);
- this.handleClose = this.handleClose.bind(this);
-
- this.state = {
- channelName: '',
- remover: ''
- };
- }
-
- handleShow() {
- var newState = {};
- if (BrowserStore.getItem('channel-removed-state')) {
- newState = BrowserStore.getItem('channel-removed-state');
- BrowserStore.removeItem('channel-removed-state');
- }
-
- var townSquare = ChannelStore.getByName('town-square');
- setTimeout(() => utils.switchChannel(townSquare), 1);
-
- this.setState(newState);
- }
-
- handleClose() {
- this.setState({channelName: '', remover: ''});
- }
-
- componentDidMount() {
- $(ReactDOM.findDOMNode(this)).on('show.bs.modal', this.handleShow);
- $(ReactDOM.findDOMNode(this)).on('hidden.bs.modal', this.handleClose);
- }
-
- componentWillUnmount() {
- $(ReactDOM.findDOMNode(this)).off('show.bs.modal', this.handleShow);
- $(ReactDOM.findDOMNode(this)).off('hidden.bs.modal', this.handleClose);
- }
-
- render() {
- var currentUser = UserStore.getCurrentUser();
-
- var channelName = (
- <FormattedMessage
- id='removed_channel.channelName'
- defaultMessage='the channel'
- />
- );
- if (this.state.channelName) {
- channelName = this.state.channelName;
- }
-
- var remover = (
- <FormattedMessage
- id='removed_channel.someone'
- defaultMessage='Someone'
- />
- );
- if (this.state.remover) {
- remover = this.state.remover;
- }
-
- if (currentUser != null) {
- return (
- <div
- className='modal fade'
- ref='modal'
- id='removed_from_channel'
- tabIndex='-1'
- role='dialog'
- aria-hidden='true'
- >
- <div className='modal-dialog'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- ><span aria-hidden='true'>&times;</span></button>
- <h4 className='modal-title'>
- <FormattedMessage
- id='removed_channel.from'
- defaultMessage='Removed from '
- />
- <span className='name'>{channelName}</span></h4>
- </div>
- <div className='modal-body'>
- <p>
- <FormattedMessage
- id='removed_channel.remover'
- defaultMessage='{remover} removed you from {channel}'
- values={{
- remover: (remover),
- channel: (channelName)
- }}
- />
- </p>
- </div>
- <div className='modal-footer'>
- <button
- type='button'
- className='btn btn-primary'
- data-dismiss='modal'
- >
- <FormattedMessage
- id='removed_channel.okay'
- defaultMessage='Okay'
- />
- </button>
- </div>
- </div>
- </div>
- </div>
- );
- }
-
- return <div/>;
- }
-}
diff --git a/web/react/components/rename_channel_modal.jsx b/web/react/components/rename_channel_modal.jsx
deleted file mode 100644
index e96ff0db2..000000000
--- a/web/react/components/rename_channel_modal.jsx
+++ /dev/null
@@ -1,321 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-const holders = defineMessages({
- required: {
- id: 'rename_channel.required',
- defaultMessage: 'This field is required'
- },
- maxLength: {
- id: 'rename_channel.maxLength',
- defaultMessage: 'This field must be less than 22 characters'
- },
- lowercase: {
- id: 'rename_channel.lowercase',
- defaultMessage: 'Must be lowercase alphanumeric characters'
- },
- handle: {
- id: 'rename_channel.handle',
- defaultMessage: 'Handle'
- },
- defaultError: {
- id: 'rename_channel.defaultError',
- defaultMessage: ' - Cannot be changed for the default channel'
- },
- displayNameHolder: {
- id: 'rename_channel.displayNameHolder',
- defaultMessage: 'Enter display name'
- },
- handleHolder: {
- id: 'rename_channel.handleHolder',
- defaultMessage: 'lowercase alphanumeric&#39;s only'
- }
-});
-
-export default class RenameChannelModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleShow = this.handleShow.bind(this);
- this.handleHide = this.handleHide.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleCancel = this.handleCancel.bind(this);
-
- this.onNameChange = this.onNameChange.bind(this);
- this.onDisplayNameChange = this.onDisplayNameChange.bind(this);
- this.displayNameKeyUp = this.displayNameKeyUp.bind(this);
-
- this.state = {
- displayName: props.channel.display_name,
- channelName: props.channel.name,
- serverError: '',
- nameError: '',
- displayNameError: '',
- invalid: false
- };
- }
-
- componentWillReceiveProps(nextProps) {
- if (!Utils.areObjectsEqual(nextProps.channel, this.props.channel)) {
- this.setState({
- displayName: nextProps.channel.display_name,
- channelName: nextProps.channel.name
- });
- }
- }
-
- shouldComponentUpdate(nextProps, nextState) {
- if (!nextProps.show && !this.props.show) {
- return false;
- }
-
- if (!Utils.areObjectsEqual(nextState, this.state)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(nextProps, this.props)) {
- return true;
- }
-
- return false;
- }
-
- componentDidUpdate(prevProps) {
- if (!prevProps.show && this.props.show) {
- this.handleShow();
- }
- }
-
- handleShow() {
- const textbox = ReactDOM.findDOMNode(this.refs.displayName);
- textbox.focus();
- Utils.placeCaretAtEnd(textbox);
- }
-
- handleHide(e) {
- if (e) {
- e.preventDefault();
- }
-
- this.props.onHide();
-
- this.setState({
- serverError: '',
- nameError: '',
- displayNameError: '',
- invalid: false
- });
- }
-
- handleSubmit(e) {
- e.preventDefault();
-
- const channel = Object.assign({}, this.props.channel);
- const oldName = channel.name;
- const oldDisplayName = channel.displayName;
- const state = {serverError: ''};
- const {formatMessage} = this.props.intl;
-
- channel.display_name = this.state.displayName.trim();
- if (!channel.display_name) {
- state.displayNameError = formatMessage(holders.required);
- state.invalid = true;
- } else if (channel.display_name.length > 22) {
- state.displayNameError = formatMessage(holders.maxLength);
- state.invalid = true;
- } else {
- state.displayNameError = '';
- }
-
- channel.name = this.state.channelName.trim();
- if (!channel.name) {
- state.nameError = formatMessage(holders.required);
- state.invalid = true;
- } else if (channel.name.length > 22) {
- state.nameError = formatMessage(holders.maxLength);
- state.invalid = true;
- } else {
- const cleanedName = Utils.cleanUpUrlable(channel.name);
- if (cleanedName === channel.name) {
- state.nameError = '';
- } else {
- state.nameError = formatMessage(holders.lowercase);
- state.invalid = true;
- }
- }
-
- this.setState(state);
-
- if (state.invalid || (oldName === channel.name && oldDisplayName === channel.display_name)) {
- return;
- }
-
- Client.updateChannel(
- channel,
- () => {
- AsyncClient.getChannel(channel.id);
- Utils.updateAddressBar(channel.name);
-
- this.handleHide();
- },
- (err) => {
- this.setState({
- serverError: err.message,
- invalid: true
- });
- }
- );
- }
-
- handleCancel(e) {
- this.setState({
- displayName: this.props.channel.display_name,
- channelName: this.props.channel.name
- });
-
- this.handleHide(e);
- }
-
- onNameChange() {
- this.setState({channelName: ReactDOM.findDOMNode(this.refs.channelName).value});
- }
-
- onDisplayNameChange() {
- this.setState({displayName: ReactDOM.findDOMNode(this.refs.displayName).value});
- }
-
- displayNameKeyUp() {
- if (this.state.channelName !== Constants.DEFAULT_CHANNEL) {
- const displayName = ReactDOM.findDOMNode(this.refs.displayName).value.trim();
- const channelName = Utils.cleanUpUrlable(displayName);
- ReactDOM.findDOMNode(this.refs.channelName).value = channelName;
- this.setState({channelName: channelName});
- }
- }
-
- render() {
- let displayNameError = null;
- let displayNameClass = 'form-group';
- if (this.state.displayNameError) {
- displayNameError = <label className='control-label'>{this.state.displayNameError}</label>;
- displayNameClass += ' has-error';
- }
-
- let nameError = null;
- let nameClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameClass += ' has-error';
- }
-
- let serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- const {formatMessage} = this.props.intl;
-
- let handleInputLabel = formatMessage(holders.handle);
- let handleInputClass = 'form-control';
- let readOnlyHandleInput = false;
- if (this.state.channelName === Constants.DEFAULT_CHANNEL) {
- handleInputLabel += formatMessage(holders.defaultError);
- readOnlyHandleInput = true;
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.handleCancel}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='rename_channel.title'
- defaultMessage='Rename Channel'
- />
- </Modal.Title>
- </Modal.Header>
- <form role='form'>
- <Modal.Body>
- <div className={displayNameClass}>
- <label className='control-label'>
- <FormattedMessage
- id='rename_channel.displayName'
- defaultMessage='Display Name'
- />
- </label>
- <input
- onKeyUp={this.displayNameKeyUp}
- onChange={this.onDisplayNameChange}
- type='text'
- ref='displayName'
- id='display_name'
- className='form-control'
- placeholder={formatMessage(holders.displayNameHolder)}
- value={this.state.displayName}
- maxLength='64'
- />
- {displayNameError}
- </div>
- <div className={nameClass}>
- <label className='control-label'>{handleInputLabel}</label>
- <input
- onChange={this.onNameChange}
- type='text'
- className={handleInputClass}
- ref='channelName'
- placeholder={formatMessage(holders.handleHolder)}
- value={this.state.channelName}
- maxLength='64'
- readOnly={readOnlyHandleInput}
- />
- {nameError}
- </div>
- {serverError}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.handleCancel}
- >
- <FormattedMessage
- id='rename_channel.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- onClick={this.handleSubmit}
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='rename_channel.save'
- defaultMessage='Save'
- />
- </button>
- </Modal.Footer>
- </form>
- </Modal>
- );
- }
-}
-
-RenameChannelModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired
-};
-
-export default injectIntl(RenameChannelModal);
diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx
deleted file mode 100644
index 9183b761f..000000000
--- a/web/react/components/rhs_comment.jsx
+++ /dev/null
@@ -1,306 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostStore from '../stores/post_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import UserProfile from './user_profile.jsx';
-import UserStore from '../stores/user_store.jsx';
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as Utils from '../utils/utils.jsx';
-import Constants from '../utils/constants.jsx';
-import FileAttachmentList from './file_attachment_list.jsx';
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-var ActionTypes = Constants.ActionTypes;
-import * as TextFormatting from '../utils/text_formatting.jsx';
-import twemoji from 'twemoji';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedDate} from 'mm-intl';
-
-const holders = defineMessages({
- comment: {
- id: 'rhs_comment.comment',
- defaultMessage: 'Comment'
- }
-});
-
-class RhsComment extends React.Component {
- constructor(props) {
- super(props);
-
- this.retryComment = this.retryComment.bind(this);
- this.parseEmojis = this.parseEmojis.bind(this);
- this.handlePermalink = this.handlePermalink.bind(this);
-
- this.state = {};
- }
- retryComment(e) {
- e.preventDefault();
-
- var post = this.props.post;
- Client.createPost(post, post.channel_id,
- (data) => {
- AsyncClient.getPosts(post.channel_id);
-
- var channel = ChannelStore.get(post.channel_id);
- var member = ChannelStore.getMember(post.channel_id);
- member.msg_count = channel.total_msg_count;
- member.last_viewed_at = (new Date()).getTime();
- ChannelStore.setChannelMember(member);
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST,
- post: data
- });
- },
- () => {
- post.state = Constants.POST_FAILED;
- PostStore.updatePendingPost(post);
- this.forceUpdate();
- }
- );
-
- post.state = Constants.POST_LOADING;
- PostStore.updatePendingPost(post);
- this.forceUpdate();
- }
- parseEmojis() {
- twemoji.parse(ReactDOM.findDOMNode(this), {size: Constants.EMOJI_SIZE});
- }
- handlePermalink(e) {
- e.preventDefault();
- GlobalActions.showGetPostLinkModal(this.props.post);
- }
- componentDidMount() {
- this.parseEmojis();
- }
- shouldComponentUpdate(nextProps) {
- if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
- return true;
- }
-
- return false;
- }
- componentDidUpdate() {
- this.parseEmojis();
- }
- createDropdown() {
- var post = this.props.post;
-
- if (post.state === Constants.POST_FAILED || post.state === Constants.POST_LOADING || post.state === Constants.POST_DELETED) {
- return '';
- }
-
- var isOwner = UserStore.getCurrentId() === post.user_id;
- var isAdmin = Utils.isAdmin(UserStore.getCurrentUser().roles);
-
- var dropdownContents = [];
-
- if (!Utils.isMobile()) {
- dropdownContents.push(
- <li
- key='rhs-root-permalink'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.handlePermalink}
- >
- <FormattedMessage
- id='rhs_comment.permalink'
- defaultMessage='Permalink'
- />
- </a>
- </li>
- );
- }
-
- if (isOwner) {
- dropdownContents.push(
- <li
- role='presentation'
- key='edit-button'
- >
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#edit_post'
- data-refocusid='#reply_textbox'
- data-title={this.props.intl.formatMessage(holders.comment)}
- data-message={post.message}
- data-postid={post.id}
- data-channelid={post.channel_id}
- >
- <FormattedMessage
- id='rhs_comment.edit'
- defaultMessage='Edit'
- />
- </a>
- </li>
- );
- }
-
- if (isOwner || isAdmin) {
- dropdownContents.push(
- <li
- role='presentation'
- key='delete-button'
- >
- <a
- href='#'
- role='menuitem'
- onClick={() => GlobalActions.showDeletePostModal(post, 0)}
- >
- <FormattedMessage
- id='rhs_comment.del'
- defaultMessage='Delete'
- />
- </a>
- </li>
- );
- }
-
- if (dropdownContents.length === 0) {
- return '';
- }
-
- return (
- <div className='dropdown'>
- <a
- href='#'
- className='post__dropdown dropdown-toggle'
- type='button'
- data-toggle='dropdown'
- aria-expanded='false'
- />
- <ul
- className='dropdown-menu'
- role='menu'
- >
- {dropdownContents}
- </ul>
- </div>
- );
- }
- render() {
- var post = this.props.post;
-
- var currentUserCss = '';
- if (UserStore.getCurrentId() === post.user_id) {
- currentUserCss = 'current--user';
- }
-
- var timestamp = UserStore.getCurrentUser().update_at;
-
- let loading;
- let postClass = '';
- let message = (
- <div
- ref='message_holder'
- onClick={TextFormatting.handleClick}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message)}}
- />
- );
-
- if (post.state === Constants.POST_FAILED) {
- postClass += ' post-fail';
- loading = (
- <a
- className='theme post-retry pull-right'
- href='#'
- onClick={this.retryComment}
- >
- <FormattedMessage
- id='rhs_comment.retry'
- defaultMessage='Retry'
- />
- </a>
- );
- } else if (post.state === Constants.POST_LOADING) {
- postClass += ' post-waiting';
- loading = (
- <img
- className='post-loading-gif pull-right'
- src='/static/images/load.gif'
- />
- );
- } else if (this.props.post.state === Constants.POST_DELETED) {
- message = (
- <FormattedMessage
- id='post_body.deleted'
- defaultMessage='(message deleted)'
- />
- );
- }
-
- var dropdown = this.createDropdown();
-
- var fileAttachment;
- if (post.filenames && post.filenames.length > 0) {
- fileAttachment = (
- <FileAttachmentList
- filenames={post.filenames}
- channelId={post.channel_id}
- userId={post.user_id}
- />
- );
- }
-
- return (
- <div className={'post ' + currentUserCss}>
- <div className='post__content'>
- <div className='post__img'>
- <img
- src={'/api/v1/users/' + post.user_id + '/image?time=' + timestamp}
- height='36'
- width='36'
- />
- </div>
- <div>
- <ul className='post__header'>
- <li className='col__name'>
- <strong><UserProfile user={this.props.user}/></strong>
- </li>
- <li className='col'>
- <time className='post__time'>
- <FormattedDate
- value={post.create_at}
- day='numeric'
- month='long'
- year='numeric'
- hour12={true}
- hour='2-digit'
- minute='2-digit'
- />
- </time>
- </li>
- <li className='col col__reply'>
- {dropdown}
- </li>
- </ul>
- <div className='post__body'>
- <div className={postClass}>
- {loading}
- {message}
- </div>
- {fileAttachment}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-RhsComment.defaultProps = {
- post: null
-};
-RhsComment.propTypes = {
- intl: intlShape.isRequired,
- post: React.PropTypes.object,
- user: React.PropTypes.object
-};
-
-export default injectIntl(RhsComment);
diff --git a/web/react/components/rhs_header_post.jsx b/web/react/components/rhs_header_post.jsx
deleted file mode 100644
index 4c9f6f3f6..000000000
--- a/web/react/components/rhs_header_post.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const ActionTypes = Constants.ActionTypes;
-
-export default class RhsHeaderPost extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleClose = this.handleClose.bind(this);
- this.handleBack = this.handleBack.bind(this);
-
- this.state = {};
- }
- handleClose(e) {
- e.preventDefault();
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
- }
- handleBack(e) {
- e.preventDefault();
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH_TERM,
- term: this.props.fromSearch,
- do_search: true,
- is_mention_search: this.props.isMentionSearch
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
- }
- render() {
- let back;
- if (this.props.fromSearch) {
- back = (
- <a
- href='#'
- onClick={this.handleBack}
- className='sidebar--right__back'
- >
- <i className='fa fa-chevron-left'></i>
- </a>
- );
- }
-
- return (
- <div className='sidebar--right__header'>
- <span className='sidebar--right__title'>
- {back}
- <FormattedMessage
- id='rhs_header.details'
- defaultMessage='Message Details'
- />
- </span>
- <button
- type='button'
- className='sidebar--right__close'
- aria-label='Close'
- onClick={this.handleClose}
- >
- <i className='fa fa-sign-out'/>
- </button>
- </div>
- );
- }
-}
-
-RhsHeaderPost.defaultProps = {
- isMentionSearch: false,
- fromSearch: ''
-};
-RhsHeaderPost.propTypes = {
- isMentionSearch: React.PropTypes.bool,
- fromSearch: React.PropTypes.string
-};
diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx
deleted file mode 100644
index fc1cd0b41..000000000
--- a/web/react/components/rhs_root_post.jsx
+++ /dev/null
@@ -1,287 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChannelStore from '../stores/channel_store.jsx';
-import UserProfile from './user_profile.jsx';
-import UserStore from '../stores/user_store.jsx';
-import * as TextFormatting from '../utils/text_formatting.jsx';
-import * as Utils from '../utils/utils.jsx';
-import * as Emoji from '../utils/emoticons.jsx';
-import FileAttachmentList from './file_attachment_list.jsx';
-import twemoji from 'twemoji';
-import PostBodyAdditionalContent from './post_body_additional_content.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage, FormattedDate} from 'mm-intl';
-
-export default class RhsRootPost extends React.Component {
- constructor(props) {
- super(props);
-
- this.parseEmojis = this.parseEmojis.bind(this);
- this.handlePermalink = this.handlePermalink.bind(this);
-
- this.state = {};
- }
- parseEmojis() {
- twemoji.parse(ReactDOM.findDOMNode(this), {
- className: 'emoji twemoji',
- base: '',
- folder: Emoji.getImagePathForEmoticon()
- });
- }
- handlePermalink(e) {
- e.preventDefault();
- GlobalActions.showGetPostLinkModal(this.props.post);
- }
- componentDidMount() {
- this.parseEmojis();
- }
- shouldComponentUpdate(nextProps) {
- if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
- return true;
- }
-
- return false;
- }
- componentDidUpdate() {
- this.parseEmojis();
- }
- render() {
- const post = this.props.post;
- const user = this.props.user;
- var isOwner = user.id === post.user_id;
- var isAdmin = Utils.isAdmin(user.roles);
- var timestamp = UserStore.getProfile(post.user_id).update_at;
- var channel = ChannelStore.get(post.channel_id);
-
- var type = 'Post';
- if (post.root_id.length > 0) {
- type = 'Comment';
- }
-
- var userCss = '';
- if (UserStore.getCurrentId() === post.user_id) {
- userCss = 'current--user';
- }
-
- var systemMessageClass = '';
- if (Utils.isSystemMessage(post)) {
- systemMessageClass = 'post--system';
- }
-
- var channelName;
- if (channel) {
- if (channel.type === 'D') {
- channelName = (
- <FormattedMessage
- id='rhs_root.direct'
- defaultMessage='Direct Message'
- />
- );
- } else {
- channelName = channel.display_name;
- }
- }
-
- var dropdownContents = [];
-
- if (!Utils.isMobile()) {
- dropdownContents.push(
- <li
- key='rhs-root-permalink'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.handlePermalink}
- >
- <FormattedMessage
- id='rhs_root.permalink'
- defaultMessage='Permalink'
- />
- </a>
- </li>
- );
- }
-
- if (isOwner) {
- dropdownContents.push(
- <li
- key='rhs-root-edit'
- role='presentation'
- >
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#edit_post'
- data-refocusid='#reply_textbox'
- data-title={type}
- data-message={post.message}
- data-postid={post.id}
- data-channelid={post.channel_id}
- >
- <FormattedMessage
- id='rhs_root.edit'
- defaultMessage='Edit'
- />
- </a>
- </li>
- );
- }
-
- if (isOwner || isAdmin) {
- dropdownContents.push(
- <li
- key='rhs-root-delete'
- role='presentation'
- >
- <a
- href='#'
- role='menuitem'
- onClick={() => GlobalActions.showDeletePostModal(post, this.props.commentCount)}
- >
- <FormattedMessage
- id='rhs_root.del'
- defaultMessage='Delete'
- />
- </a>
- </li>
- );
- }
-
- var rootOptions = '';
- if (dropdownContents.length > 0) {
- rootOptions = (
- <div className='dropdown'>
- <a
- href='#'
- className='post__dropdown dropdown-toggle'
- type='button'
- data-toggle='dropdown'
- aria-expanded='false'
- />
- <ul
- className='dropdown-menu'
- role='menu'
- >
- {dropdownContents}
- </ul>
- </div>
- );
- }
-
- var fileAttachment;
- if (post.filenames && post.filenames.length > 0) {
- fileAttachment = (
- <FileAttachmentList
- filenames={post.filenames}
- channelId={post.channel_id}
- userId={post.user_id}
- />
- );
- }
-
- let userProfile = <UserProfile user={user}/>;
- let botIndicator;
-
- if (post.props && post.props.from_webhook) {
- if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') {
- userProfile = (
- <UserProfile
- user={user}
- overwriteName={post.props.override_username}
- disablePopover={true}
- />
- );
- }
-
- botIndicator = <li className='col col__name bot-indicator'>{'BOT'}</li>;
- } else if (Utils.isSystemMessage(post)) {
- userProfile = (
- <UserProfile
- user={{}}
- overwriteName={Constants.SYSTEM_MESSAGE_PROFILE_NAME}
- overwriteImage={Constants.SYSTEM_MESSAGE_PROFILE_IMAGE}
- disablePopover={true}
- />
- );
- }
-
- let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp;
- if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
- if (post.props.override_icon_url) {
- src = post.props.override_icon_url;
- }
- } else if (Utils.isSystemMessage(post)) {
- src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
- }
-
- const profilePic = (
- <img
- className='post-profile-img'
- src={src}
- height='36'
- width='36'
- />
- );
-
- return (
- <div className={'post post--root ' + userCss + ' ' + systemMessageClass}>
- <div className='post-right-channel__name'>{channelName}</div>
- <div className='post__content'>
- <div className='post__img'>
- {profilePic}
- </div>
- <div>
- <ul className='post__header'>
- <li className='col__name'>{userProfile}</li>
- {botIndicator}
- <li className='col'>
- <time className='post__time'>
- <FormattedDate
- value={post.create_at}
- day='numeric'
- month='long'
- year='numeric'
- hour12={true}
- hour='2-digit'
- minute='2-digit'
- />
- </time>
- </li>
- <li className='col col__reply'>
- <div>
- {rootOptions}
- </div>
- </li>
- </ul>
- <div className='post__body'>
- <div
- ref='message_holder'
- onClick={TextFormatting.handleClick}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message)}}
- />
- <PostBodyAdditionalContent
- post={post}
- />
- {fileAttachment}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-RhsRootPost.defaultProps = {
- commentCount: 0
-};
-RhsRootPost.propTypes = {
- post: React.PropTypes.object.isRequired,
- user: React.PropTypes.object.isRequired,
- commentCount: React.PropTypes.number
-};
diff --git a/web/react/components/rhs_thread.jsx b/web/react/components/rhs_thread.jsx
deleted file mode 100644
index 292624846..000000000
--- a/web/react/components/rhs_thread.jsx
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostStore from '../stores/post_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-import SearchBox from './search_bar.jsx';
-import CreateComment from './create_comment.jsx';
-import RhsHeaderPost from './rhs_header_post.jsx';
-import RootPost from './rhs_root_post.jsx';
-import Comment from './rhs_comment.jsx';
-import Constants from '../utils/constants.jsx';
-import FileUploadOverlay from './file_upload_overlay.jsx';
-
-export default class RhsThread extends React.Component {
- constructor(props) {
- super(props);
-
- this.mounted = false;
-
- this.onPostChange = this.onPostChange.bind(this);
- this.onUserChange = this.onUserChange.bind(this);
- this.forceUpdateInfo = this.forceUpdateInfo.bind(this);
- this.handleResize = this.handleResize.bind(this);
-
- const state = {};
- state.windowWidth = Utils.windowWidth();
- state.windowHeight = Utils.windowHeight();
- state.selected = PostStore.getSelectedPost();
- state.posts = PostStore.getSelectedPostThread();
- state.profiles = JSON.parse(JSON.stringify(UserStore.getProfiles()));
-
- this.state = state;
- }
- componentDidMount() {
- PostStore.addSelectedPostChangeListener(this.onPostChange);
- PostStore.addChangeListener(this.onPostChange);
- PreferenceStore.addChangeListener(this.forceUpdateInfo);
- UserStore.addChangeListener(this.onUserChange);
-
- this.resize();
- window.addEventListener('resize', this.handleResize);
-
- this.mounted = true;
- }
- componentDidUpdate() {
- if ($('.post-right__scroll')[0]) {
- $('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight);
- }
- this.resize();
- }
- componentWillUnmount() {
- PostStore.removeSelectedPostChangeListener(this.onPostChange);
- PostStore.removeChangeListener(this.onPostChange);
- PreferenceStore.removeChangeListener(this.forceUpdateInfo);
- UserStore.removeChangeListener(this.onUserChange);
-
- window.removeEventListener('resize', this.handleResize);
-
- this.mounted = false;
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(nextState.posts, this.state.posts)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(nextState.selected, this.state.selected)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(nextState.profiles, this.state.profiles)) {
- return true;
- }
-
- return false;
- }
- forceUpdateInfo() {
- if (this.state.postList) {
- for (var postId in this.state.postList.posts) {
- if (this.refs[postId]) {
- this.refs[postId].forceUpdate();
- }
- }
- }
- }
- handleResize() {
- this.setState({
- windowWidth: Utils.windowWidth(),
- windowHeight: Utils.windowHeight()
- });
- }
- onPostChange() {
- if (this.mounted) {
- const selected = PostStore.getSelectedPost();
- const posts = PostStore.getSelectedPostThread();
- this.setState({posts, selected});
- }
- }
- onUserChange() {
- const profiles = JSON.parse(JSON.stringify(UserStore.getProfiles()));
- this.setState({profiles});
- }
- resize() {
- $('.post-right__scroll').scrollTop(100000);
- if (this.state.windowWidth > 768) {
- $('.post-right__scroll').perfectScrollbar();
- $('.post-right__scroll').perfectScrollbar('update');
- }
- }
- render() {
- const posts = this.state.posts;
- const selected = this.state.selected;
- const profiles = this.state.profiles || {};
-
- if (posts == null || selected == null) {
- return (
- <div></div>
- );
- }
-
- var postsArray = [];
-
- for (const id in posts) {
- if (posts.hasOwnProperty(id)) {
- const cpost = posts[id];
- if (cpost.root_id === selected.id) {
- postsArray.push(cpost);
- }
- }
- }
-
- // sort failed posts to bottom, followed by pending, and then regular posts
- postsArray.sort(function postSort(a, b) {
- if ((a.state === Constants.POST_LOADING || a.state === Constants.POST_FAILED) && (b.state !== Constants.POST_LOADING && b.state !== Constants.POST_FAILED)) {
- return 1;
- }
- if ((a.state !== Constants.POST_LOADING && a.state !== Constants.POST_FAILED) && (b.state === Constants.POST_LOADING || b.state === Constants.POST_FAILED)) {
- return -1;
- }
-
- if (a.state === Constants.POST_LOADING && b.state === Constants.POST_FAILED) {
- return -1;
- }
- if (a.state === Constants.POST_FAILED && b.state === Constants.POST_LOADING) {
- return 1;
- }
-
- if (a.create_at < b.create_at) {
- return -1;
- }
- if (a.create_at > b.create_at) {
- return 1;
- }
- return 0;
- });
-
- var currentId = UserStore.getCurrentId();
- var searchForm;
- if (currentId != null) {
- searchForm = <SearchBox/>;
- }
-
- let profile;
- if (UserStore.getCurrentId() === selected.user_id) {
- profile = UserStore.getCurrentUser();
- } else {
- profile = profiles[selected.user_id];
- }
-
- return (
- <div className='post-right__container'>
- <FileUploadOverlay overlayType='right'/>
- <div className='search-bar__container sidebar--right__search-header'>{searchForm}</div>
- <div className='sidebar-right__body'>
- <RhsHeaderPost
- fromSearch={this.props.fromSearch}
- isMentionSearch={this.props.isMentionSearch}
- />
- <div className='post-right__scroll'>
- <RootPost
- ref={selected.id}
- post={selected}
- commentCount={postsArray.length}
- user={profile}
- />
- <div className='post-right-comments-container'>
- {postsArray.map(function mapPosts(comPost) {
- let p;
- if (UserStore.getCurrentId() === comPost.user_id) {
- p = UserStore.getCurrentUser();
- } else {
- p = profiles[comPost.user_id];
- }
- return (
- <Comment
- ref={comPost.id}
- key={comPost.id + 'commentKey'}
- post={comPost}
- user={p}
- />
- );
- })}
- </div>
- <div className='post-create__container'>
- <CreateComment
- channelId={selected.channel_id}
- rootId={selected.id}
- />
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-RhsThread.defaultProps = {
- fromSearch: '',
- isMentionSearch: false
-};
-
-RhsThread.propTypes = {
- fromSearch: React.PropTypes.string,
- isMentionSearch: React.PropTypes.bool
-};
diff --git a/web/react/components/root.jsx b/web/react/components/root.jsx
deleted file mode 100644
index 70038203b..000000000
--- a/web/react/components/root.jsx
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import BrowserStore from '../stores/browser_store.jsx';
-import LocalizationStore from '../stores/localization_store.jsx';
-
-var IntlProvider = ReactIntl.IntlProvider;
-
-export default class Root extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- locale: 'en',
- translations: null
- };
-
- this.localizationChanged = this.localizationChanged.bind(this);
- }
- localizationChanged() {
- this.setState({locale: LocalizationStore.getLocale(), translations: LocalizationStore.getTranslations()});
- }
- componentWillMount() {
- // Setup localization listener
- LocalizationStore.addChangeListener(this.localizationChanged);
-
- // Browser store check version
- BrowserStore.checkVersion();
-
- window.onerror = (msg, url, line, column, stack) => {
- var l = {};
- l.level = 'ERROR';
- l.message = 'msg: ' + msg + ' row: ' + line + ' col: ' + column + ' stack: ' + stack + ' url: ' + url;
-
- $.ajax({
- url: '/api/v1/admin/log_client',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(l)
- });
-
- if (window.mm_config.EnableDeveloper === 'true') {
- window.ErrorStore.storeLastError({message: 'DEVELOPER MODE: A javascript error has occured. Please use the javascript console to capture and report the error (row: ' + line + ' col: ' + column + ').'});
- window.ErrorStore.emitChange();
- }
- };
-
- // Ya....
- /*eslint-disable */
- if (window.mm_config.SegmentDeveloperKey != null && window.mm_config.SegmentDeveloperKey !== "") {
- !function(){var analytics=global.window.analytics=global.window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","group","track","ready","alias","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t){var e=document.createElement("script");e.type="text/javascript";e.async=!0;e.src=("https:"===document.location.protocol?"https://":"http://")+"cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)};analytics.SNIPPET_VERSION="3.0.1";
- analytics.load(window.mm_config.SegmentDeveloperKey);
- analytics.page();
- }}();
- } else {
- global.window.analytics = {};
- global.window.analytics.page = function(){};
- global.window.analytics.track = function(){};
- }
- /*eslint-enable */
-
- // Get our localizaiton
- GlobalActions.newLocalizationSelected('en');
- }
- componentWillUnmount() {
- LocalizationStore.removeChangeListener(this.localizationChanged);
- }
- render() {
- if (this.state.translations == null) {
- return <div/>;
- }
-
- return (
- <IntlProvider
- locale={this.state.locale}
- messages={this.state.translations}
- key={this.state.locale}
- >
- {this.props.children}
- </IntlProvider>
- );
- }
-}
-Root.defaultProps = {
-};
-
-Root.propTypes = {
- children: React.PropTypes.object
-};
diff --git a/web/react/components/search_bar.jsx b/web/react/components/search_bar.jsx
deleted file mode 100644
index 3afd0e840..000000000
--- a/web/react/components/search_bar.jsx
+++ /dev/null
@@ -1,206 +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 AsyncClient from '../utils/async_client.jsx';
-import SearchStore from '../stores/search_store.jsx';
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import SuggestionBox from './suggestion/suggestion_box.jsx';
-import SearchChannelProvider from './suggestion/search_channel_provider.jsx';
-import SearchSuggestionList from './suggestion/search_suggestion_list.jsx';
-import SearchUserProvider from './suggestion/search_user_provider.jsx';
-import * as utils from '../utils/utils.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-var ActionTypes = Constants.ActionTypes;
-var Popover = ReactBootstrap.Popover;
-
-const holders = defineMessages({
- search: {
- id: 'search_bar.search',
- defaultMessage: 'Search'
- }
-});
-
-class SearchBar extends React.Component {
- constructor() {
- super();
- this.mounted = false;
-
- this.onListenerChange = this.onListenerChange.bind(this);
- this.handleUserInput = this.handleUserInput.bind(this);
- this.handleUserFocus = this.handleUserFocus.bind(this);
- this.handleUserBlur = this.handleUserBlur.bind(this);
- this.performSearch = this.performSearch.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- const state = this.getSearchTermStateFromStores();
- state.focused = false;
- this.state = state;
-
- this.suggestionProviders = [new SearchChannelProvider(), new SearchUserProvider()];
- }
- getSearchTermStateFromStores() {
- var term = SearchStore.getSearchTerm() || '';
- return {
- searchTerm: term
- };
- }
- componentDidMount() {
- SearchStore.addSearchTermChangeListener(this.onListenerChange);
- this.mounted = true;
- }
- componentWillUnmount() {
- SearchStore.removeSearchTermChangeListener(this.onListenerChange);
- this.mounted = false;
- }
- onListenerChange(doSearch, isMentionSearch) {
- if (this.mounted) {
- var newState = this.getSearchTermStateFromStores();
- if (!utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- if (doSearch) {
- this.performSearch(newState.searchTerm, isMentionSearch);
- }
- }
- }
- clearFocus() {
- $('.search-bar__container').removeClass('focused');
- }
- handleClose(e) {
- e.preventDefault();
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH_TERM,
- term: null,
- do_search: false,
- is_mention_search: false
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
- }
- handleUserInput(text) {
- var term = text;
- SearchStore.storeSearchTerm(term);
- SearchStore.emitSearchTermChange(false);
- this.setState({searchTerm: term});
- }
- handleUserBlur() {
- this.setState({focused: false});
- }
- handleUserFocus() {
- $('.search-bar__container').addClass('focused');
-
- this.setState({focused: true});
- }
- performSearch(terms, isMentionSearch) {
- if (terms.length) {
- this.setState({isSearching: true});
-
- client.search(
- terms,
- (data) => {
- this.setState({isSearching: false});
- if (utils.isMobile()) {
- ReactDOM.findDOMNode(this.refs.search).value = '';
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: data,
- is_mention_search: isMentionSearch
- });
- },
- (err) => {
- this.setState({isSearching: false});
- AsyncClient.dispatchError(err, 'search');
- }
- );
- }
- }
- handleSubmit(e) {
- e.preventDefault();
- this.performSearch(this.state.searchTerm.trim());
- }
-
- render() {
- var isSearching = null;
- if (this.state.isSearching) {
- isSearching = <span className={'glyphicon glyphicon-refresh glyphicon-refresh-animate'}></span>;
- }
-
- let helpClass = 'search-help-popover';
- if (!this.state.searchTerm && this.state.focused) {
- helpClass += ' visible';
- }
-
- return (
- <div>
- <div
- className='sidebar__collapse'
- onClick={this.handleClose}
- >
- <span className='fa fa-angle-left'></span>
- </div>
- <span
- className='search__clear'
- onClick={this.clearFocus}
- >
- <FormattedMessage
- id='search_bar.cancel'
- defaultMessage='Cancel'
- />
- </span>
- <form
- role='form'
- className='search__form'
- onSubmit={this.handleSubmit}
- style={{overflow: 'visible'}}
- autoComplete='off'
- >
- <span className='glyphicon glyphicon-search sidebar__search-icon'/>
- <SuggestionBox
- ref='search'
- className='form-control search-bar'
- placeholder={this.props.intl.formatMessage(holders.search)}
- value={this.state.searchTerm}
- onFocus={this.handleUserFocus}
- onBlur={this.handleUserBlur}
- onUserInput={this.handleUserInput}
- listComponent={SearchSuggestionList}
- providers={this.suggestionProviders}
- />
- {isSearching}
- <Popover
- id='searchbar-help-popup'
- placement='bottom'
- className={helpClass}
- >
- <FormattedHTMLMessage
- id='search_bar.usage'
- defaultMessage='<h4>Search Options</h4><ul><li><span>Use </span><b>"quotation marks"</b><span> to search for phrases</span></li><li><span>Use </span><b>from:</b><span> to find posts from specific users and </span><b>in:</b><span> to find posts in specific channels</span></li></ul>'
- />
- </Popover>
- </form>
- </div>
- );
- }
-}
-
-SearchBar.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(SearchBar);
-
diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx
deleted file mode 100644
index 8985063a0..000000000
--- a/web/react/components/search_results.jsx
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChannelStore from '../stores/channel_store.jsx';
-import SearchStore from '../stores/search_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import SearchBox from './search_bar.jsx';
-import * as Utils from '../utils/utils.jsx';
-import SearchResultsHeader from './search_results_header.jsx';
-import SearchResultsItem from './search_results_item.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-function getStateFromStores() {
- const results = SearchStore.getSearchResults();
-
- const channels = new Map();
-
- if (results && results.order) {
- const channelIds = results.order.map((postId) => results.posts[postId].channel_id);
- for (const id of channelIds) {
- if (channels.has(id)) {
- continue;
- }
-
- channels.set(id, ChannelStore.get(id));
- }
- }
-
- return {
- results,
- channels
- };
-}
-
-export default class SearchResults extends React.Component {
- constructor(props) {
- super(props);
-
- this.mounted = false;
-
- this.onChange = this.onChange.bind(this);
- this.onUserChange = this.onUserChange.bind(this);
- this.resize = this.resize.bind(this);
- this.handleResize = this.handleResize.bind(this);
-
- const state = getStateFromStores();
- state.windowWidth = Utils.windowWidth();
- state.windowHeight = Utils.windowHeight();
- state.profiles = JSON.parse(JSON.stringify(UserStore.getProfiles()));
- this.state = state;
- }
-
- componentDidMount() {
- this.mounted = true;
- SearchStore.addSearchChangeListener(this.onChange);
- ChannelStore.addChangeListener(this.onChange);
- UserStore.addChangeListener(this.onUserChange);
- this.resize();
- window.addEventListener('resize', this.handleResize);
- }
-
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(this.props, nextProps)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(this.state, nextState)) {
- return true;
- }
-
- return false;
- }
-
- componentDidUpdate() {
- this.resize();
- }
-
- componentWillUnmount() {
- SearchStore.removeSearchChangeListener(this.onChange);
- ChannelStore.removeChangeListener(this.onChange);
- UserStore.removeChangeListener(this.onUserChange);
- this.mounted = false;
- window.removeEventListener('resize', this.handleResize);
- }
-
- handleResize() {
- this.setState({
- windowWidth: Utils.windowWidth(),
- windowHeight: Utils.windowHeight()
- });
- }
-
- onChange() {
- if (this.mounted) {
- this.setState(getStateFromStores());
- }
- }
-
- onUserChange() {
- this.setState({profiles: JSON.parse(JSON.stringify(UserStore.getProfiles()))});
- }
-
- resize() {
- $('#search-items-container').scrollTop(0);
- if (this.state.windowWidth > 768) {
- $('#search-items-container').perfectScrollbar();
- }
- }
-
- render() {
- var results = this.state.results;
- var currentId = UserStore.getCurrentId();
- var searchForm = null;
- if (currentId) {
- searchForm = <SearchBox/>;
- }
- var noResults = (!results || !results.order || !results.order.length);
- var searchTerm = SearchStore.getSearchTerm();
- const profiles = this.state.profiles || {};
-
- var ctls = null;
-
- if (!searchTerm && noResults) {
- ctls = (
- <div className='sidebar--right__subheader'>
- <FormattedHTMLMessage
- id='search_results.usage'
- defaultMessage='<ul><li>Use <b>"quotation marks"</b> to search for phrases</li><li>Use <b>from:</b> to find posts from specific users and <b>in:</b> to find posts in specific channels</li></ul>'
- />
- </div>
- );
- } else if (noResults) {
- ctls =
- (
- <div className='sidebar--right__subheader'>
- <h4>
- <FormattedMessage
- id='search_results.noResults'
- defaultMessage='NO RESULTS'
- />
- </h4>
- <FormattedHTMLMessage
- id='search_results.because'
- defaultMessage='<ul>
- <li>If you&#39;re searching a partial phrase (ex. searching "rea", looking for "reach" or "reaction"), append a * to your search term</li>
- <li>Due to the volume of results, two letter searches and common words like "this", "a" and "is" won&#39;t appear in search results</li>
- </ul>'
- />
- </div>
- );
- } else {
- ctls = results.order.map(function mymap(id) {
- const post = results.posts[id];
- let profile;
- if (UserStore.getCurrentId() === post.user_id) {
- profile = UserStore.getCurrentUser();
- } else {
- profile = profiles[post.user_id];
- }
- return (
- <SearchResultsItem
- key={post.id}
- channel={this.state.channels.get(post.channel_id)}
- post={post}
- user={profile}
- term={searchTerm}
- isMentionSearch={this.props.isMentionSearch}
- />
- );
- }, this);
- }
-
- return (
- <div className='sidebar--right__content'>
- <div className='search-bar__container sidebar--right__search-header'>{searchForm}</div>
- <div className='sidebar-right__body'>
- <SearchResultsHeader isMentionSearch={this.props.isMentionSearch}/>
- <div
- id='search-items-container'
- className='search-items-container'
- >
- {ctls}
- </div>
- </div>
- </div>
- );
- }
-}
-
-SearchResults.propTypes = {
- isMentionSearch: React.PropTypes.bool
-};
diff --git a/web/react/components/search_results_header.jsx b/web/react/components/search_results_header.jsx
deleted file mode 100644
index 20fe342dc..000000000
--- a/web/react/components/search_results_header.jsx
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-var ActionTypes = Constants.ActionTypes;
-
-export default class SearchResultsHeader extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleClose = this.handleClose.bind(this);
- }
-
- handleClose(e) {
- e.preventDefault();
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH_TERM,
- term: null,
- do_search: false,
- is_mention_search: false
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
- }
-
- render() {
- var title = (
- <FormattedMessage
- id='search_header.results'
- defaultMessage='Search Results'
- />
- );
-
- if (this.props.isMentionSearch) {
- title = (
- <FormattedMessage
- id='search_header.title2'
- defaultMessage='Recent Mentions'
- />
- );
- }
-
- return (
- <div className='sidebar--right__header'>
- <span className='sidebar--right__title'>{title}</span>
- <button
- type='button'
- className='sidebar--right__close'
- aria-label='Close'
- title='Close'
- onClick={this.handleClose}
- >
- <i className='fa fa-sign-out'/>
- </button>
- </div>
- );
- }
-}
-
-SearchResultsHeader.propTypes = {
- isMentionSearch: React.PropTypes.bool
-};
diff --git a/web/react/components/search_results_item.jsx b/web/react/components/search_results_item.jsx
deleted file mode 100644
index 9c3b0336b..000000000
--- a/web/react/components/search_results_item.jsx
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../stores/user_store.jsx';
-import UserProfile from './user_profile.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import * as TextFormatting from '../utils/text_formatting.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage, FormattedDate} from 'mm-intl';
-
-export default class SearchResultsItem extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleClick = this.handleClick.bind(this);
- this.handleFocusRHSClick = this.handleFocusRHSClick.bind(this);
- }
-
- handleClick(e) {
- e.preventDefault();
-
- GlobalActions.emitPostFocusEvent(this.props.post.id);
-
- if ($(window).width() < 768) {
- $('.sidebar--right').removeClass('move--left');
- $('.inner-wrap').removeClass('move--left');
- }
- }
-
- handleFocusRHSClick(e) {
- e.preventDefault();
- GlobalActions.emitPostFocusRightHandSideFromSearch(this.props.post, this.props.isMentionSearch);
- }
-
- render() {
- let channelName = null;
- const channel = this.props.channel;
- const timestamp = UserStore.getCurrentUser().update_at;
- const user = this.props.user || {};
-
- if (channel) {
- channelName = channel.display_name;
- if (channel.type === 'D') {
- channelName = (
- <FormattedMessage
- id='search_item.direct'
- defaultMessage='Direct Message'
- />
- );
- }
- }
-
- const formattingOptions = {
- searchTerm: this.props.term,
- mentionHighlight: this.props.isMentionSearch
- };
-
- return (
- <div className='search-item__container'>
- <div className='date-separator'>
- <hr className='separator__hr'/>
- <div className='separator__text'>
- <FormattedDate
- value={this.props.post.create_at}
- day='numeric'
- month='long'
- year='numeric'
- />
- </div>
- </div>
- <div
- className='post'
- >
- <div className='search-channel__name'>{channelName}</div>
- <div className='post__content'>
- <div className='post__img'>
- <img
- src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp}
- height='36'
- width='36'
- />
- </div>
- <div>
- <ul className='post__header'>
- <li className='col__name'><strong><UserProfile user={user}/></strong></li>
- <li className='col'>
- <time className='search-item-time'>
- <FormattedDate
- value={this.props.post.create_at}
- hour12={true}
- hour='2-digit'
- minute='2-digit'
- />
- </time>
- </li>
- <li>
- <a
- href='#'
- className='search-item__jump'
- onClick={this.handleClick}
- >
- <FormattedMessage
- id='search_item.jump'
- defaultMessage='Jump'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className='comment-icon__container search-item__comment'
- onClick={this.handleFocusRHSClick}
- >
- <span
- className='comment-icon'
- dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
- />
- </a>
- </li>
- </ul>
- <div className='search-item-snippet'>
- <span
- onClick={TextFormatting.handleClick}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, formattingOptions)}}
- />
- </div>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-SearchResultsItem.propTypes = {
- post: React.PropTypes.object,
- user: React.PropTypes.object,
- channel: React.PropTypes.object,
- isMentionSearch: React.PropTypes.bool,
- term: React.PropTypes.string
-};
diff --git a/web/react/components/setting_item_max.jsx b/web/react/components/setting_item_max.jsx
deleted file mode 100644
index 70f011d26..000000000
--- a/web/react/components/setting_item_max.jsx
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class SettingItemMax extends React.Component {
- render() {
- var clientError = null;
- if (this.props.client_error) {
- clientError = (<div className='form-group'><label className='col-sm-12 has-error'>{this.props.client_error}</label></div>);
- }
-
- var serverError = null;
- if (this.props.server_error) {
- serverError = (<div className='form-group'><label className='col-sm-12 has-error'>{this.props.server_error}</label></div>);
- }
-
- var extraInfo = null;
- if (this.props.extraInfo) {
- extraInfo = (<div className='setting-list__hint'>{this.props.extraInfo}</div>);
- }
-
- var submit = '';
- if (this.props.submit) {
- submit = (
- <a
- className='btn btn-sm btn-primary'
- href='#'
- onClick={this.props.submit}
- >
- <FormattedMessage
- id='setting_item_max.save'
- defaultMessage='Save'
- />
- </a>
- );
- }
-
- var inputs = this.props.inputs;
- var widthClass;
- if (this.props.width === 'full') {
- widthClass = 'col-sm-12';
- } else if (this.props.width === 'medium') {
- widthClass = 'col-sm-10 col-sm-offset-2';
- } else {
- widthClass = 'col-sm-9 col-sm-offset-3';
- }
-
- let title;
- if (this.props.title) {
- title = <li className='col-sm-12 section-title'>{this.props.title}</li>;
- }
-
- return (
- <ul className='section-max form-horizontal'>
- {title}
- <li className={widthClass}>
- <ul className='setting-list'>
- <li className='setting-list-item'>
- {inputs}
- {extraInfo}
- </li>
- <li className='setting-list-item'>
- <hr/>
- {serverError}
- {clientError}
- {submit}
- <a
- className='btn btn-sm theme'
- href='#'
- onClick={this.props.updateSection}
- >
- <FormattedMessage
- id='setting_item_max.cancel'
- defaultMessage='Cancel'
- />
- </a>
- </li>
- </ul>
- </li>
- </ul>
- );
- }
-}
-
-SettingItemMax.propTypes = {
- inputs: React.PropTypes.array,
- client_error: React.PropTypes.string,
- server_error: React.PropTypes.string,
- extraInfo: React.PropTypes.element,
- updateSection: React.PropTypes.func,
- submit: React.PropTypes.func,
- title: React.PropTypes.node,
- width: React.PropTypes.string
-};
diff --git a/web/react/components/setting_item_min.jsx b/web/react/components/setting_item_min.jsx
deleted file mode 100644
index cb2ee0d8f..000000000
--- a/web/react/components/setting_item_min.jsx
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class SettingItemMin extends React.Component {
- render() {
- let editButton = null;
- if (!this.props.disableOpen) {
- editButton = (
- <li className='col-sm-3 section-edit'>
- <a
- className='theme'
- href='#'
- onClick={this.props.updateSection}
- >
- <i className='fa fa-pencil'/>
- <FormattedMessage
- id='setting_item_min.edit'
- defaultMessage='Edit'
- />
- </a>
- </li>
- );
- }
-
- return (
- <ul
- className='section-min'
- onClick={this.props.updateSection}
- >
- <li className='col-sm-9 section-title'>{this.props.title}</li>
- {editButton}
- <li className='col-sm-9 section-describe'>{this.props.describe}</li>
- </ul>
- );
- }
-}
-
-SettingItemMin.propTypes = {
- title: React.PropTypes.node,
- disableOpen: React.PropTypes.bool,
- updateSection: React.PropTypes.func,
- describe: React.PropTypes.node
-};
diff --git a/web/react/components/setting_picture.jsx b/web/react/components/setting_picture.jsx
deleted file mode 100644
index 70e0e6755..000000000
--- a/web/react/components/setting_picture.jsx
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class SettingPicture extends React.Component {
- constructor(props) {
- super(props);
-
- this.setPicture = this.setPicture.bind(this);
- }
-
- setPicture(file) {
- if (file) {
- var reader = new FileReader();
-
- var img = ReactDOM.findDOMNode(this.refs.image);
- reader.onload = function load(e) {
- $(img).attr('src', e.target.result);
- };
-
- reader.readAsDataURL(file);
- }
- }
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.picture) {
- this.setPicture(nextProps.picture);
- }
- }
-
- render() {
- var clientError = null;
- if (this.props.client_error) {
- clientError = <div className='form-group has-error'><label className='control-label'>{this.props.client_error}</label></div>;
- }
- var serverError = null;
- if (this.props.server_error) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.props.server_error}</label></div>;
- }
-
- var img = null;
- if (this.props.picture) {
- img = (
- <img
- ref='image'
- className='profile-img rounded'
- src=''
- />
- );
- } else {
- img = (
- <img
- ref='image'
- className='profile-img rounded'
- src={this.props.src}
- />
- );
- }
-
- var confirmButton;
- if (this.props.loadingPicture) {
- confirmButton = (
- <img
- className='spinner'
- src='/static/images/load.gif'
- />
- );
- } else {
- var confirmButtonClass = 'btn btn-sm';
- if (this.props.submitActive) {
- confirmButtonClass += ' btn-primary';
- } else {
- confirmButtonClass += ' btn-inactive disabled';
- }
-
- confirmButton = (
- <a
- className={confirmButtonClass}
- onClick={this.props.submit}
- >
- <FormattedMessage
- id='setting_picture.save'
- defaultMessage='Save'
- />
- </a>
- );
- }
- var helpText = (
- <FormattedMessage
- id='setting_picture.help'
- defaultMessage='Upload a profile picture in either JPG or PNG format, at least {width}px in width and {height}px height.'
- values={{
- width: global.window.mm_config.ProfileWidth,
- height: global.window.mm_config.ProfileHeight
- }}
- />
- );
-
- var self = this;
- return (
- <ul className='section-max'>
- <li className='col-xs-12 section-title'>{this.props.title}</li>
- <li className='col-xs-offset-3 col-xs-8'>
- <ul className='setting-list'>
- <li className='setting-list-item'>
- {img}
- </li>
- <li className='setting-list-item'>
- {helpText}
- </li>
- <li className='setting-list-item'>
- {serverError}
- {clientError}
- <span className='btn btn-sm btn-primary btn-file sel-btn'>
- <FormattedMessage
- id='setting_picture.select'
- defaultMessage='Select'
- />
- <input
- ref='input'
- accept='.jpg,.png,.bmp'
- type='file'
- onChange={this.props.pictureChange}
- />
- </span>
- {confirmButton}
- <a
- className='btn btn-sm theme'
- href='#'
- onClick={self.props.updateSection}
- >
- <FormattedMessage
- id='setting_picture.cancel'
- defaultMessage='Cancel'
- />
- </a>
- </li>
- </ul>
- </li>
- </ul>
- );
- }
-}
-
-SettingPicture.propTypes = {
- client_error: React.PropTypes.string,
- server_error: React.PropTypes.string,
- src: React.PropTypes.string,
- picture: React.PropTypes.object,
- loadingPicture: React.PropTypes.bool,
- submitActive: React.PropTypes.bool,
- submit: React.PropTypes.func,
- title: React.PropTypes.string,
- pictureChange: React.PropTypes.func
-};
diff --git a/web/react/components/setting_upload.jsx b/web/react/components/setting_upload.jsx
deleted file mode 100644
index 5d5cdfdf7..000000000
--- a/web/react/components/setting_upload.jsx
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class SettingsUpload extends React.Component {
- constructor(props) {
- super(props);
-
- this.doFileSelect = this.doFileSelect.bind(this);
- this.doSubmit = this.doSubmit.bind(this);
-
- this.state = {
- clientError: this.props.clientError,
- serverError: this.props.serverError,
- filename: ''
- };
- }
-
- componentWillReceiveProps() {
- this.setState({
- clientError: this.props.clientError,
- serverError: this.props.serverError
- });
- }
-
- doFileSelect(e) {
- e.preventDefault();
- var filename = $(e.target).val();
- if (filename.substring(3, 11) === 'fakepath') {
- filename = filename.substring(12);
- }
- this.setState({
- clientError: '',
- serverError: '',
- filename
- });
- }
-
- doSubmit(e) {
- e.preventDefault();
- var inputnode = ReactDOM.findDOMNode(this.refs.uploadinput);
- if (inputnode.files && inputnode.files[0]) {
- this.props.submit(inputnode.files[0]);
- } else {
- this.setState({clientError: true});
- }
- }
-
- render() {
- let clientError = null;
- if (this.state.clientError) {
- clientError = (
- <div className='file-status'>
- <FormattedMessage
- id='setting_upload.noFile'
- defaultMessage='No file selected.'
- />
- </div>
- );
- }
- let serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='file-status'>{this.state.serverError}</div>
- );
- }
- let fileNameText = null;
- let submitButtonClass = 'btn btn-sm btn-primary disabled';
- if (this.state.filename) {
- fileNameText = (
- <div className='file-status file-name'>{this.state.filename}</div>
- );
- submitButtonClass = 'btn btn-sm btn-primary';
- }
-
- return (
- <ul className='section-max'>
- <li className='col-sm-12 section-title'>{this.props.title}</li>
- <li className='col-sm-offset-3 col-sm-9'>{this.props.helpText}</li>
- <li className='col-sm-offset-3 col-sm-9'>
- <ul className='setting-list'>
- <li className='setting-list-item'>
- <span className='btn btn-sm btn-primary btn-file sel-btn'>
- <FormattedMessage
- id='setting_upload.select'
- defaultMessage='Select file'
- />
- <input
- ref='uploadinput'
- accept={this.props.fileTypesAccepted}
- type='file'
- onChange={this.doFileSelect}
- />
- </span>
- <a
- className={submitButtonClass}
- onClick={this.doSubmit}
- >
- <FormattedMessage
- id='setting_upload.import'
- defaultMessage='Import'
- />
- </a>
- {fileNameText}
- {serverError}
- {clientError}
- </li>
- </ul>
- </li>
- </ul>
- );
- }
-}
-
-SettingsUpload.propTypes = {
- title: React.PropTypes.string.isRequired,
- submit: React.PropTypes.func.isRequired,
- fileTypesAccepted: React.PropTypes.string.isRequired,
- clientError: React.PropTypes.string,
- serverError: React.PropTypes.string,
- helpText: React.PropTypes.object
-}; \ No newline at end of file
diff --git a/web/react/components/settings_sidebar.jsx b/web/react/components/settings_sidebar.jsx
deleted file mode 100644
index da8001f6f..000000000
--- a/web/react/components/settings_sidebar.jsx
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-
-export default class SettingsSidebar extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleClick = this.handleClick.bind(this);
- }
- handleClick(tab, e) {
- e.preventDefault();
- this.props.updateTab(tab.name);
- $(e.target).closest('.settings-modal').addClass('display--content');
- }
- componentDidMount() {
- if (Utils.isBrowserFirefox()) {
- $('.settings-modal .settings-table .nav').addClass('position--top');
- }
- }
- render() {
- let tabList = this.props.tabs.map(function makeTab(tab) {
- let key = `${tab.name}_li`;
- let className = '';
- if (this.props.activeTab === tab.name) {
- className = 'active';
- }
-
- return (
- <li
- key={key}
- className={className}
- >
- <a
- href='#'
- onClick={this.handleClick.bind(null, tab)}
- >
- <i className={tab.icon}/>
- {tab.uiName}
- </a>
- </li>
- );
- }.bind(this));
-
- return (
- <div>
- <ul className='nav nav-pills nav-stacked'>
- {tabList}
- </ul>
- </div>
- );
- }
-}
-
-SettingsSidebar.propTypes = {
- tabs: React.PropTypes.arrayOf(React.PropTypes.shape({
- name: React.PropTypes.string.isRequired,
- uiName: React.PropTypes.string.isRequired,
- icon: React.PropTypes.string.isRequired
- })).isRequired,
- activeTab: React.PropTypes.string,
- updateTab: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/should_verify_email.jsx b/web/react/components/should_verify_email.jsx
deleted file mode 100644
index c473fe366..000000000
--- a/web/react/components/should_verify_email.jsx
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-import * as Client from '../utils/client.jsx';
-
-export default class ShouldVerifyEmail extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleResend = this.handleResend.bind(this);
-
- this.state = {
- resendStatus: 'none'
- };
- }
- handleResend() {
- const teamName = this.props.location.query.teamname;
- const email = this.props.location.query.email;
-
- this.setState({resendStatus: 'sending'});
-
- Client.resendVerification(() => {
- this.setState({resendStatus: 'success'});
- },
- () => {
- this.setState({resendStatus: 'failure'});
- },
- teamName,
- email);
- }
- render() {
- let resendConfirm = '';
- if (this.state.resendStatus === 'success') {
- resendConfirm = (
- <div>
- <br/>
- <p className='alert alert-success'>
- <i className='fa fa-check'/>
- <FormattedMessage
- id='email_verify.sent'
- defaultMessage=' Verification email sent.'
- />
- </p>
- </div>
- );
- }
-
- if (this.state.resendStatus === 'failure') {
- resendConfirm = (
- <div>
- <br/>
- <p className='alert alert-danger'>
- <i className='fa fa-times'/>
- <FormattedMessage id='email_verify.failed'/>
- </p>
- </div>
- );
- }
-
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <h3>
- <FormattedMessage
- id='email_verify.almost'
- defaultMessage='{siteName}: You are almost done'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </h3>
- <div>
- <p>
- <FormattedMessage
- id='email_verify.notVerifiedBody'
- defaultMessage='Please verify your email address. Check your inbox for an email.'
- />
- </p>
- <button
- onClick={this.handleResend}
- className='btn btn-primary'
- >
- <FormattedMessage
- id='email_verify.resend'
- defaultMessage='Resend Email'
- />
- </button>
- {resendConfirm}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-ShouldVerifyEmail.defaultProps = {
-};
-ShouldVerifyEmail.propTypes = {
- location: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx
deleted file mode 100644
index 5c682d64b..000000000
--- a/web/react/components/sidebar.jsx
+++ /dev/null
@@ -1,714 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import NewChannelFlow from './new_channel_flow.jsx';
-import MoreDirectChannels from './more_direct_channels.jsx';
-import SidebarHeader from './sidebar_header.jsx';
-import UnreadChannelIndicator from './unread_channel_indicator.jsx';
-import TutorialTip from './tutorial/tutorial_tip.jsx';
-
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import TeamStore from '../stores/team_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const Preferences = Constants.Preferences;
-const TutorialSteps = Constants.TutorialSteps;
-
-const Tooltip = ReactBootstrap.Tooltip;
-const OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-export default class Sidebar extends React.Component {
- constructor(props) {
- super(props);
-
- this.badgesActive = false;
- this.firstUnreadChannel = null;
- this.lastUnreadChannel = null;
-
- this.getStateFromStores = this.getStateFromStores.bind(this);
-
- this.onChange = this.onChange.bind(this);
- this.onScroll = this.onScroll.bind(this);
- this.updateUnreadIndicators = this.updateUnreadIndicators.bind(this);
- this.handleLeaveDirectChannel = this.handleLeaveDirectChannel.bind(this);
- this.handleResize = this.handleResize.bind(this);
-
- this.showMoreChannelsModal = this.showMoreChannelsModal.bind(this);
- this.showNewChannelModal = this.showNewChannelModal.bind(this);
- this.hideNewChannelModal = this.hideNewChannelModal.bind(this);
- this.showMoreDirectChannelsModal = this.showMoreDirectChannelsModal.bind(this);
- this.hideMoreDirectChannelsModal = this.hideMoreDirectChannelsModal.bind(this);
-
- this.createChannelElement = this.createChannelElement.bind(this);
- this.updateTitle = this.updateTitle.bind(this);
-
- this.isLeaving = new Map();
-
- const state = this.getStateFromStores();
- state.newChannelModalType = '';
- state.showDirectChannelsModal = false;
- state.loadingDMChannel = -1;
- state.windowWidth = Utils.windowWidth();
- this.state = state;
- }
- getTotalUnreadCount() {
- let msgs = 0;
- let mentions = 0;
- const unreadCounts = this.state.unreadCounts;
-
- Object.keys(unreadCounts).forEach((chId) => {
- msgs += unreadCounts[chId].msgs;
- mentions += unreadCounts[chId].mentions;
- });
-
- return {msgs, mentions};
- }
- getStateFromStores() {
- const members = ChannelStore.getAllMembers();
- const currentChannelId = ChannelStore.getCurrentId();
- const currentUserId = UserStore.getCurrentId();
-
- const channels = Object.assign([], ChannelStore.getAll());
- channels.sort((a, b) => a.display_name.localeCompare(b.display_name));
-
- const publicChannels = channels.filter((channel) => channel.type === Constants.OPEN_CHANNEL);
- const privateChannels = channels.filter((channel) => channel.type === Constants.PRIVATE_CHANNEL);
-
- const preferences = PreferenceStore.getCategory(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW);
-
- const directChannels = [];
- for (const preference of preferences) {
- if (preference.value !== 'true') {
- continue;
- }
-
- const teammateId = preference.name;
-
- let directChannel = channels.find(Utils.isDirectChannelForUser.bind(null, teammateId));
-
- // a direct channel doesn't exist yet so create a fake one
- if (directChannel == null) {
- directChannel = {
- name: Utils.getDirectChannelName(currentUserId, teammateId),
- last_post_at: 0,
- total_msg_count: 0,
- type: Constants.DM_CHANNEL,
- fake: true
- };
- } else {
- directChannel = JSON.parse(JSON.stringify(directChannel));
- }
-
- directChannel.display_name = Utils.displayUsername(teammateId);
- directChannel.teammate_id = teammateId;
- directChannel.status = UserStore.getStatus(teammateId);
-
- directChannels.push(directChannel);
- }
-
- directChannels.sort(this.sortChannelsByDisplayName);
-
- const hiddenDirectChannelCount = UserStore.getActiveOnlyProfileList(true).length - directChannels.length;
-
- const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
-
- return {
- activeId: currentChannelId,
- members,
- publicChannels,
- privateChannels,
- directChannels,
- hiddenDirectChannelCount,
- unreadCounts: JSON.parse(JSON.stringify(ChannelStore.getUnreadCounts())),
- showTutorialTip: tutorialStep === TutorialSteps.CHANNEL_POPOVER,
- currentTeam: TeamStore.getCurrent(),
- currentUser: UserStore.getCurrentUser()
- };
- }
-
- componentDidMount() {
- ChannelStore.addChangeListener(this.onChange);
- UserStore.addChangeListener(this.onChange);
- UserStore.addStatusesChangeListener(this.onChange);
- TeamStore.addChangeListener(this.onChange);
- PreferenceStore.addChangeListener(this.onChange);
-
- this.updateTitle();
- this.updateUnreadIndicators();
-
- window.addEventListener('resize', this.handleResize);
-
- if ($(window).width() > 768) {
- $('.nav-pills__container').perfectScrollbar();
- }
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(nextState, this.state)) {
- return true;
- }
- return false;
- }
- componentDidUpdate() {
- this.updateTitle();
- this.updateUnreadIndicators();
- }
- componentWillUnmount() {
- window.removeEventListener('resize', this.handleResize);
-
- ChannelStore.removeChangeListener(this.onChange);
- UserStore.removeChangeListener(this.onChange);
- UserStore.removeStatusesChangeListener(this.onChange);
- TeamStore.removeChangeListener(this.onChange);
- PreferenceStore.removeChangeListener(this.onChange);
- }
- handleResize() {
- this.setState({
- windowWidth: Utils.windowWidth(),
- windowHeight: Utils.windowHeight()
- });
- }
- onChange() {
- this.setState(this.getStateFromStores());
- }
- updateTitle() {
- const channel = ChannelStore.getCurrent();
- if (channel && this.state.currentTeam) {
- let currentSiteName = '';
- if (global.window.mm_config.SiteName != null) {
- currentSiteName = global.window.mm_config.SiteName;
- }
-
- let currentChannelName = channel.display_name;
- if (channel.type === 'D') {
- const teammate = Utils.getDirectTeammate(channel.id);
- if (teammate != null) {
- currentChannelName = teammate.username;
- }
- }
-
- const unread = this.getTotalUnreadCount();
- const mentionTitle = unread.mentions > 0 ? '(' + unread.mentions + ') ' : '';
- const unreadTitle = unread.msgs > 0 ? '* ' : '';
- document.title = mentionTitle + unreadTitle + currentChannelName + ' - ' + this.state.currentTeam.display_name + ' ' + currentSiteName;
- }
- }
- onScroll() {
- this.updateUnreadIndicators();
- }
- updateUnreadIndicators() {
- const container = $(ReactDOM.findDOMNode(this.refs.container));
-
- var showTopUnread = false;
- var showBottomUnread = false;
-
- if (this.firstUnreadChannel) {
- var firstUnreadElement = $(ReactDOM.findDOMNode(this.refs[this.firstUnreadChannel]));
-
- if (firstUnreadElement.position().top + firstUnreadElement.height() < 0) {
- showTopUnread = true;
- }
- }
-
- if (this.lastUnreadChannel) {
- var lastUnreadElement = $(ReactDOM.findDOMNode(this.refs[this.lastUnreadChannel]));
-
- if (lastUnreadElement.position().top > container.height()) {
- showBottomUnread = true;
- }
- }
-
- this.setState({
- showTopUnread,
- showBottomUnread
- });
- }
-
- handleLeaveDirectChannel(channel) {
- if (!this.isLeaving.get(channel.id)) {
- this.isLeaving.set(channel.id, true);
-
- const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, channel.teammate_id, 'false');
-
- // bypass AsyncClient since we've already saved the updated preferences
- Client.savePreferences(
- [preference],
- () => {
- this.isLeaving.set(channel.id, false);
- },
- () => {
- this.isLeaving.set(channel.id, false);
- }
- );
-
- this.setState(this.getStateFromStores());
- }
-
- if (channel.id === this.state.activeId) {
- Utils.switchChannel(ChannelStore.getByName(Constants.DEFAULT_CHANNEL));
- }
- }
-
- sortChannelsByDisplayName(a, b) {
- return a.display_name.localeCompare(b.display_name);
- }
-
- showMoreChannelsModal() {
- // manually show the modal because using data-toggle messes with keyboard focus when the modal is dismissed
- $('#more_channels').modal({'data-channeltype': 'O'}).modal('show');
- }
-
- showNewChannelModal(type) {
- this.setState({newChannelModalType: type});
- }
- hideNewChannelModal() {
- this.setState({newChannelModalType: ''});
- }
-
- showMoreDirectChannelsModal() {
- this.setState({showDirectChannelsModal: true});
- }
- hideMoreDirectChannelsModal() {
- this.setState({showDirectChannelsModal: false});
- }
-
- createTutorialTip() {
- const screens = [];
-
- screens.push(
- <div>
- <FormattedHTMLMessage
- id='sidebar.tutorialScreen1'
- defaultMessage='<h4>Channels</h4><p><strong>Channels</strong> organize conversations across different topics. They’re open to everyone on your team. To send private communications use <strong>Direct Messages</strong> for a single person or <strong>Private Groups</strong> for multiple people.</p>'
- />
- </div>
- );
-
- screens.push(
- <div>
- <FormattedHTMLMessage
- id='sidebar.tutorialScreen2'
- defaultMessage='<h4>"Town Square" and "Off-Topic" channels</h4>
- <p>Here are two public channels to start:</p>
- <p><strong>Town Square</strong> is a place for team-wide communication. Everyone in your team is a member of this channel.</p>
- <p><strong>Off-Topic</strong> is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.</p>'
- />
- </div>
- );
-
- screens.push(
- <div>
- <FormattedHTMLMessage
- id='sidebar.tutorialScreen3'
- defaultMessage='<h4>Creating and Joining Channels</h4>
- <p>Click <strong>"More..."</strong> to create a new channel or join an existing one.</p>
- <p>You can also create a new channel or private group by clicking the <strong>"+" symbol</strong> next to the channel or private group header.</p>'
- />
- </div>
- );
-
- return (
- <TutorialTip
- placement='right'
- screens={screens}
- overlayClass='tip-overlay--sidebar'
- />
- );
- }
-
- createChannelElement(channel, index, arr, handleClose) {
- const members = this.state.members;
- const activeId = this.state.activeId;
- const channelMember = members[channel.id];
- const unreadCount = this.state.unreadCounts[channel.id] || {msgs: 0, mentions: 0};
- let msgCount;
-
- let linkClass = '';
- if (channel.id === activeId) {
- linkClass = 'active';
- }
-
- let rowClass = 'sidebar-channel';
-
- var unread = false;
- if (channelMember) {
- msgCount = unreadCount.msgs + unreadCount.mentions;
- unread = msgCount > 0 || channelMember.mention_count > 0;
- }
-
- if (unread) {
- rowClass += ' unread-title';
-
- if (channel.id !== activeId) {
- if (!this.firstUnreadChannel) {
- this.firstUnreadChannel = channel.name;
- }
- this.lastUnreadChannel = channel.name;
- }
- }
-
- var badge = null;
- if (channelMember) {
- if (unreadCount.mentions) {
- badge = <span className='badge pull-right small'>{unreadCount.mentions}</span>;
- this.badgesActive = true;
- }
- } else if (this.state.loadingDMChannel === index && channel.type === 'D') {
- badge = (
- <img
- className='channel-loading-gif pull-right'
- src='/static/images/load.gif'
- />
- );
- }
-
- if (msgCount > 0) {
- rowClass += ' has-badge';
- }
-
- // set up status icon for direct message channels
- var status = null;
- if (channel.type === 'D') {
- var statusIcon = '';
- if (channel.status === 'online') {
- statusIcon = Constants.ONLINE_ICON_SVG;
- } else if (channel.status === 'away') {
- statusIcon = Constants.AWAY_ICON_SVG;
- } else {
- statusIcon = Constants.OFFLINE_ICON_SVG;
- }
- status = (
- <span
- className='status'
- dangerouslySetInnerHTML={{__html: statusIcon}}
- />
- );
- }
-
- var icon = null;
- if (channel.type === 'O') {
- icon = <div className='status'><i className='fa fa-globe'></i></div>;
- } else if (channel.type === 'P') {
- icon = <div className='status'><i className='fa fa-lock'></i></div>;
- }
-
- // set up click handler to switch channels (or create a new channel for non-existant ones)
- var handleClick = null;
- var href = '#';
-
- if (!channel.fake) {
- handleClick = function clickHandler(e) {
- if (e.target.attributes.getNamedItem('data-close')) {
- handleClose(channel);
- } else {
- Utils.switchChannel(channel);
- }
-
- e.preventDefault();
- };
- } else if (channel.fake) {
- // It's a direct message channel that doesn't exist yet so let's create it now
- var otherUserId = Utils.getUserIdFromChannelName(channel);
-
- if (this.state.loadingDMChannel === -1) {
- handleClick = function clickHandler(e) {
- e.preventDefault();
-
- if (e.target.attributes.getNamedItem('data-close')) {
- handleClose(channel);
- } else {
- this.setState({loadingDMChannel: index});
-
- Client.createDirectChannel(channel, otherUserId,
- (data) => {
- this.setState({loadingDMChannel: -1});
- AsyncClient.getChannel(data.id);
- Utils.switchChannel(data);
- },
- () => {
- this.setState({loadingDMChannel: -1});
- window.location.href = '/' + this.state.currentTeam.name;
- }
- );
- }
- }.bind(this);
- }
- }
-
- let closeButton = null;
- const removeTooltip = (
- <Tooltip id='remove-dm-tooltip'>
- <FormattedMessage
- id='sidebar.removeList'
- defaultMessage='Remove from list'
- />
- </Tooltip>
- );
- if (handleClose && !badge) {
- closeButton = (
- <OverlayTrigger
- delayShow={1000}
- placement='top'
- overlay={removeTooltip}
- >
- <span
- className='btn-close'
- data-close='true'
- >
- {'×'}
- </span>
- </OverlayTrigger>
- );
-
- rowClass += ' has-close';
- }
-
- let tutorialTip = null;
- if (this.state.showTutorialTip && channel.name === Constants.DEFAULT_CHANNEL) {
- tutorialTip = this.createTutorialTip();
- }
-
- return (
- <li
- key={channel.name}
- ref={channel.name}
- className={linkClass}
- >
- <a
- className={rowClass}
- href={href}
- onClick={handleClick}
- >
- {icon}
- {status}
- {channel.display_name}
- {badge}
- {closeButton}
- </a>
- {tutorialTip}
- </li>
- );
- }
- render() {
- // Check if we have all info needed to render
- if (this.state.currentTeam == null || this.state.currentUser == null) {
- return (<div/>);
- }
-
- this.badgesActive = false;
-
- // keep track of the first and last unread channels so we can use them to set the unread indicators
- this.firstUnreadChannel = null;
- this.lastUnreadChannel = null;
-
- // create elements for all 3 types of channels
- const publicChannelItems = this.state.publicChannels.map(this.createChannelElement);
-
- const privateChannelItems = this.state.privateChannels.map(this.createChannelElement);
-
- const directMessageItems = this.state.directChannels.map((channel, index, arr) => {
- return this.createChannelElement(channel, index, arr, this.handleLeaveDirectChannel);
- });
-
- // update the favicon to show if there are any notifications
- var link = document.createElement('link');
- link.type = 'image/x-icon';
- link.rel = 'shortcut icon';
- link.id = 'favicon';
- if (this.badgesActive) {
- link.href = '/static/images/favicon/redfavicon-16x16.png';
- } else {
- link.href = '/static/images/favicon/favicon-16x16.png';
- }
- var head = document.getElementsByTagName('head')[0];
- var oldLink = document.getElementById('favicon');
- if (oldLink) {
- head.removeChild(oldLink);
- }
- head.appendChild(link);
-
- var directMessageMore = null;
- if (this.state.hiddenDirectChannelCount > 0) {
- directMessageMore = (
- <li key='more'>
- <a
- href='#'
- onClick={this.showMoreDirectChannelsModal}
- >
- <FormattedMessage
- id='sidebar.more'
- defaultMessage='More ({count})'
- values={{
- count: this.state.hiddenDirectChannelCount
- }}
- />
- </a>
- </li>
- );
- }
-
- let showChannelModal = false;
- if (this.state.newChannelModalType !== '') {
- showChannelModal = true;
- }
-
- const createChannelTootlip = (
- <Tooltip id='new-channel-tooltip' >
- <FormattedMessage
- id='sidebar.createChannel'
- defaultMessage='Create new channel'
- />
- </Tooltip>
- );
- const createGroupTootlip = (
- <Tooltip id='new-group-tooltip'>
- <FormattedMessage
- id='sidebar.createGroup'
- defaultMessage='Create new group'
- />
- </Tooltip>
- );
-
- const above = (
- <FormattedMessage
- id='sidebar.unreadAbove'
- defaultMessage='Unread post(s) above'
- />
- );
-
- const below = (
- <FormattedMessage
- id='sidebar.unreadBelow'
- defaultMessage='Unread post(s) below'
- />
- );
-
- return (
- <div
- className='sidebar--left'
- id='sidebar-left'
- >
- <NewChannelFlow
- show={showChannelModal}
- channelType={this.state.newChannelModalType}
- onModalDismissed={this.hideNewChannelModal}
- />
- <MoreDirectChannels
- show={this.state.showDirectChannelsModal}
- onModalDismissed={this.hideMoreDirectChannelsModal}
- />
-
- <SidebarHeader
- teamDisplayName={this.state.currentTeam.display_name}
- teamName={this.state.currentTeam.name}
- teamType={this.state.currentTeam.type}
- currentUser={this.state.currentUser}
- />
-
- <UnreadChannelIndicator
- show={this.state.showTopUnread}
- extraClass='nav-pills__unread-indicator-top'
- text={above}
- />
- <UnreadChannelIndicator
- show={this.state.showBottomUnread}
- extraClass='nav-pills__unread-indicator-bottom'
- text={below}
- />
-
- <div
- ref='container'
- className='nav-pills__container'
- onScroll={this.onScroll}
- >
- <ul className='nav nav-pills nav-stacked'>
- <li>
- <h4>
- <FormattedMessage
- id='sidebar.channels'
- defaultMessage='Channels'
- />
- <OverlayTrigger
- delayShow={500}
- placement='top'
- overlay={createChannelTootlip}
- >
- <a
- className='add-channel-btn'
- href='#'
- onClick={this.showNewChannelModal.bind(this, 'O')}
- >
- {'+'}
- </a>
- </OverlayTrigger>
- </h4>
- </li>
- {publicChannelItems}
- <li>
- <a
- href='#'
- className='nav-more'
- onClick={this.showMoreChannelsModal}
- >
- <FormattedMessage
- id='sidebar.moreElips'
- defaultMessage='More...'
- />
- </a>
- </li>
- </ul>
-
- <ul className='nav nav-pills nav-stacked'>
- <li>
- <h4>
- <FormattedMessage
- id='sidebar.pg'
- defaultMessage='Private Groups'
- />
- <OverlayTrigger
- delayShow={500}
- placement='top'
- overlay={createGroupTootlip}
- >
- <a
- className='add-channel-btn'
- href='#'
- onClick={this.showNewChannelModal.bind(this, 'P')}
- >
- {'+'}
- </a>
- </OverlayTrigger>
- </h4>
- </li>
- {privateChannelItems}
- </ul>
- <ul className='nav nav-pills nav-stacked'>
- <li>
- <h4>
- <FormattedMessage
- id='sidebar.direct'
- defaultMessage='Direct Messages'
- />
- </h4>
- </li>
- {directMessageItems}
- {directMessageMore}
- </ul>
- </div>
- </div>
- );
- }
-}
-
-Sidebar.defaultProps = {
-};
-Sidebar.propTypes = {
-};
diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx
deleted file mode 100644
index 00d30948a..000000000
--- a/web/react/components/sidebar_header.jsx
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import NavbarDropdown from './navbar_dropdown.jsx';
-import TutorialTip from './tutorial/tutorial_tip.jsx';
-
-import PreferenceStore from '../stores/preference_store.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {FormattedHTMLMessage} from 'mm-intl';
-
-const Preferences = Constants.Preferences;
-const TutorialSteps = Constants.TutorialSteps;
-
-const Tooltip = ReactBootstrap.Tooltip;
-const OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-export default class SidebarHeader extends React.Component {
- constructor(props) {
- super(props);
-
- this.toggleDropdown = this.toggleDropdown.bind(this);
- this.onPreferenceChange = this.onPreferenceChange.bind(this);
-
- this.state = this.getStateFromStores();
- }
- componentDidMount() {
- PreferenceStore.addChangeListener(this.onPreferenceChange);
- }
- componentWillUnmount() {
- PreferenceStore.removeChangeListener(this.onPreferenceChange);
- }
- getStateFromStores() {
- const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, this.props.currentUser.id, 999);
-
- return {showTutorialTip: tutorialStep === TutorialSteps.MENU_POPOVER};
- }
- onPreferenceChange() {
- this.setState(this.getStateFromStores());
- }
- toggleDropdown(e) {
- e.preventDefault();
- if (this.refs.dropdown.blockToggle) {
- this.refs.dropdown.blockToggle = false;
- return;
- }
- $('.team__header').find('.dropdown-toggle').dropdown('toggle');
- }
- createTutorialTip() {
- const screens = [];
-
- screens.push(
- <div>
- <FormattedHTMLMessage
- id='sidebar_header.tutorial'
- defaultMessage='<h4>Main Menu</h4>
- <p>The <strong>Main Menu</strong> is where you can <strong>Invite New Members</strong>, access your <strong>Account Settings</strong> and set your <strong>Theme Color</strong>.</p>
- <p>Team administrators can also access their <strong>Team Settings</strong> from this menu.</p><p>System administrators will find a <strong>System Console</strong> option to administrate the entire system.</p>'
- />
- </div>
- );
-
- return (
- <div
- onClick={this.toggleDropdown}
- >
- <TutorialTip
- ref='tip'
- placement='right'
- screens={screens}
- overlayClass='tip-overlay--header'
- />
- </div>
- );
- }
- render() {
- var me = this.props.currentUser;
- var profilePicture = null;
-
- if (!me) {
- return null;
- }
-
- if (me.last_picture_update) {
- profilePicture = (
- <img
- className='user__picture'
- src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at}
- />
- );
- }
-
- let tutorialTip = null;
- if (this.state.showTutorialTip) {
- tutorialTip = this.createTutorialTip();
- }
-
- return (
- <div className='team__header theme'>
- {tutorialTip}
- <a
- href='#'
- onClick={this.toggleDropdown}
- >
- {profilePicture}
- <div className='header__info'>
- <div className='user__name'>{'@' + me.username}</div>
- <OverlayTrigger
- trigger={['hover', 'focus']}
- delayShow={1000}
- placement='bottom'
- overlay={<Tooltip id='team-name__tooltip'>{this.props.teamDisplayName}</Tooltip>}
- ref='descriptionOverlay'
- >
- <div className='team__name'>{this.props.teamDisplayName}</div>
- </OverlayTrigger>
- </div>
- </a>
- <NavbarDropdown
- ref='dropdown'
- teamType={this.props.teamType}
- teamDisplayName={this.props.teamDisplayName}
- teamName={this.props.teamName}
- currentUser={this.props.currentUser}
- />
- </div>
- );
- }
-}
-
-SidebarHeader.defaultProps = {
- teamDisplayName: '',
- teamType: ''
-};
-SidebarHeader.propTypes = {
- teamDisplayName: React.PropTypes.string,
- teamName: React.PropTypes.string,
- teamType: React.PropTypes.string,
- currentUser: React.PropTypes.object
-};
diff --git a/web/react/components/sidebar_right.jsx b/web/react/components/sidebar_right.jsx
deleted file mode 100644
index fc9888626..000000000
--- a/web/react/components/sidebar_right.jsx
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SearchResults from './search_results.jsx';
-import RhsThread from './rhs_thread.jsx';
-import SearchStore from '../stores/search_store.jsx';
-import PostStore from '../stores/post_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-const SIDEBAR_SCROLL_DELAY = 500;
-
-export default class SidebarRight extends React.Component {
- constructor(props) {
- super(props);
-
- this.plScrolledToBottom = true;
-
- this.onSelectedChange = this.onSelectedChange.bind(this);
- this.onSearchChange = this.onSearchChange.bind(this);
- this.onShowSearch = this.onShowSearch.bind(this);
-
- this.doStrangeThings = this.doStrangeThings.bind(this);
-
- this.state = this.getStateFromStores();
- }
- getStateFromStores() {
- return {
- search_visible: SearchStore.getSearchResults() != null,
- post_right_visible: PostStore.getSelectedPost() != null,
- is_mention_search: SearchStore.getIsMentionSearch()
- };
- }
- componentDidMount() {
- SearchStore.addSearchChangeListener(this.onSearchChange);
- PostStore.addSelectedPostChangeListener(this.onSelectedChange);
- SearchStore.addShowSearchListener(this.onShowSearch);
- this.doStrangeThings();
- }
- componentWillUnmount() {
- SearchStore.removeSearchChangeListener(this.onSearchChange);
- PostStore.removeSelectedPostChangeListener(this.onSelectedChange);
- SearchStore.removeShowSearchListener(this.onShowSearch);
- }
- componentWillUpdate(nextProps, nextState) {
- const isOpen = this.state.search_visible || this.state.post_right_visible;
- const willOpen = nextState.search_visible || nextState.post_right_visible;
-
- if (!isOpen && willOpen) {
- setTimeout(() => PostStore.jumpPostsViewSidebarOpen(), SIDEBAR_SCROLL_DELAY);
- }
- }
- doStrangeThings() {
- // We should have a better way to do this stuff
- // Hence the function name.
- var windowWidth = $(window).outerWidth();
- var sidebarRightWidth = $('.sidebar--right').outerWidth();
-
- $('.inner-wrap').removeClass('.move--right');
- $('.inner-wrap').addClass('move--left');
- $('.sidebar--left').removeClass('move--right');
- $('.sidebar--right').addClass('move--left');
-
- //$('.sidebar--right').prepend('<div class="sidebar__overlay"></div>');
- if (this.state.search_visible || this.state.post_right_visible) {
- if (windowWidth > 960) {
- $('.inner-wrap').velocity({marginRight: sidebarRightWidth}, {duration: 500, easing: 'easeOutSine'});
- $('.sidebar--right').velocity({translateX: 0}, {duration: 500, easing: 'easeOutSine'});
- } else {
- $('.inner-wrap, .sidebar--right').attr('style', '');
- }
- } else {
- if (windowWidth > 960) {
- $('.inner-wrap').velocity({marginRight: 0}, {duration: 500, easing: 'easeOutSine'});
- $('.sidebar--right').velocity({translateX: sidebarRightWidth}, {duration: 500, easing: 'easeOutSine'});
- } else {
- $('.inner-wrap, .sidebar--right').attr('style', '');
- }
- $('.inner-wrap').removeClass('move--left').removeClass('move--right');
- $('.sidebar--right').removeClass('move--left');
- return (
- <div></div>
- );
- }
-
- /*setTimeout(() => {
- $('.sidebar__overlay').fadeOut('200', () => {
- $('.sidebar__overlay').remove();
- });
- }, 500);*/
- return null;
- }
- componentDidUpdate() {
- this.doStrangeThings();
- }
- onSelectedChange(fromSearch) {
- var newState = this.getStateFromStores(fromSearch);
- newState.from_search = fromSearch;
- if (!Utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- }
- onSearchChange() {
- var newState = this.getStateFromStores();
- if (!Utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- }
- onShowSearch() {
- if (!this.state.search_visible) {
- this.setState({
- search_visible: true
- });
- }
- }
- render() {
- var content = '';
-
- if (this.state.search_visible) {
- content = <SearchResults isMentionSearch={this.state.is_mention_search}/>;
- } else if (this.state.post_right_visible) {
- content = (
- <RhsThread
- fromSearch={this.state.from_search}
- isMentionSearch={this.state.is_mention_search}
- />
- );
- }
-
- return (
- <div
- className='sidebar--right'
- id='sidebar-right'
- >
- <div className='sidebar-right-container'>
- {content}
- </div>
- </div>
- );
- }
-}
diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx
deleted file mode 100644
index c7c5bcfd6..000000000
--- a/web/react/components/sidebar_right_menu.jsx
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import TeamMembersModal from './team_members_modal.jsx';
-import ToggleModalButton from './toggle_modal_button.jsx';
-import UserSettingsModal from './user_settings/user_settings_modal.jsx';
-import UserStore from '../stores/user_store.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-import {Link} from 'react-router';
-
-export default class SidebarRightMenu extends React.Component {
- componentDidMount() {
- $('.sidebar--left .dropdown-menu').perfectScrollbar();
- }
-
- constructor(props) {
- super(props);
-
- this.state = {
- showUserSettingsModal: false
- };
- }
-
- render() {
- var teamLink = '';
- var inviteLink = '';
- var teamSettingsLink = '';
- var manageLink = '';
- var consoleLink = '';
- var currentUser = UserStore.getCurrentUser();
- var isAdmin = false;
- var isSystemAdmin = false;
-
- if (currentUser != null) {
- isAdmin = Utils.isAdmin(currentUser.roles);
- isSystemAdmin = Utils.isSystemAdmin(currentUser.roles);
-
- inviteLink = (
- <li>
- <a
- href='#'
- onClick={GlobalActions.showInviteMemberModal}
- >
- <i className='fa fa-user'></i>
- <FormattedMessage
- id='sidebar_right_menu.inviteNew'
- defaultMessage='Invite New Member'
- />
- </a>
- </li>
- );
-
- if (this.props.teamType === 'O') {
- teamLink = (
- <li>
- <a
- href='#'
- onClick={GlobalActions.showGetTeamInviteLinkModal}
- >
- <i className='glyphicon glyphicon-link'></i>
- <FormattedMessage
- id='sidebar_right_menu.teamLink'
- defaultMessage='Get Team Invite Link'
- />
- </a>
- </li>
- );
- }
- }
-
- if (isAdmin) {
- teamSettingsLink = (
- <li>
- <a
- href='#'
- data-toggle='modal'
- data-target='#team_settings'
- >
- <i className='fa fa-globe'></i>
- <FormattedMessage
- id='sidebar_right_menu.teamSettings'
- defaultMessage='Team Settings'
- />
- </a>
- </li>
- );
- manageLink = (
- <li>
- <ToggleModalButton dialogType={TeamMembersModal}>
- <i className='fa fa-users'></i>
- <FormattedMessage
- id='sidebar_right_menu.manageMembers'
- defaultMessage='Manage Members'
- />
- </ToggleModalButton>
- </li>
- );
- }
-
- if (isSystemAdmin && !Utils.isMobile()) {
- consoleLink = (
- <li>
- <a
- href={'/admin_console'}
- >
- <i className='fa fa-wrench'></i>
- <FormattedMessage
- id='sidebar_right_menu.console'
- defaultMessage='System Console'
- />
- </a>
- </li>
- );
- }
-
- var siteName = '';
- if (global.window.mm_config.SiteName != null) {
- siteName = global.window.mm_config.SiteName;
- }
- var teamDisplayName = siteName;
- if (this.props.teamDisplayName) {
- teamDisplayName = this.props.teamDisplayName;
- }
-
- let helpLink = null;
- if (global.window.mm_config.HelpLink) {
- helpLink = (
- <li>
- <a
- target='_blank'
- href={global.window.mm_config.HelpLink}
- >
- <i className='fa fa-question'></i>
- <FormattedMessage
- id='sidebar_right_menu.help'
- defaultMessage='Help'
- />
- </a>
- </li>
- );
- }
-
- let reportLink = null;
- if (global.window.mm_config.ReportAProblemLink) {
- reportLink = (
- <li>
- <a
- target='_blank'
- href={global.window.mm_config.ReportAProblemLink}
- >
- <i className='fa fa-phone'></i>
- <FormattedMessage
- id='sidebar_right_menu.report'
- defaultMessage='Report a Problem'
- />
- </a>
- </li>
- );
- }
- return (
- <div
- className='sidebar--menu'
- id='sidebar-menu'
- >
- <div className='team__header theme'>
- <a
- className='team__name'
- href='/channels/town-square'
- >{teamDisplayName}</a>
- </div>
-
- <div className='nav-pills__container'>
- <ul className='nav nav-pills nav-stacked'>
- <li>
- <a
- href='#'
- onClick={() => this.setState({showUserSettingsModal: true})}
- >
- <i className='fa fa-cog'></i>
- <FormattedMessage
- id='sidebar_right_menu.accountSettings'
- defaultMessage='Account Settings'
- />
- </a>
- </li>
- {teamSettingsLink}
- {inviteLink}
- {teamLink}
- {manageLink}
- {consoleLink}
- <li>
- <Link to={Utils.getTeamURLFromAddressBar() + '/logout'}>
- <i className='fa fa-sign-out'></i>
- <FormattedMessage
- id='sidebar_right_menu.logout'
- defaultMessage='Logout'
- />
- </Link>
- </li>
- <li className='divider'></li>
- {helpLink}
- {reportLink}
- </ul>
- </div>
- <UserSettingsModal
- show={this.state.showUserSettingsModal}
- onModalDismissed={() => this.setState({showUserSettingsModal: false})}
- />
- </div>
- );
- }
-}
-
-SidebarRightMenu.propTypes = {
- teamType: React.PropTypes.string,
- teamDisplayName: React.PropTypes.string
-};
diff --git a/web/react/components/signup_team.jsx b/web/react/components/signup_team.jsx
deleted file mode 100644
index 2adf8d111..000000000
--- a/web/react/components/signup_team.jsx
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChoosePage from './team_signup_choose_auth.jsx';
-import EmailSignUpPage from './team_signup_with_email.jsx';
-import SSOSignupPage from './team_signup_with_sso.jsx';
-import LdapSignUpPage from './team_signup_with_ldap.jsx';
-import Constants from '../utils/constants.jsx';
-import TeamStore from '../stores/team_store.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class TeamSignUp extends React.Component {
- constructor(props) {
- super(props);
-
- this.updatePage = this.updatePage.bind(this);
- this.onTeamUpdate = this.onTeamUpdate.bind(this);
-
- var count = 0;
-
- if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
- count = count + 1;
- }
-
- if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
- count = count + 1;
- }
-
- if (global.window.mm_config.EnableLdap === 'true') {
- count = count + 1;
- }
-
- if (count > 1) {
- this.state = {page: 'choose'};
- } else if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
- this.state = {page: 'email'};
- } else if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
- this.state = {page: 'gitlab'};
- } else if (global.window.mm_config.EnableLdap === 'true') {
- this.state = {page: 'ldap'};
- } else {
- this.state = {page: 'none'};
- }
- }
-
- updatePage(page) {
- this.setState({page});
- }
-
- componentWillMount() {
- if (global.window.mm_config.EnableTeamListing === 'true') {
- AsyncClient.getAllTeams();
- this.onTeamUpdate();
- }
- }
-
- componentDidMount() {
- TeamStore.addChangeListener(this.onTeamUpdate);
- }
-
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.onTeamUpdate);
- }
-
- onTeamUpdate() {
- this.setState({
- teams: TeamStore.getAll()
- });
- }
-
- render() {
- let teamListing = null;
-
- if (global.window.mm_config.EnableTeamListing === 'true') {
- if (this.state.teams == null) {
- teamListing = (<div/>);
- } else if (this.state.teams.length === 0) {
- if (global.window.mm_config.EnableTeamCreation !== 'true') {
- teamListing = (
- <div>
- <FormattedMessage
- id='signup_team.noTeams'
- defaultMessage='There are no teams include in the Team Directory and team creation has been disabled.'
- />
- </div>
- );
- }
- } else {
- teamListing = (
- <div>
- <h4>
- <FormattedMessage
- id='signup_team.choose'
- defaultMessage='Choose a Team'
- />
- </h4>
- <div className='signup-team-all'>
- {
- Object.values(this.state.teams).map((team) => {
- if (team.allow_team_listing) {
- return (
- <div
- key={'team_' + team.name}
- className='signup-team-dir'
- >
- <a
- href={'/' + team.name}
- >
- <span className='signup-team-dir__name'>{team.display_name}</span>
- <span
- className='glyphicon glyphicon-menu-right right signup-team-dir__arrow'
- aria-hidden='true'
- />
- </a>
- </div>
- );
- }
- return null;
- })
- }
- </div>
- <h4>
- <FormattedMessage
- id='signup_team.createTeam'
- defaultMessage='Or Create a Team'
- />
- </h4>
- </div>
- );
- }
- }
-
- let signupMethod = null;
-
- if (global.window.mm_config.EnableTeamCreation !== 'true') {
- if (teamListing == null) {
- signupMethod = (
- <FormattedMessage
- id='signup_team.disabled'
- defaultMessage='Team creation has been disabled. Please contact an administrator for access.'
- />
- );
- }
- } else if (this.state.page === 'choose') {
- signupMethod = (
- <ChoosePage
- updatePage={this.updatePage}
- />
- );
- } else if (this.state.page === 'email') {
- signupMethod = (
- <EmailSignUpPage/>
- );
- } else if (this.state.page === 'ldap') {
- return (
- <div>
- {teamListing}
- <LdapSignUpPage/>
- </div>
- );
- } else if (this.state.page === 'gitlab') {
- signupMethod = (
- <SSOSignupPage service={Constants.GITLAB_SERVICE}/>
- );
- } else if (this.state.page === 'google') {
- signupMethod = (
- <SSOSignupPage service={Constants.GOOGLE_SERVICE}/>
- );
- } else if (this.state.page === 'none') {
- signupMethod = (
- <FormattedMessage
- id='signup_team.none'
- defaultMessage='No team creation method has been enabled. Please contact an administrator for access.'
- />
- );
- }
-
- return (
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h1>{global.window.mm_config.SiteName}</h1>
- <h4 className='color--light'>
- <FormattedMessage
- id='web.root.singup_info'
- />
- </h4>
- <div id='signup-team'>
- {teamListing}
- {signupMethod}
- </div>
- </div>
- </div>
- );
- }
-}
-
-TeamSignUp.propTypes = {
-};
-
diff --git a/web/react/components/signup_team_complete/components/signup_team_complete.jsx b/web/react/components/signup_team_complete/components/signup_team_complete.jsx
deleted file mode 100644
index 5ad21e941..000000000
--- a/web/react/components/signup_team_complete/components/signup_team_complete.jsx
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import BrowserStore from '../../../stores/browser_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-import {browserHistory} from 'react-router';
-
-export default class SignupTeamComplete extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateParent = this.updateParent.bind(this);
- }
- componentWillMount() {
- const data = JSON.parse(this.props.location.query.d);
- this.hash = this.props.location.query.h;
-
- var initialState = BrowserStore.getGlobalItem(this.hash);
-
- if (!initialState) {
- initialState = {};
- initialState.wizard = 'welcome';
- initialState.team = {};
- initialState.team.email = data.email;
- initialState.team.allowed_domains = '';
- initialState.invites = [];
- initialState.invites.push('');
- initialState.invites.push('');
- initialState.invites.push('');
- initialState.user = {};
- initialState.hash = this.hash;
- initialState.data = this.props.location.query.d;
- }
-
- this.setState(initialState);
- }
- componentDidMount() {
- browserHistory.push('/signup_team_complete/welcome');
- }
- updateParent(state, skipSet) {
- BrowserStore.setGlobalItem(this.hash, state);
-
- if (!skipSet) {
- this.setState(state);
- browserHistory.push('/signup_team_complete/' + state.wizard);
- }
- }
- render() {
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span classNameName='fa fa-chevron-left'/>
- <FormattedMessage id='web.header.back'/>
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <div id='signup-team-complete'>
- {React.cloneElement(this.props.children, {
- state: this.state,
- updateParent: this.updateParent
- })}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-SignupTeamComplete.defaultProps = {
-};
-SignupTeamComplete.propTypes = {
- location: React.PropTypes.object,
- children: React.PropTypes.node
-};
diff --git a/web/react/components/signup_team_complete/components/team_signup_display_name_page.jsx b/web/react/components/signup_team_complete/components/team_signup_display_name_page.jsx
deleted file mode 100644
index 280e53ce4..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_display_name_page.jsx
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as utils from '../../../utils/utils.jsx';
-import * as client from '../../../utils/client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- required: {
- id: 'team_signup_display_name.required',
- defaultMessage: 'This field is required'
- },
- charLength: {
- id: 'team_signup_display_name.charLength',
- defaultMessage: 'Name must be 4 or more characters up to a maximum of 15'
- }
-});
-
-class TeamSignupDisplayNamePage extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitBack = this.submitBack.bind(this);
- this.submitNext = this.submitNext.bind(this);
-
- this.state = {};
- }
- submitBack(e) {
- e.preventDefault();
- this.props.state.wizard = 'welcome';
- this.props.updateParent(this.props.state);
- }
- submitNext(e) {
- e.preventDefault();
-
- const {formatMessage} = this.props.intl;
- var displayName = ReactDOM.findDOMNode(this.refs.name).value.trim();
- if (!displayName) {
- this.setState({nameError: formatMessage(holders.required)});
- return;
- } else if (displayName.length < 4 || displayName.length > 15) {
- this.setState({nameError: formatMessage(holders.charLength)});
- return;
- }
-
- this.props.state.wizard = 'team_url';
- this.props.state.team.display_name = displayName;
- this.props.state.team.name = utils.cleanUpUrlable(displayName);
- this.props.updateParent(this.props.state);
- }
- handleFocus(e) {
- e.preventDefault();
- e.currentTarget.select();
- }
- render() {
- client.track('signup', 'signup_team_02_name');
-
- var nameError = null;
- var nameDivClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameDivClass += ' has-error';
- }
-
- return (
- <div>
- <form>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h2>
- <FormattedMessage
- id='team_signup_display_name.teamName'
- defaultMessage='Team Name'
- />
- </h2>
- <div className={nameDivClass}>
- <div className='row'>
- <div className='col-sm-9'>
- <input
- type='text'
- ref='name'
- className='form-control'
- placeholder=''
- maxLength='128'
- defaultValue={this.props.state.team.display_name}
- autoFocus={true}
- onFocus={this.handleFocus}
- spellCheck='false'
- />
- </div>
- </div>
- {nameError}
- </div>
- <div>
- <FormattedMessage
- id='team_signup_display_name.nameHelp'
- defaultMessage='Name your team in any language. Your team name shows in menus and headings.'
- />
- </div>
- <button
- type='submit'
- className='btn btn-primary margin--extra'
- onClick={this.submitNext}
- >
- <FormattedMessage
- id='team_signup_display_name.next'
- defaultMessage='Next'
- /><i className='glyphicon glyphicon-chevron-right'></i>
- </button>
- <div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
- <FormattedMessage
- id='team_signup_display_name.back'
- defaultMessage='Back to previous step'
- />
- </a>
- </div>
- </form>
- </div>
- );
- }
-}
-
-TeamSignupDisplayNamePage.propTypes = {
- intl: intlShape.isRequired,
- state: React.PropTypes.object,
- updateParent: React.PropTypes.func
-};
-
-export default injectIntl(TeamSignupDisplayNamePage);
diff --git a/web/react/components/signup_team_complete/components/team_signup_email_item.jsx b/web/react/components/signup_team_complete/components/team_signup_email_item.jsx
deleted file mode 100644
index c87d6ec07..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_email_item.jsx
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- validEmail: {
- id: 'team_signup_email.validEmail',
- defaultMessage: 'Please enter a valid email address'
- },
- different: {
- id: 'team_signup_email.different',
- defaultMessage: 'Please use a different email than the one used at signup'
- },
- address: {
- id: 'team_signup_email.address',
- defaultMessage: 'Email Address'
- }
-});
-
-class TeamSignupEmailItem extends React.Component {
- constructor(props) {
- super(props);
-
- this.getValue = this.getValue.bind(this);
- this.validate = this.validate.bind(this);
-
- this.state = {};
- }
- getValue() {
- return ReactDOM.findDOMNode(this.refs.email).value.trim();
- }
- validate(teamEmail) {
- const {formatMessage} = this.props.intl;
- const email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
-
- if (!email) {
- return true;
- }
-
- if (!Utils.isEmail(email)) {
- this.setState({emailError: formatMessage(holders.validEmail)});
- return false;
- } else if (email === teamEmail) {
- this.setState({emailError: formatMessage(holders.different)});
- return false;
- }
-
- this.setState({emailError: ''});
- return true;
- }
- render() {
- let emailError = null;
- let emailDivClass = 'form-group';
- if (this.state.emailError) {
- emailError = <label className='control-label'>{this.state.emailError}</label>;
- emailDivClass += ' has-error';
- }
-
- return (
- <div className={emailDivClass}>
- <input
- autoFocus={this.props.focus}
- type='email'
- ref='email'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.address)}
- defaultValue={this.props.email}
- maxLength='128'
- spellCheck='false'
- />
- {emailError}
- </div>
- );
- }
-}
-
-TeamSignupEmailItem.propTypes = {
- intl: intlShape.isRequired,
- focus: React.PropTypes.bool,
- email: React.PropTypes.string
-};
-
-export default injectIntl(TeamSignupEmailItem, {withRef: true});
diff --git a/web/react/components/signup_team_complete/components/team_signup_finished.jsx b/web/react/components/signup_team_complete/components/team_signup_finished.jsx
deleted file mode 100644
index fc5f756e7..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_finished.jsx
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class FinishedPage extends React.Component {
- render() {
- return (
- <FormattedMessage
- id='signup_team_complete.completed'
- defaultMessage="You've already completed the signup process for this invitation or this invitation has expired."
- />
- );
- }
-}
diff --git a/web/react/components/signup_team_complete/components/team_signup_password_page.jsx b/web/react/components/signup_team_complete/components/team_signup_password_page.jsx
deleted file mode 100644
index 490a11040..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_password_page.jsx
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../../utils/client.jsx';
-import BrowserStore from '../../../stores/browser_store.jsx';
-import UserStore from '../../../stores/user_store.jsx';
-import Constants from '../../../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-import {browserHistory} from 'react-router';
-
-const holders = defineMessages({
- passwordError: {
- id: 'team_signup_password.passwordError',
- defaultMessage: 'Please enter at least {chars} characters'
- },
- creating: {
- id: 'team_signup_password.creating',
- defaultMessage: 'Creating team...'
- }
-});
-
-class TeamSignupPasswordPage extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitBack = this.submitBack.bind(this);
- this.submitNext = this.submitNext.bind(this);
-
- this.state = {};
- }
- submitBack(e) {
- e.preventDefault();
- this.props.state.wizard = 'username';
- this.props.updateParent(this.props.state);
- }
- submitNext(e) {
- e.preventDefault();
-
- var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
- this.setState({passwordError: this.props.intl.formatMessage(holders.passwordError, {chars: Constants.MIN_PASSWORD_LENGTH})});
- return;
- }
-
- this.setState({passwordError: null, serverError: null});
- $('#finish-button').button('loading');
- var teamSignup = JSON.parse(JSON.stringify(this.props.state));
- teamSignup.user.password = password;
- teamSignup.user.allow_marketing = true;
- delete teamSignup.wizard;
-
- Client.createTeamFromSignup(teamSignup,
- () => {
- Client.track('signup', 'signup_team_08_complete');
-
- var props = this.props;
-
- Client.loginByEmail(teamSignup.team.name, teamSignup.team.email, teamSignup.user.password,
- () => {
- UserStore.setLastEmail(teamSignup.team.email);
- if (this.props.hash > 0) {
- BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: 'finished'}));
- }
-
- $('#sign-up-button').button('reset');
- props.state.wizard = 'finished';
- props.updateParent(props.state, true);
-
- browserHistory.push('/' + teamSignup.team.name + '/channels/town-square');
- },
- (err) => {
- if (err.id === 'api.user.login.not_verified.app_error') {
- browserHistory.push('/verify_email?email=' + encodeURIComponent(teamSignup.team.email) + '&teamname=' + encodeURIComponent(teamSignup.team.name));
- } else {
- this.setState({serverError: err.message});
- $('#finish-button').button('reset');
- }
- }
- );
- },
- (err) => {
- this.setState({serverError: err.message});
- $('#finish-button').button('reset');
- }
- );
- }
- render() {
- Client.track('signup', 'signup_team_07_password');
-
- var passwordError = null;
- var passwordDivStyle = 'form-group';
- if (this.state.passwordError) {
- passwordError = <div className='form-group has-error'><label className='control-label'>{this.state.passwordError}</label></div>;
- passwordDivStyle = ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- return (
- <div>
- <form>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h2 className='margin--less'>
- <FormattedMessage
- id='team_signup_password.yourPassword'
- defaultMessage='Your password'
- />
- </h2>
- <h5 className='color--light'>
- <FormattedMessage
- id='team_signup_password.selectPassword'
- defaultMessage="Select a password that you'll use to login with your email address:"
- />
- </h5>
- <div className='inner__content margin--extra'>
- <h5><strong>
- <FormattedMessage
- id='team_signup_password.email'
- defaultMessage='Email'
- />
- </strong></h5>
- <div className='block--gray form-group'>{this.props.state.team.email}</div>
- <div className={passwordDivStyle}>
- <div className='row'>
- <div className='col-sm-11'>
- <h5><strong>
- <FormattedMessage
- id='team_signup_password.choosePwd'
- defaultMessage='Choose your password'
- />
- </strong></h5>
- <input
- autoFocus={true}
- type='password'
- ref='password'
- className='form-control'
- placeholder=''
- maxLength='128'
- spellCheck='false'
- />
- <span className='color--light help-block'>
- <FormattedMessage
- id='team_signup_password.hint'
- defaultMessage='Passwords must contain {min} to {max} characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.'
- values={{
- min: Constants.MIN_PASSWORD_LENGTH,
- max: Constants.MAX_PASSWORD_LENGTH
- }}
- />
- </span>
- </div>
- </div>
- {passwordError}
- {serverError}
- </div>
- </div>
- <div className='form-group'>
- <button
- type='submit'
- className='btn btn-primary margin--extra'
- id='finish-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.creating)}
- onClick={this.submitNext}
- >
- <FormattedMessage
- id='team_signup_password.finish'
- defaultMessage='Finish'
- />
- </button>
- </div>
- <p>
- <FormattedHTMLMessage
- id='team_signup_password.agreement'
- defaultMessage="By proceeding to create your account and use {siteName}, you agree to our <a href='/static/help/terms.html'>Terms of Service</a> and <a href='/static/help/privacy.html'>Privacy Policy</a>. If you do not agree, you cannot use {siteName}."
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </p>
- <div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
- <FormattedMessage
- id='team_signup_password.back'
- defaultMessage='Back to previous step'
- />
- </a>
- </div>
- </form>
- </div>
- );
- }
-}
-
-TeamSignupPasswordPage.defaultProps = {
- state: {},
- hash: ''
-};
-TeamSignupPasswordPage.propTypes = {
- intl: intlShape.isRequired,
- state: React.PropTypes.object,
- hash: React.PropTypes.string,
- updateParent: React.PropTypes.func
-};
-
-export default injectIntl(TeamSignupPasswordPage);
diff --git a/web/react/components/signup_team_complete/components/team_signup_send_invites_page.jsx b/web/react/components/signup_team_complete/components/team_signup_send_invites_page.jsx
deleted file mode 100644
index 5e987ef2c..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_send_invites_page.jsx
+++ /dev/null
@@ -1,210 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import EmailItem from './team_signup_email_item.jsx';
-import * as Client from '../../../utils/client.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-export default class TeamSignupSendInvitesPage extends React.Component {
- constructor(props) {
- super(props);
- this.submitBack = this.submitBack.bind(this);
- this.submitNext = this.submitNext.bind(this);
- this.submitAddInvite = this.submitAddInvite.bind(this);
- this.submitSkip = this.submitSkip.bind(this);
- this.keySubmit = this.keySubmit.bind(this);
- this.state = {
- emailEnabled: global.window.mm_config.SendEmailNotifications === 'true'
- };
- }
- submitBack(e) {
- e.preventDefault();
- this.props.state.wizard = 'team_url';
-
- this.props.updateParent(this.props.state);
- }
- submitNext(e) {
- e.preventDefault();
-
- var valid = true;
-
- if (this.state.emailEnabled) {
- var emails = [];
-
- for (var i = 0; i < this.props.state.invites.length; i++) {
- if (this.refs['email_' + i].getWrappedInstance().validate(this.props.state.team.email)) {
- emails.push(this.refs['email_' + i].getWrappedInstance().getValue());
- } else {
- valid = false;
- }
- }
-
- if (valid) {
- this.props.state.invites = emails;
- }
- }
-
- if (valid) {
- this.props.state.wizard = 'username';
- this.props.updateParent(this.props.state);
- }
- }
- submitAddInvite(e) {
- e.preventDefault();
- this.props.state.wizard = 'send_invites';
- if (!this.props.state.invites) {
- this.props.state.invites = [];
- }
- this.props.state.invites.push('');
- this.props.updateParent(this.props.state);
- }
- submitSkip(e) {
- e.preventDefault();
- this.props.state.wizard = 'username';
- this.props.updateParent(this.props.state);
- }
- keySubmit(e) {
- if (e && e.keyCode === 13) {
- this.submitNext(e);
- }
- }
- componentDidMount() {
- if (!this.state.emailEnabled) {
- // Must use keypress not keyup due to event chain of pressing enter
- $('body').keypress(this.keySubmit);
- }
- }
- componentWillUnmount() {
- if (!this.state.emailEnabled) {
- $('body').off('keypress', this.keySubmit);
- }
- }
- render() {
- Client.track('signup', 'signup_team_05_send_invites');
-
- var content = null;
- var bottomContent = null;
-
- if (this.state.emailEnabled) {
- var emails = [];
-
- for (var i = 0; i < this.props.state.invites.length; i++) {
- if (i === 0) {
- emails.push(
- <EmailItem
- focus={true}
- key={i}
- ref={'email_' + i}
- email={this.props.state.invites[i]}
- />
- );
- } else {
- emails.push(
- <EmailItem
- focus={false}
- key={i}
- ref={'email_' + i}
- email={this.props.state.invites[i]}
- />
- );
- }
- }
-
- content = (
- <div>
- {emails}
- <div className='form-group text-right'>
- <a
- href='#'
- onClick={this.submitAddInvite}
- >
- <FormattedMessage
- id='team_signup_send_invites.addInvitation'
- defaultMessage='Add Invitation'
- />
- </a>
- </div>
- </div>
- );
-
- bottomContent = (
- <p className='color--light'>
- <FormattedHTMLMessage
- id='team_signup_send_invites.prefer'
- defaultMessage='if you prefer, you can invite team members later<br /> and '
- />
- <a
- href='#'
- onClick={this.submitSkip}
- >
- <FormattedMessage
- id='team_signup_send_invites.skip'
- defaultMessage='skip this step '
- />
- </a>
- <FormattedMessage
- id='team_signup_send_invites.forNow'
- defaultMessage='for now.'
- />
- </p>
- );
- } else {
- content = (
- <div className='form-group color--light'>
- <FormattedMessage
- id='team_signup_send_invites.disabled'
- defaultMessage='Email is currently disabled for your team, and emails cannot be sent. Contact your system administrator to enable email and email invitations.'
- />
- </div>
- );
- }
-
- return (
- <div>
- <form>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h2>
- <FormattedMessage
- id='team_signup_send_invites.title'
- defaultMessage='Invite Team Members'
- />
- </h2>
- {content}
- <div className='form-group'>
- <button
- type='submit'
- className='btn-primary btn'
- onClick={this.submitNext}
- >
- <FormattedMessage
- id='team_signup_send_invites.next'
- defaultMessage='Next'
- /><i className='glyphicon glyphicon-chevron-right'/>
- </button>
- </div>
- </form>
- {bottomContent}
- <div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
- <FormattedMessage
- id='team_signup_send_invites.back'
- defaultMessage='Back to previous step'
- />
- </a>
- </div>
- </div>
- );
- }
-}
-
-TeamSignupSendInvitesPage.propTypes = {
- state: React.PropTypes.object.isRequired,
- updateParent: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/signup_team_complete/components/team_signup_url_page.jsx b/web/react/components/signup_team_complete/components/team_signup_url_page.jsx
deleted file mode 100644
index ec50e2d25..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_url_page.jsx
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../../utils/utils.jsx';
-import * as Client from '../../../utils/client.jsx';
-import Constants from '../../../utils/constants.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const holders = defineMessages({
- required: {
- id: 'team_signup_url.required',
- defaultMessage: 'This field is required'
- },
- regex: {
- id: 'team_signup_url.regex',
- defaultMessage: "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash."
- },
- charLength: {
- id: 'team_signup_url.charLength',
- defaultMessage: 'Name must be 4 or more characters up to a maximum of 15'
- },
- taken: {
- id: 'team_signup_url.taken',
- defaultMessage: 'URL is taken or contains a reserved word'
- },
- unavailable: {
- id: 'team_signup_url.unavailable',
- defaultMessage: 'This URL is unavailable. Please try another.'
- }
-});
-
-class TeamSignupUrlPage extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitBack = this.submitBack.bind(this);
- this.submitNext = this.submitNext.bind(this);
- this.handleFocus = this.handleFocus.bind(this);
-
- this.state = {nameError: ''};
- }
- submitBack(e) {
- e.preventDefault();
- this.props.state.wizard = 'team_display_name';
- this.props.updateParent(this.props.state);
- }
- submitNext(e) {
- e.preventDefault();
-
- const {formatMessage} = this.props.intl;
- const name = ReactDOM.findDOMNode(this.refs.name).value.trim();
- if (!name) {
- this.setState({nameError: formatMessage(holders.required)});
- return;
- }
-
- const cleanedName = Utils.cleanUpUrlable(name);
-
- const urlRegex = /^[a-z]+([a-z\-0-9]+|(__)?)[a-z0-9]+$/g;
- if (cleanedName !== name || !urlRegex.test(name)) {
- this.setState({nameError: formatMessage(holders.regex)});
- return;
- } else if (cleanedName.length < 4 || cleanedName.length > 15) {
- this.setState({nameError: formatMessage(holders.charLength)});
- return;
- }
-
- if (global.window.mm_config.RestrictTeamNames === 'true') {
- for (let index = 0; index < Constants.RESERVED_TEAM_NAMES.length; index++) {
- if (cleanedName.indexOf(Constants.RESERVED_TEAM_NAMES[index]) === 0) {
- this.setState({nameError: formatMessage(holders.taken)});
- return;
- }
- }
- }
-
- Client.findTeamByName(name,
- (data) => {
- if (data) {
- this.setState({nameError: formatMessage(holders.unavailable)});
- } else {
- if (global.window.mm_config.SendEmailNotifications === 'true') {
- this.props.state.wizard = 'send_invites';
- } else {
- this.props.state.wizard = 'username';
- }
- this.props.state.team.type = 'O';
-
- this.props.state.team.name = name;
- this.props.updateParent(this.props.state);
- }
- },
- (err) => {
- this.setState({nameError: err.message});
- }
- );
- }
- handleFocus(e) {
- e.preventDefault();
-
- e.currentTarget.select();
- }
- render() {
- $('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'});
-
- Client.track('signup', 'signup_team_03_url');
-
- let nameError = null;
- let nameDivClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameDivClass += ' has-error';
- }
-
- const title = `${Utils.getWindowLocationOrigin()}/`;
-
- return (
- <div>
- <form>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h2>
- <FormattedMessage
- id='team_signup_url.teamUrl'
- defaultMessage='Team URL'
- />
- </h2>
- <div className={nameDivClass}>
- <div className='row'>
- <div className='col-sm-11'>
- <div className='input-group input-group--limit'>
- <span
- data-toggle='tooltip'
- title={title}
- className='input-group-addon'
- >
- {title}
- </span>
- <input
- type='text'
- ref='name'
- className='form-control'
- placeholder=''
- maxLength='128'
- defaultValue={this.props.state.team.name}
- autoFocus={true}
- onFocus={this.handleFocus}
- spellCheck='false'
- />
- </div>
- </div>
- </div>
- {nameError}
- </div>
- <p>
- <FormattedMessage
- id='team_signup_url.webAddress'
- defaultMessage='Choose the web address of your new team:'
- />
- </p>
- <ul className='color--light'>
- <FormattedHTMLMessage
- id='team_signup_url.hint'
- defaultMessage="<li>Short and memorable is best</li>
- <li>Use lowercase letters, numbers and dashes</li>
- <li>Must start with a letter and can't end in a dash</li>"
- />
- </ul>
- <button
- type='submit'
- className='btn btn-primary margin--extra'
- onClick={this.submitNext}
- >
- <FormattedMessage
- id='team_signup_url.next'
- defaultMessage='Next'
- /><i className='glyphicon glyphicon-chevron-right'></i>
- </button>
- <div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
- <FormattedMessage
- id='team_signup_url.back'
- defaultMessage='Back to previous step'
- />
- </a>
- </div>
- </form>
- </div>
- );
- }
-}
-
-TeamSignupUrlPage.propTypes = {
- intl: intlShape.isRequired,
- state: React.PropTypes.object,
- updateParent: React.PropTypes.func
-};
-
-export default injectIntl(TeamSignupUrlPage);
diff --git a/web/react/components/signup_team_complete/components/team_signup_username_page.jsx b/web/react/components/signup_team_complete/components/team_signup_username_page.jsx
deleted file mode 100644
index e56aa4cd7..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_username_page.jsx
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../../utils/utils.jsx';
-import * as Client from '../../../utils/client.jsx';
-import Constants from '../../../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- reserved: {
- id: 'team_signup_username.reserved',
- defaultMessage: 'This username is reserved, please choose a new one.'
- },
- invalid: {
- id: 'team_signup_username.invalid',
- defaultMessage: 'Username must begin with a letter, and contain between {min} to {max} characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''
- }
-});
-
-class TeamSignupUsernamePage extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitBack = this.submitBack.bind(this);
- this.submitNext = this.submitNext.bind(this);
-
- this.state = {};
- }
- submitBack(e) {
- e.preventDefault();
- if (global.window.mm_config.SendEmailNotifications === 'true') {
- this.props.state.wizard = 'send_invites';
- } else {
- this.props.state.wizard = 'team_url';
- }
-
- this.props.updateParent(this.props.state);
- }
- submitNext(e) {
- e.preventDefault();
-
- const {formatMessage} = this.props.intl;
- var name = ReactDOM.findDOMNode(this.refs.name).value.trim().toLowerCase();
-
- var usernameError = Utils.isValidUsername(name);
- if (usernameError === 'Cannot use a reserved word as a username.') { //this should be change to some kind of ID
- this.setState({nameError: formatMessage(holders.reserved)});
- return;
- } else if (usernameError) {
- this.setState({nameError: formatMessage(holders.invalid, {min: Constants.MIN_USERNAME_LENGTH, max: Constants.MAX_USERNAME_LENGTH})});
- return;
- }
-
- this.props.state.wizard = 'password';
- this.props.state.user.username = name;
- this.props.updateParent(this.props.state);
- }
- render() {
- Client.track('signup', 'signup_team_06_username');
-
- var nameError = null;
- var nameHelpText = (
- <span className='color--light help-block'>
- <FormattedMessage
- id='team_signup_username.hint'
- defaultMessage="Usernames must begin with a letter and contain between {min} to {max} characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'"
- values={{
- min: Constants.MIN_USERNAME_LENGTH,
- max: Constants.MAX_USERNAME_LENGTH
- }}
- />
- </span>
- );
- var nameDivClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameHelpText = '';
- nameDivClass += ' has-error';
- }
-
- return (
- <div>
- <form>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h2 className='margin--less'>
- <FormattedMessage
- id='team_signup_username.username'
- defaultMessage='Your username'
- />
- </h2>
- <h5 className='color--light'>
- <FormattedMessage
- id='team_signup_username.memorable'
- defaultMessage='Select a memorable username that makes it easy for teammates to identify you:'
- />
- </h5>
- <div className='inner__content margin--extra'>
- <div className={nameDivClass}>
- <div className='row'>
- <div className='col-sm-11'>
- <h5><strong>
- <FormattedMessage
- id='team_signup_username.chooseUsername'
- defaultMessage='Choose your username'
- />
- </strong></h5>
- <input
- autoFocus={true}
- type='text'
- ref='name'
- className='form-control'
- placeholder=''
- defaultValue={this.props.state.user.username}
- maxLength={Constants.MAX_USERNAME_LENGTH}
- spellCheck='false'
- />
- {nameHelpText}
- </div>
- </div>
- {nameError}
- </div>
- </div>
- <button
- type='submit'
- className='btn btn-primary margin--extra'
- onClick={this.submitNext}
- >
- <FormattedMessage
- id='team_signup_username.next'
- defaultMessage='Next'
- />
- <i className='glyphicon glyphicon-chevron-right'></i>
- </button>
- <div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
- <FormattedMessage
- id='team_signup_username.back'
- defaultMessage='Back to previous step'
- />
- </a>
- </div>
- </form>
- </div>
- );
- }
-}
-
-TeamSignupUsernamePage.defaultProps = {
- state: null
-};
-TeamSignupUsernamePage.propTypes = {
- intl: intlShape.isRequired,
- state: React.PropTypes.object,
- updateParent: React.PropTypes.func
-};
-
-export default injectIntl(TeamSignupUsernamePage);
diff --git a/web/react/components/signup_team_complete/components/team_signup_welcome_page.jsx b/web/react/components/signup_team_complete/components/team_signup_welcome_page.jsx
deleted file mode 100644
index 97782e54a..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_welcome_page.jsx
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../../utils/utils.jsx';
-import * as Client from '../../../utils/client.jsx';
-import BrowserStore from '../../../stores/browser_store.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-import {browserHistory} from 'react-router';
-
-const holders = defineMessages({
- storageError: {
- id: 'team_signup_welcome.storageError',
- defaultMessage: 'This service requires local storage to be enabled. Please enable it or exit private browsing.'
- },
- validEmailError: {
- id: 'team_signup_welcome.validEmailError',
- defaultMessage: 'Please enter a valid email address'
- },
- address: {
- id: 'team_signup_welcome.address',
- defaultMessage: 'Email Address'
- }
-});
-
-class TeamSignupWelcomePage extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitNext = this.submitNext.bind(this);
- this.handleDiffEmail = this.handleDiffEmail.bind(this);
- this.handleDiffSubmit = this.handleDiffSubmit.bind(this);
- this.handleKeyPress = this.handleKeyPress.bind(this);
-
- this.state = {useDiff: false};
-
- document.addEventListener('keyup', this.handleKeyPress, false);
- }
- submitNext(e) {
- if (!BrowserStore.isLocalStorageSupported()) {
- this.setState({storageError: this.props.intl.formatMessage(holders.storageError)});
- return;
- }
- e.preventDefault();
- this.props.state.wizard = 'team_display_name';
- this.props.updateParent(this.props.state);
- }
- handleDiffEmail(e) {
- e.preventDefault();
- this.setState({useDiff: true});
- }
- handleDiffSubmit(e) {
- e.preventDefault();
-
- const {formatMessage} = this.props.intl;
- var state = {useDiff: true, serverError: ''};
-
- var email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
- if (!email || !Utils.isEmail(email)) {
- state.emailError = formatMessage(holders.validEmailError);
- this.setState(state);
- return;
- } else if (!BrowserStore.isLocalStorageSupported()) {
- state.emailError = formatMessage(holders.storageError);
- this.setState(state);
- return;
- }
- state.emailError = '';
-
- Client.signupTeam(email,
- function success(data) {
- if (data.follow_link) {
- window.location.href = data.follow_link;
- } else {
- this.props.state.wizard = 'finished';
- this.props.updateParent(this.props.state);
- browserHistory.push('/signup_team_confirm/?email=' + encodeURIComponent(email));
- }
- }.bind(this),
- function error(err) {
- let errorMsg = err.message;
-
- if (err.detailed_error.indexOf('Invalid RCPT TO address provided') >= 0) {
- errorMsg = formatMessage(holders.validEmailError);
- }
-
- this.setState({emailError: '', serverError: errorMsg});
- }.bind(this)
- );
- }
- handleKeyPress(event) {
- if (event.keyCode === 13) {
- this.submitNext(event);
- }
- }
- componentWillUnmount() {
- document.removeEventListener('keyup', this.handleKeyPress, false);
- }
- render() {
- Client.track('signup', 'signup_team_01_welcome');
-
- var storageError = null;
- if (this.state.storageError) {
- storageError = <label className='control-label'>{this.state.storageError}</label>;
- }
-
- var emailError = null;
- var emailDivClass = 'form-group';
- if (this.state.emailError) {
- emailError = <label className='control-label'>{this.state.emailError}</label>;
- emailDivClass += ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='form-group has-error'>
- <label className='control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- var differentEmailLinkClass = '';
- var emailDivContainerClass = 'hidden';
- if (this.state.useDiff) {
- differentEmailLinkClass = 'hidden';
- emailDivContainerClass = '';
- }
-
- return (
- <div>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h3 className='sub-heading'>
- <FormattedMessage
- id='team_signup_welcome.welcome'
- defaultMessage='Welcome to:'
- />
- </h3>
- <h1 className='margin--top-none'>{global.window.mm_config.SiteName}</h1>
- <p className='margin--less'>
- <FormattedMessage
- id='team_signup_welcome.lets'
- defaultMessage="Let's set up your new team"
- />
- </p>
- <div>
- <FormattedMessage
- id='team_signup_welcome.confirm'
- defaultMessage='Please confirm your email address:'
- />
- <br/>
- <div className='inner__content'>
- <div className='block--gray'>{this.props.state.team.email}</div>
- </div>
- </div>
- <p className='margin--extra color--light'>
- <FormattedHTMLMessage
- id='team_signup_welcome.admin'
- defaultMessage='Your account will administer the new team site. <br />
- You can add other administrators later.'
- />
- </p>
- <div className='form-group'>
- <button
- className='btn-primary btn form-group'
- type='submit'
- onClick={this.submitNext}
- >
- <i className='glyphicon glyphicon-ok'></i>
- <FormattedMessage
- id='team_signup_welcome.yes'
- defaultMessage='Yes, this address is correct'
- />
- </button>
- {storageError}
- </div>
- <hr/>
- <div className={emailDivContainerClass}>
- <div className={emailDivClass}>
- <div className='row'>
- <div className='col-sm-9'>
- <input
- type='email'
- ref='email'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.address)}
- maxLength='128'
- spellCheck='false'
- />
- </div>
- </div>
- {emailError}
- </div>
- {serverError}
- <button
- className='btn btn-md btn-primary'
- type='button'
- onClick={this.handleDiffSubmit}
- >
- <FormattedMessage
- id='team_signup_welcome.instead'
- defaultMessage='Use this instead'
- />
- </button>
- </div>
- <a
- href='#'
- onClick={this.handleDiffEmail}
- className={differentEmailLinkClass}
- >
- <FormattedMessage
- id='team_signup_welcome.different'
- defaultMessage='Use a different email'
- />
- </a>
- </div>
- );
- }
-}
-
-TeamSignupWelcomePage.defaultProps = {
- state: {}
-};
-TeamSignupWelcomePage.propTypes = {
- intl: intlShape.isRequired,
- updateParent: React.PropTypes.func.isRequired,
- state: React.PropTypes.object
-};
-
-export default injectIntl(TeamSignupWelcomePage);
diff --git a/web/react/components/signup_team_confirm.jsx b/web/react/components/signup_team_confirm.jsx
deleted file mode 100644
index 1afbb3d30..000000000
--- a/web/react/components/signup_team_confirm.jsx
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-export default class SignupTeamConfirm extends React.Component {
- render() {
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </a>
- </div>
- <div className='col-sm-12'>
- <div classNameName='signup-team__container'>
- <h3>
- <FormattedMessage
- id='signup_team_confirm.title'
- defaultMessage='Sign up Complete'
- />
- </h3>
- <p>
- <FormattedHTMLMessage
- id='signup_team_confirm.checkEmail'
- defaultMessage='Please check your email: <strong>{email}</strong><br />Your email contains a link to set up your team'
- values={{
- email: this.props.location.query.email
- }}
- />
- </p>
- </div>
- </div>
- </div>
- );
- }
-}
-
-SignupTeamConfirm.defaultProps = {
-};
-SignupTeamConfirm.propTypes = {
- location: React.PropTypes.object
-};
diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx
deleted file mode 100644
index d2128a50f..000000000
--- a/web/react/components/signup_user_complete.jsx
+++ /dev/null
@@ -1,491 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import UserStore from '../stores/user_store.jsx';
-import BrowserStore from '../stores/browser_store.jsx';
-import Constants from '../utils/constants.jsx';
-import LoadingScreen from '../components/loading_screen.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-import {browserHistory} from 'react-router';
-
-class SignupUserComplete extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.inviteInfoRecieved = this.inviteInfoRecieved.bind(this);
-
- this.state = {
- data: '',
- hash: '',
- usedBefore: false,
- email: '',
- teamDisplayName: '',
- teamName: '',
- teamId: ''
- };
- }
- componentWillMount() {
- let data = this.props.location.query.d;
- let hash = this.props.location.query.h;
- const inviteId = this.props.location.query.id;
- let usedBefore = false;
- let email = '';
- let teamDisplayName = '';
- let teamName = '';
- let teamId = '';
-
- // If we have a hash in the url then we are attempting to access a private team
- if (hash) {
- const parsedData = JSON.parse(data);
- usedBefore = BrowserStore.getGlobalItem(hash);
- email = parsedData.email;
- teamDisplayName = parsedData.display_name;
- teamName = parsedData.name;
- teamId = parsedData.id;
- } else {
- Client.getInviteInfo(this.inviteInfoRecieved, null, inviteId);
- data = '';
- hash = '';
- }
-
- this.setState({
- data,
- hash,
- usedBefore,
- email,
- teamDisplayName,
- teamName,
- teamId
- });
- }
- inviteInfoRecieved(data) {
- if (!data) {
- return;
- }
-
- this.setState({
- teamDisplayName: data.display_name,
- teamName: data.name,
- teamId: data.id
- });
- }
- handleSubmit(e) {
- e.preventDefault();
-
- const providedEmail = ReactDOM.findDOMNode(this.refs.email).value.trim();
- if (!providedEmail) {
- this.setState({
- nameError: '',
- emailError: (<FormattedMessage id='signup_user_completed.required'/>),
- passwordError: '',
- serverError: ''
- });
- return;
- }
-
- if (!Utils.isEmail(providedEmail)) {
- this.setState({
- nameError: '',
- emailError: (<FormattedMessage id='signup_user_completed.validEmail'/>),
- passwordError: '',
- serverError: ''
- });
- return;
- }
-
- const providedUsername = ReactDOM.findDOMNode(this.refs.name).value.trim().toLowerCase();
- if (!providedUsername) {
- this.setState({
- nameError: (<FormattedMessage id='signup_user_completed.required'/>),
- emailError: '',
- passwordError: '',
- serverError: ''
- });
- return;
- }
-
- const usernameError = Utils.isValidUsername(providedUsername);
- if (usernameError === 'Cannot use a reserved word as a username.') {
- this.setState({
- nameError: (<FormattedMessage id='signup_user_completed.reserved'/>),
- emailError: '',
- passwordError: '',
- serverError: ''
- });
- return;
- } else if (usernameError) {
- this.setState({
- nameError: (
- <FormattedMessage
- id='signup_user_completed.usernameLength'
- min={Constants.MIN_USERNAME_LENGTH}
- max={Constants.MAX_USERNAME_LENGTH}
- />
- ),
- emailError: '',
- passwordError: '',
- serverError: ''
- });
- return;
- }
-
- const providedPassword = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!providedPassword || providedPassword.length < Constants.MIN_PASSWORD_LENGTH) {
- this.setState({
- nameError: '',
- emailError: '',
- passwordError: (
- <FormattedMessage
- id='signup_user_completed.passwordLength'
- min={Constants.MIN_PASSWORD_LENGTH}
- />
- ),
- serverError: ''
- });
- return;
- }
-
- this.setState({
- nameError: '',
- emailError: '',
- passwordError: '',
- serverError: ''
- });
-
- const user = {
- team_id: this.state.teamId,
- email: providedEmail,
- username: providedUsername,
- password: providedPassword,
- allow_marketing: true
- };
-
- Client.createUser(user, this.state.data, this.state.hash,
- () => {
- Client.track('signup', 'signup_user_02_complete');
-
- Client.loginByEmail(this.state.teamName, user.email, user.password,
- () => {
- UserStore.setLastEmail(user.email);
- if (this.state.hash > 0) {
- BrowserStore.setGlobalItem(this.state.hash, JSON.stringify({usedBefore: true}));
- }
- browserHistory.push('/' + this.state.teamName + '/channels/town-square');
- },
- (err) => {
- if (err.id === 'api.user.login.not_verified.app_error') {
- browserHistory.push('/should_verify_email?email=' + encodeURIComponent(user.email) + '&teamname=' + encodeURIComponent(this.state.teamName));
- } else {
- this.setState({serverError: err.message});
- }
- }
- );
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- render() {
- Client.track('signup', 'signup_user_01_welcome');
-
- // If we have been used then just display a message
- if (this.state.usedBefore) {
- return (
- <div>
- <FormattedMessage
- id='signup_user_completed.expired'
- defaultMessage="You've already completed the signup process for this invitation or this invitation has expired."
- />
- </div>
- );
- }
-
- // If we haven't got a team id yet we are waiting for
- // the client so just show the standard loading screen
- if (this.state.teamId === '') {
- return (<LoadingScreen/>);
- }
-
- // set up error labels
- var emailError = null;
- var emailHelpText = (
- <span className='help-block'>
- <FormattedMessage
- id='signup_user_completed.emailHelp'
- defaultMessage='Valid email required for sign-up'
- />
- </span>
- );
- var emailDivStyle = 'form-group';
- if (this.state.emailError) {
- emailError = (<label className='control-label'>{this.state.emailError}</label>);
- emailHelpText = '';
- emailDivStyle += ' has-error';
- }
-
- var nameError = null;
- var nameHelpText = (
- <span className='help-block'>
- <FormattedMessage
- id='signup_user_completed.userHelp'
- defaultMessage="Username must begin with a letter, and contain between {min} to {max} lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'"
- values={{
- min: Constants.MIN_USERNAME_LENGTH,
- max: Constants.MAX_USERNAME_LENGTH
- }}
- />
- </span>
- );
- var nameDivStyle = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameHelpText = '';
- nameDivStyle += ' has-error';
- }
-
- var passwordError = null;
- var passwordDivStyle = 'form-group';
- if (this.state.passwordError) {
- passwordError = <label className='control-label'>{this.state.passwordError}</label>;
- passwordDivStyle += ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className={'form-group has-error'}>
- <label className='control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- // set up the email entry and hide it if an email was provided
- var yourEmailIs = '';
- if (this.state.email) {
- yourEmailIs = (
- <FormattedHTMLMessage
- id='signup_user_completed.emailIs'
- defaultMessage="Your email address is <strong>{email}</strong>. You'll use this address to sign in to {siteName}."
- values={{
- email: this.state.email,
- siteName: global.window.mm_config.SiteName
- }}
- />
- );
- }
-
- var emailContainerStyle = 'margin--extra';
- if (this.state.email) {
- emailContainerStyle = 'hidden';
- }
-
- var email = (
- <div className={emailContainerStyle}>
- <h5><strong>
- <FormattedMessage
- id='signup_user_completed.whatis'
- defaultMessage="What's your email address?"
- />
- </strong></h5>
- <div className={emailDivStyle}>
- <input
- type='email'
- ref='email'
- className='form-control'
- defaultValue={this.state.email}
- placeholder=''
- maxLength='128'
- autoFocus={true}
- spellCheck='false'
- />
- {emailError}
- {emailHelpText}
- </div>
- </div>
- );
-
- var signupMessage = [];
- if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
- signupMessage.push(
- <a
- className='btn btn-custom-login gitlab'
- key='gitlab'
- href={'/api/v1/oauth/gitlab/signup' + window.location.search + '&team=' + encodeURIComponent(this.state.teamName)}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='signup_user_completed.gitlab'
- defaultMessage='with GitLab'
- />
- </span>
- </a>
- );
- }
-
- if (global.window.mm_config.EnableSignUpWithGoogle === 'true') {
- signupMessage.push(
- <a
- className='btn btn-custom-login google'
- key='google'
- href={'/api/v1/oauth/google/signup' + window.location.search + '&team=' + encodeURIComponent(this.state.teamName)}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='signup_user_completed.google'
- defaultMessage='with Google'
- />
- </span>
- </a>
- );
- }
-
- var emailSignup;
- if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
- emailSignup = (
- <div>
- <div className='inner__content'>
- {email}
- {yourEmailIs}
- <div className='margin--extra'>
- <h5><strong>
- <FormattedMessage
- id='signup_user_completed.chooseUser'
- defaultMessage='Choose your username'
- />
- </strong></h5>
- <div className={nameDivStyle}>
- <input
- type='text'
- ref='name'
- className='form-control'
- placeholder=''
- maxLength={Constants.MAX_USERNAME_LENGTH}
- spellCheck='false'
- />
- {nameError}
- {nameHelpText}
- </div>
- </div>
- <div className='margin--extra'>
- <h5><strong>
- <FormattedMessage
- id='signup_user_completed.choosePwd'
- defaultMessage='Choose your password'
- />
- </strong></h5>
- <div className={passwordDivStyle}>
- <input
- type='password'
- ref='password'
- className='form-control'
- placeholder=''
- maxLength='128'
- spellCheck='false'
- />
- {passwordError}
- </div>
- </div>
- </div>
- <p className='margin--extra'>
- <button
- type='submit'
- onClick={this.handleSubmit}
- className='btn-primary btn'
- >
- <FormattedMessage
- id='signup_user_completed.create'
- defaultMessage='Create Account'
- />
- </button>
- </p>
- </div>
- );
- }
-
- if (signupMessage.length > 0 && emailSignup) {
- signupMessage = (
- <div>
- {signupMessage}
- <div className='or__container'>
- <FormattedMessage
- id='signup_user_completed.or'
- defaultMessage='or'
- />
- </div>
- </div>
- );
- }
-
- if (signupMessage.length === 0 && !emailSignup) {
- emailSignup = (
- <div>
- <FormattedMessage
- id='signup_user_completed.none'
- defaultMessage='No user creation method has been enabled. Please contact an administrator for access.'
- />
- </div>
- );
- }
-
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span classNameNameName='fa fa-chevron-left'/>
- <FormattedMessage id='web.header.back'/>
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container padding--less'>
- <form>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h5 className='margin--less'>
- <FormattedMessage
- id='signup_user_completed.welcome'
- defaultMessage='Welcome to:'
- />
- </h5>
- <h2 className='signup-team__name'>{this.state.teamName}</h2>
- <h2 className='signup-team__subdomain'>
- <FormattedMessage
- id='signup_user_completed.onSite'
- defaultMessage='on {siteName}'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </h2>
- <h4 className='color--light'>
- <FormattedMessage
- id='signup_user_completed.lets'
- defaultMessage="Let's create your account"
- />
- </h4>
- {signupMessage}
- {emailSignup}
- {serverError}
- </form>
- </div>
- </div>
- </div>
- );
- }
-}
-
-SignupUserComplete.defaultProps = {
-};
-SignupUserComplete.propTypes = {
- location: React.PropTypes.object
-};
-
-export default SignupUserComplete;
diff --git a/web/react/components/suggestion/at_mention_provider.jsx b/web/react/components/suggestion/at_mention_provider.jsx
deleted file mode 100644
index b53b351d9..000000000
--- a/web/react/components/suggestion/at_mention_provider.jsx
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-import UserStore from '../../stores/user_store.jsx';
-import * as Utils from '../../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const MaxUserSuggestions = 40;
-
-class AtMentionSuggestion extends React.Component {
- render() {
- const {item, isSelection, onClick} = this.props;
-
- let username;
- let description;
- let icon;
- if (item.username === 'all') {
- username = 'all';
- description = (
- <FormattedMessage
- id='suggestion.mention.all'
- defaultMessage='Notifies everyone in the team'
- />
- );
- icon = <i className='mention__image fa fa-users fa-2x'/>;
- } else if (item.username === 'channel') {
- username = 'channel';
- description = (
- <FormattedMessage
- id='suggestion.mention.channel'
- defaultMessage='Notifies everyone in the channel'
- />
- );
- icon = <i className='mention__image fa fa-users fa-2x'/>;
- } else {
- username = item.username;
- description = Utils.getFullName(item);
- icon = (
- <img
- className='mention__image'
- src={'/api/v1/users/' + item.id + '/image?time=' + item.update_at}
- />
- );
- }
-
- let className = 'mentions__name';
- if (isSelection) {
- className += ' suggestion--selected';
- }
-
- return (
- <div
- className={className}
- onClick={onClick}
- >
- <div className='pull-left'>
- {icon}
- </div>
- <div className='pull-left mention--align'>
- <span>
- {'@' + username}
- </span>
- <span className='mention__fullname'>
- {description}
- </span>
- </div>
- </div>
- );
- }
-}
-
-AtMentionSuggestion.propTypes = {
- item: React.PropTypes.object.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class AtMentionProvider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/@([a-z0-9\-\._]*)$/i).exec(pretext);
- if (captured) {
- const usernamePrefix = captured[1];
-
- const users = UserStore.getActiveOnlyProfiles(true);
- let filtered = [];
-
- for (const id of Object.keys(users)) {
- const user = users[id];
-
- if (user.username.startsWith(usernamePrefix) && user.delete_at <= 0) {
- filtered.push(user);
- }
-
- if (filtered.length >= MaxUserSuggestions) {
- break;
- }
- }
-
- // add dummy users to represent the @all and @channel special mentions
- if ('all'.startsWith(usernamePrefix)) {
- filtered.push({username: 'all'});
- }
-
- if ('channel'.startsWith(usernamePrefix)) {
- filtered.push({username: 'channel'});
- }
-
- filtered = filtered.sort((a, b) => a.username.localeCompare(b.username));
-
- const mentions = filtered.map((user) => '@' + user.username);
-
- SuggestionStore.setMatchedPretext(suggestionId, captured[0]);
- SuggestionStore.addSuggestions(suggestionId, mentions, filtered, AtMentionSuggestion);
- }
- }
-}
diff --git a/web/react/components/suggestion/command_provider.jsx b/web/react/components/suggestion/command_provider.jsx
deleted file mode 100644
index fffb2df07..000000000
--- a/web/react/components/suggestion/command_provider.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-class CommandSuggestion extends React.Component {
- render() {
- const {item, isSelection, onClick} = this.props;
-
- let className = 'command';
- if (isSelection) {
- className += ' suggestion--selected';
- }
-
- return (
- <div
- className={className}
- onClick={onClick}
- >
- <div className='command__title'>
- <string>{item.suggestion} {item.hint}</string>
- </div>
- <div className='command__desc'>
- {item.description}
- </div>
- </div>
- );
- }
-}
-
-CommandSuggestion.propTypes = {
- item: React.PropTypes.object.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class CommandProvider {
- handlePretextChanged(suggestionId, pretext) {
- if (pretext.startsWith('/')) {
- AsyncClient.getSuggestedCommands(pretext, suggestionId, CommandSuggestion);
- }
- }
-}
diff --git a/web/react/components/suggestion/emoticon_provider.jsx b/web/react/components/suggestion/emoticon_provider.jsx
deleted file mode 100644
index fd470cf21..000000000
--- a/web/react/components/suggestion/emoticon_provider.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-import * as Emoticons from '../../utils/emoticons.jsx';
-
-const MAX_EMOTICON_SUGGESTIONS = 40;
-
-class EmoticonSuggestion extends React.Component {
- render() {
- const text = this.props.term;
- const name = this.props.item;
-
- let className = 'emoticon-suggestion';
- if (this.props.isSelection) {
- className += ' suggestion--selected';
- }
-
- return (
- <div
- className={className}
- onClick={this.props.onClick}
- >
- <div className='pull-left'>
- <img
- alt={text}
- className='emoticon-suggestion__image'
- src={Emoticons.getImagePathForEmoticon(name)}
- title={text}
- />
- </div>
- <div className='pull-left'>
- {text}
- </div>
- </div>
- );
- }
-}
-
-EmoticonSuggestion.propTypes = {
- item: React.PropTypes.string.isRequired,
- term: React.PropTypes.string.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class EmoticonProvider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/(?:^|\s)(:([a-zA-Z0-9_+\-]*))$/g).exec(pretext);
- if (captured) {
- const text = captured[1];
- const partialName = captured[2];
-
- const names = [];
-
- for (const emoticon of Emoticons.emoticonMap.keys()) {
- if (emoticon.indexOf(partialName) !== -1) {
- names.push(emoticon);
-
- if (names.length >= MAX_EMOTICON_SUGGESTIONS) {
- break;
- }
- }
- }
-
- // sort the emoticons so that emoticons starting with the entered text come first
- names.sort((a, b) => {
- const aPrefix = a.startsWith(partialName);
- const bPrefix = b.startsWith(partialName);
-
- if (aPrefix === bPrefix) {
- return a.localeCompare(b);
- } else if (aPrefix) {
- return -1;
- }
-
- return 1;
- });
-
- const terms = names.map((name) => ':' + name + ':');
-
- if (terms.length > 0) {
- SuggestionStore.setMatchedPretext(suggestionId, text);
- SuggestionStore.addSuggestions(suggestionId, terms, names, EmoticonSuggestion);
-
- // force the selection to be cleared since the order of elements may have changed
- SuggestionStore.clearSelection(suggestionId);
- }
- }
- }
-}
diff --git a/web/react/components/suggestion/search_channel_provider.jsx b/web/react/components/suggestion/search_channel_provider.jsx
deleted file mode 100644
index 66a534907..000000000
--- a/web/react/components/suggestion/search_channel_provider.jsx
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChannelStore from '../../stores/channel_store.jsx';
-import Constants from '../../utils/constants.jsx';
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-
-class SearchChannelSuggestion extends React.Component {
- render() {
- const {item, isSelection, onClick} = this.props;
-
- let className = 'search-autocomplete__item';
- if (isSelection) {
- className += ' selected';
- }
-
- return (
- <div
- onClick={onClick}
- className={className}
- >
- <i className='fa fa fa-plus-square'></i>{item.name}
- </div>
- );
- }
-}
-
-SearchChannelSuggestion.propTypes = {
- item: React.PropTypes.object.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class SearchChannelProvider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/\b(?:in|channel):\s*(\S*)$/i).exec(pretext);
- if (captured) {
- const channelPrefix = captured[1];
-
- const channels = ChannelStore.getAll();
- const publicChannels = [];
- const privateChannels = [];
-
- for (const id of Object.keys(channels)) {
- const channel = channels[id];
-
- // don't show direct channels
- if (channel.type !== Constants.DM_CHANNEL && channel.name.startsWith(channelPrefix)) {
- if (channel.type === Constants.OPEN_CHANNEL) {
- publicChannels.push(channel);
- } else {
- privateChannels.push(channel);
- }
- }
- }
-
- publicChannels.sort((a, b) => a.name.localeCompare(b.name));
- const publicChannelNames = publicChannels.map((channel) => channel.name);
-
- privateChannels.sort((a, b) => a.name.localeCompare(b.name));
- const privateChannelNames = privateChannels.map((channel) => channel.name);
-
- SuggestionStore.setMatchedPretext(suggestionId, channelPrefix);
-
- SuggestionStore.addSuggestions(suggestionId, publicChannelNames, publicChannels, SearchChannelSuggestion);
- SuggestionStore.addSuggestions(suggestionId, privateChannelNames, privateChannels, SearchChannelSuggestion);
- }
- }
-}
diff --git a/web/react/components/suggestion/search_suggestion_list.jsx b/web/react/components/suggestion/search_suggestion_list.jsx
deleted file mode 100644
index 60a5562fa..000000000
--- a/web/react/components/suggestion/search_suggestion_list.jsx
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../../utils/constants.jsx';
-import SuggestionList from './suggestion_list.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class SearchSuggestionList extends SuggestionList {
- componentDidUpdate(prevProps, prevState) {
- if (this.state.items.length > 0 && prevState.items.length === 0) {
- this.getContent().perfectScrollbar();
- }
- }
-
- getContent() {
- return $(ReactDOM.findDOMNode(this.refs.popover)).find('.popover-content');
- }
-
- renderChannelDivider(type) {
- let text;
- if (type === Constants.OPEN_CHANNEL) {
- text = (
- <FormattedMessage
- id='suggestion.search.public'
- defaultMessage='Public Channels'
- />
- );
- } else {
- text = (
- <FormattedMessage
- id='suggestion.search.private'
- defaultMessage='Private Groups'
- />
- );
- }
-
- return (
- <div
- key={type + '-divider'}
- className='search-autocomplete__divider'
- >
- <span>{text}</span>
- </div>
- );
- }
-
- render() {
- if (this.state.items.length === 0) {
- return null;
- }
-
- const items = [];
- for (let i = 0; i < this.state.items.length; i++) {
- const item = this.state.items[i];
- const term = this.state.terms[i];
- const isSelection = term === this.state.selection;
-
- // ReactComponent names need to be upper case when used in JSX
- const Component = this.state.components[i];
-
- // temporary hack to add dividers between public and private channels in the search suggestion list
- if (i === 0 || item.type !== this.state.items[i - 1].type) {
- if (item.type === Constants.OPEN_CHANNEL) {
- items.push(this.renderChannelDivider(Constants.OPEN_CHANNEL));
- } else if (item.type === Constants.PRIVATE_CHANNEL) {
- items.push(this.renderChannelDivider(Constants.PRIVATE_CHANNEL));
- }
- }
-
- items.push(
- <Component
- key={term}
- ref={term}
- item={item}
- isSelection={isSelection}
- onClick={this.handleItemClick.bind(this, term)}
- />
- );
- }
-
- return (
- <ReactBootstrap.Popover
- ref='popover'
- id='search-autocomplete__popover'
- className='search-help-popover autocomplete visible'
- placement='bottom'
- >
- {items}
- </ReactBootstrap.Popover>
- );
- }
-}
-
-SearchSuggestionList.propTypes = {
- ...SuggestionList.propTypes
-};
diff --git a/web/react/components/suggestion/search_user_provider.jsx b/web/react/components/suggestion/search_user_provider.jsx
deleted file mode 100644
index 0d553bfc4..000000000
--- a/web/react/components/suggestion/search_user_provider.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-import UserStore from '../../stores/user_store.jsx';
-
-class SearchUserSuggestion extends React.Component {
- render() {
- const {item, isSelection, onClick} = this.props;
-
- let className = 'search-autocomplete__item';
- if (isSelection) {
- className += ' selected';
- }
-
- return (
- <div
- className={className}
- onClick={onClick}
- >
- <img
- className='profile-img rounded'
- src={'/api/v1/users/' + item.id + '/image?time=' + item.update_at}
- />
- <i className='fa fa fa-plus-square'></i>{item.username}
- </div>
- );
- }
-}
-
-SearchUserSuggestion.propTypes = {
- item: React.PropTypes.object.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class SearchUserProvider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/\bfrom:\s*(\S*)$/i).exec(pretext);
- if (captured) {
- const usernamePrefix = captured[1];
-
- const users = UserStore.getProfiles();
- let filtered = [];
-
- for (const id of Object.keys(users)) {
- const user = users[id];
-
- if (user.username.startsWith(usernamePrefix)) {
- filtered.push(user);
- }
- }
-
- filtered = filtered.sort((a, b) => a.username.localeCompare(b.username));
-
- const usernames = filtered.map((user) => user.username);
-
- SuggestionStore.setMatchedPretext(suggestionId, usernamePrefix);
- SuggestionStore.addSuggestions(suggestionId, usernames, filtered, SearchUserSuggestion);
- }
- }
-}
diff --git a/web/react/components/suggestion/suggestion_box.jsx b/web/react/components/suggestion/suggestion_box.jsx
deleted file mode 100644
index 12b098cbd..000000000
--- a/web/react/components/suggestion/suggestion_box.jsx
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../../utils/constants.jsx';
-import * as GlobalActions from '../../action_creators/global_actions.jsx';
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-import * as Utils from '../../utils/utils.jsx';
-
-const KeyCodes = Constants.KeyCodes;
-
-export default class SuggestionBox extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleDocumentClick = this.handleDocumentClick.bind(this);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleCompleteWord = this.handleCompleteWord.bind(this);
- this.handleKeyDown = this.handleKeyDown.bind(this);
- this.handlePretextChanged = this.handlePretextChanged.bind(this);
-
- this.suggestionId = Utils.generateId();
- }
-
- componentDidMount() {
- SuggestionStore.registerSuggestionBox(this.suggestionId);
- $(document).on('click', this.handleDocumentClick);
-
- SuggestionStore.addCompleteWordListener(this.suggestionId, this.handleCompleteWord);
- SuggestionStore.addPretextChangedListener(this.suggestionId, this.handlePretextChanged);
- }
-
- componentWillUnmount() {
- SuggestionStore.removeCompleteWordListener(this.suggestionId, this.handleCompleteWord);
- SuggestionStore.removePretextChangedListener(this.suggestionId, this.handlePretextChanged);
-
- SuggestionStore.unregisterSuggestionBox(this.suggestionId);
- $(document).off('click', this.handleDocumentClick);
- }
-
- getTextbox() {
- // this is to support old code that looks at the input/textarea DOM nodes
- return ReactDOM.findDOMNode(this.refs.textbox);
- }
-
- handleDocumentClick(e) {
- const container = $(ReactDOM.findDOMNode(this));
- if (!(container.is(e.target) || container.has(e.target).length > 0)) {
- // we can't just use blur for this because it fires and hides the children before
- // their click handlers can be called
- GlobalActions.emitClearSuggestions(this.suggestionId);
- }
- }
-
- handleChange(e) {
- const textbox = ReactDOM.findDOMNode(this.refs.textbox);
- const caret = Utils.getCaretPosition(textbox);
- const pretext = textbox.value.substring(0, caret);
-
- GlobalActions.emitSuggestionPretextChanged(this.suggestionId, pretext);
-
- if (this.props.onUserInput) {
- this.props.onUserInput(textbox.value);
- }
-
- if (this.props.onChange) {
- this.props.onChange(e);
- }
- }
-
- handleCompleteWord(term) {
- const textbox = ReactDOM.findDOMNode(this.refs.textbox);
- const caret = Utils.getCaretPosition(textbox);
-
- const text = this.props.value;
- const prefix = text.substring(0, caret - SuggestionStore.getMatchedPretext(this.suggestionId).length);
- const suffix = text.substring(caret);
-
- if (this.props.onUserInput) {
- this.props.onUserInput(prefix + term + ' ' + suffix);
- }
-
- // set the caret position after the next rendering
- window.requestAnimationFrame(() => {
- Utils.setCaretPosition(textbox, prefix.length + term.length + 1);
- });
- }
-
- handleKeyDown(e) {
- if (SuggestionStore.hasSuggestions(this.suggestionId)) {
- if (e.which === KeyCodes.UP) {
- GlobalActions.emitSelectPreviousSuggestion(this.suggestionId);
- e.preventDefault();
- } else if (e.which === KeyCodes.DOWN) {
- GlobalActions.emitSelectNextSuggestion(this.suggestionId);
- e.preventDefault();
- } else if (e.which === KeyCodes.ENTER || e.which === KeyCodes.TAB) {
- GlobalActions.emitCompleteWordSuggestion(this.suggestionId);
- e.preventDefault();
- } else if (this.props.onKeyDown) {
- this.props.onKeyDown(e);
- }
- } else if (this.props.onKeyDown) {
- this.props.onKeyDown(e);
- }
- }
-
- handlePretextChanged(pretext) {
- for (const provider of this.props.providers) {
- provider.handlePretextChanged(this.suggestionId, pretext);
- }
- }
-
- render() {
- const newProps = Object.assign({}, this.props, {
- onChange: this.handleChange,
- onKeyDown: this.handleKeyDown
- });
-
- let textbox = null;
- if (this.props.type === 'input') {
- textbox = (
- <input
- ref='textbox'
- type='text'
- {...newProps}
- />
- );
- } else if (this.props.type === 'textarea') {
- textbox = (
- <textarea
- ref='textbox'
- {...newProps}
- />
- );
- }
-
- const SuggestionListComponent = this.props.listComponent;
-
- return (
- <div>
- {textbox}
- <SuggestionListComponent suggestionId={this.suggestionId}/>
- </div>
- );
- }
-}
-
-SuggestionBox.defaultProps = {
- type: 'input'
-};
-
-SuggestionBox.propTypes = {
- listComponent: React.PropTypes.func.isRequired,
- type: React.PropTypes.oneOf(['input', 'textarea']).isRequired,
- value: React.PropTypes.string.isRequired,
- onUserInput: React.PropTypes.func,
- providers: React.PropTypes.arrayOf(React.PropTypes.object),
-
- // explicitly name any input event handlers we override and need to manually call
- onChange: React.PropTypes.func,
- onKeyDown: React.PropTypes.func
-};
diff --git a/web/react/components/suggestion/suggestion_list.jsx b/web/react/components/suggestion/suggestion_list.jsx
deleted file mode 100644
index 0f5907179..000000000
--- a/web/react/components/suggestion/suggestion_list.jsx
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as GlobalActions from '../../action_creators/global_actions.jsx';
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-
-export default class SuggestionList extends React.Component {
- constructor(props) {
- super(props);
-
- this.getContent = this.getContent.bind(this);
-
- this.handleItemClick = this.handleItemClick.bind(this);
- this.handleSuggestionsChanged = this.handleSuggestionsChanged.bind(this);
-
- this.scrollToItem = this.scrollToItem.bind(this);
-
- this.state = {
- items: [],
- terms: [],
- components: [],
- selection: ''
- };
- }
-
- componentDidMount() {
- SuggestionStore.addSuggestionsChangedListener(this.props.suggestionId, this.handleSuggestionsChanged);
- }
-
- componentWillUnmount() {
- SuggestionStore.removeSuggestionsChangedListener(this.props.suggestionId, this.handleSuggestionsChanged);
- }
-
- getContent() {
- return $(ReactDOM.findDOMNode(this.refs.content));
- }
-
- handleItemClick(term, e) {
- GlobalActions.emitCompleteWordSuggestion(this.props.suggestionId, term);
-
- e.preventDefault();
- }
-
- handleSuggestionsChanged() {
- const selection = SuggestionStore.getSelection(this.props.suggestionId);
-
- this.setState({
- items: SuggestionStore.getItems(this.props.suggestionId),
- terms: SuggestionStore.getTerms(this.props.suggestionId),
- components: SuggestionStore.getComponents(this.props.suggestionId),
- selection
- });
-
- if (selection) {
- window.requestAnimationFrame(() => this.scrollToItem(this.state.selection));
- }
- }
-
- scrollToItem(term) {
- const content = this.getContent();
- const visibleContentHeight = content[0].clientHeight;
- const actualContentHeight = content[0].scrollHeight;
-
- if (visibleContentHeight < actualContentHeight) {
- const contentTop = content.scrollTop();
- const contentTopPadding = parseInt(content.css('padding-top'), 10);
- const contentBottomPadding = parseInt(content.css('padding-top'), 10);
-
- const item = $(ReactDOM.findDOMNode(this.refs[term]));
- const itemTop = item[0].offsetTop - parseInt(item.css('margin-top'), 10);
- const itemBottomMargin = parseInt(item.css('margin-bottom'), 10) + parseInt(item.css('padding-bottom'), 10);
- const itemBottom = item[0].offsetTop + item.height() + itemBottomMargin;
-
- if (itemTop - contentTopPadding < contentTop) {
- // the item is off the top of the visible space
- content.scrollTop(itemTop - contentTopPadding);
- } else if (itemBottom + contentTopPadding + contentBottomPadding > contentTop + visibleContentHeight) {
- // the item has gone off the bottom of the visible space
- content.scrollTop(itemBottom - visibleContentHeight + contentTopPadding + contentBottomPadding);
- }
- }
- }
-
- render() {
- if (this.state.items.length === 0) {
- return null;
- }
-
- const items = [];
- for (let i = 0; i < this.state.items.length; i++) {
- const item = this.state.items[i];
- const term = this.state.terms[i];
- const isSelection = term === this.state.selection;
-
- // ReactComponent names need to be upper case when used in JSX
- const Component = this.state.components[i];
-
- items.push(
- <Component
- key={term}
- ref={term}
- item={item}
- term={term}
- isSelection={isSelection}
- onClick={this.handleItemClick.bind(this, term)}
- />
- );
- }
-
- return (
- <div className='suggestion-list suggestion-list--top'>
- <div
- ref='content'
- className='suggestion-list__content suggestion-list__content--top'
- >
- {items}
- </div>
- </div>
- );
- }
-}
-
-SuggestionList.propTypes = {
- suggestionId: React.PropTypes.string.isRequired
-};
diff --git a/web/react/components/team_export_tab.jsx b/web/react/components/team_export_tab.jsx
deleted file mode 100644
index 12743d9e3..000000000
--- a/web/react/components/team_export_tab.jsx
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class TeamExportTab extends React.Component {
- constructor(props) {
- super(props);
- this.state = {status: 'request', link: '', err: ''};
-
- this.onExportSuccess = this.onExportSuccess.bind(this);
- this.onExportFailure = this.onExportFailure.bind(this);
- this.doExport = this.doExport.bind(this);
- }
- onExportSuccess(data) {
- this.setState({status: 'ready', link: data.link, err: ''});
- }
- onExportFailure(e) {
- this.setState({status: 'failure', link: '', err: e.message});
- }
- doExport() {
- if (this.state.status === 'in-progress') {
- return;
- }
- this.setState({status: 'in-progress'});
- Client.exportTeam(this.onExportSuccess, this.onExportFailure);
- }
- render() {
- var messageSection = '';
- switch (this.state.status) {
- case 'request':
- messageSection = '';
- break;
- case 'in-progress':
- messageSection = (
- <p className='confirm-import alert alert-warning'>
- <i className='fa fa-spinner fa-pulse'/>
- <FormattedMessage
- id='team_export_tab.exporting'
- defaultMessage=' Exporting...'
- />
- </p>
- );
- break;
- case 'ready':
- messageSection = (
- <p className='confirm-import alert alert-success'>
- <i className='fa fa-check'/>
- <FormattedMessage
- id='team_export_tab.ready'
- defaultMessage=' Ready for '
- />
- <a
- href={this.state.link}
- download={true}
- >
- <FormattedMessage
- id='team_export_tab.download'
- defaultMessage='download'
- />
- </a>
- </p>
- );
- break;
- case 'failure':
- messageSection = (
- <p className='confirm-import alert alert-warning'>
- <i className='fa fa-warning'/>
- <FormattedMessage
- id='team_export_tab.unable'
- defaultMessage=' Unable to export: {error}'
- values={{
- error: this.state.err
- }}
- />
- </p>
- );
- break;
- }
-
- return (
- <div
- ref='wrapper'
- className='user-settings'
- >
- <h3 className='tab-header'>
- <FormattedMessage
- id='team_export_tab.export'
- defaultMessage='Export'
- />
- </h3>
- <div className='divider-dark first'/>
- <ul className='section-max'>
- <li className='col-xs-12 section-title'>
- <FormattedMessage
- id='team_export_tab.exportTeam'
- defaultMessage='Export your team'
- />
- </li>
- <li className='col-xs-offset-3 col-xs-8'>
- <ul className='setting-list'>
- <li className='setting-list-item'>
- <a
- className='btn btn-sm btn-primary btn-file sel-btn'
- href='#'
- onClick={this.doExport}
- >
- <FormattedMessage
- id='team_export_tab.export'
- defaultMessage='Export'
- />
- </a>
- </li>
- </ul>
- </li>
- </ul>
- <div className='divider-dark'/>
- {messageSection}
- </div>
- );
- }
-}
diff --git a/web/react/components/team_general_tab.jsx b/web/react/components/team_general_tab.jsx
deleted file mode 100644
index ef7410b2f..000000000
--- a/web/react/components/team_general_tab.jsx
+++ /dev/null
@@ -1,655 +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 Client from '../utils/client.jsx';
-import * as Utils from '../utils/utils.jsx';
-import TeamStore from '../stores/team_store.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- dirDisabled: {
- id: 'general_tab.dirDisabled',
- defaultMessage: 'Team Directory has been disabled. Please ask a System Admin to enable the Team Directory in the System Console team settings.'
- },
- required: {
- id: 'general_tab.required',
- defaultMessage: 'This field is required'
- },
- chooseName: {
- id: 'general_tab.chooseName',
- defaultMessage: 'Please choose a new name for your team'
- },
- includeDirTitle: {
- id: 'general_tab.includeDirTitle',
- defaultMessage: 'Include this team in the Team Directory'
- },
- yes: {
- id: 'general_tab.yes',
- defaultMessage: 'Yes'
- },
- no: {
- id: 'general_tab.no',
- defaultMessage: 'No'
- },
- dirOff: {
- id: 'general_tab.dirOff',
- defaultMessage: 'Team directory is turned off for this system.'
- },
- openInviteTitle: {
- id: 'general_tab.openInviteTitle',
- defaultMessage: 'Allow anyone to sign-up from login page'
- },
- codeTitle: {
- id: 'general_tab.codeTitle',
- defaultMessage: 'Invite Code'
- },
- codeDesc: {
- id: 'general_tab.codeDesc',
- defaultMessage: "Click 'Edit' to regenerate Invite Code."
- },
- teamNameInfo: {
- id: 'general_tab.teamNameInfo',
- defaultMessage: 'Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.'
- }
-});
-
-class GeneralTab extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateSection = this.updateSection.bind(this);
- this.handleNameSubmit = this.handleNameSubmit.bind(this);
- this.handleInviteIdSubmit = this.handleInviteIdSubmit.bind(this);
- this.handleOpenInviteSubmit = this.handleOpenInviteSubmit.bind(this);
- this.handleTeamListingSubmit = this.handleTeamListingSubmit.bind(this);
- this.handleClose = this.handleClose.bind(this);
- this.onUpdateNameSection = this.onUpdateNameSection.bind(this);
- this.updateName = this.updateName.bind(this);
- this.onUpdateInviteIdSection = this.onUpdateInviteIdSection.bind(this);
- this.updateInviteId = this.updateInviteId.bind(this);
- this.onUpdateOpenInviteSection = this.onUpdateOpenInviteSection.bind(this);
- this.handleOpenInviteRadio = this.handleOpenInviteRadio.bind(this);
- this.onUpdateTeamListingSection = this.onUpdateTeamListingSection.bind(this);
- this.handleTeamListingRadio = this.handleTeamListingRadio.bind(this);
- this.handleGenerateInviteId = this.handleGenerateInviteId.bind(this);
-
- this.state = this.setupInitialState(props);
- }
-
- updateSection(section) {
- this.setState(this.setupInitialState(this.props));
- this.props.updateSection(section);
- }
-
- setupInitialState(props) {
- const team = props.team;
-
- return {
- name: team.display_name,
- invite_id: team.invite_id,
- allow_open_invite: team.allow_open_invite,
- allow_team_listing: team.allow_team_listing,
- serverError: '',
- clientError: ''
- };
- }
-
- handleGenerateInviteId(e) {
- e.preventDefault();
-
- var newId = '';
- for (var i = 0; i < 32; i++) {
- newId += Math.floor(Math.random() * 16).toString(16);
- }
-
- this.setState({invite_id: newId});
- }
-
- handleOpenInviteRadio(openInvite) {
- this.setState({allow_open_invite: openInvite});
- }
-
- handleTeamListingRadio(listing) {
- if (global.window.mm_config.EnableTeamListing !== 'true' && listing) {
- this.setState({clientError: this.props.intl.formatMessage(holders.dirDisabled)});
- } else {
- this.setState({allow_team_listing: listing});
- }
- }
-
- handleOpenInviteSubmit(e) {
- e.preventDefault();
-
- var state = {serverError: '', clientError: ''};
-
- var data = this.props.team;
- data.allow_open_invite = this.state.allow_open_invite;
- Client.updateTeam(data,
- (team) => {
- TeamStore.saveTeam(team);
- TeamStore.emitChange();
- this.updateSection('');
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
-
- handleTeamListingSubmit(e) {
- e.preventDefault();
-
- var state = {serverError: '', clientError: ''};
-
- var data = this.props.team;
- data.allow_team_listing = this.state.allow_team_listing;
- Client.updateTeam(data,
- (team) => {
- TeamStore.saveTeam(team);
- TeamStore.emitChange();
- this.updateSection('');
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
-
- handleNameSubmit(e) {
- e.preventDefault();
-
- var state = {serverError: '', clientError: ''};
- let valid = true;
-
- const {formatMessage} = this.props.intl;
- const name = this.state.name.trim();
- if (!name) {
- state.clientError = formatMessage(holders.required);
- valid = false;
- } else if (name === this.props.team.display_name) {
- state.clientError = formatMessage(holders.chooseName);
- valid = false;
- } else {
- state.clientError = '';
- }
-
- this.setState(state);
-
- if (!valid) {
- return;
- }
-
- var data = this.props.team;
- data.display_name = this.state.name;
- Client.updateTeam(data,
- (team) => {
- TeamStore.saveTeam(team);
- TeamStore.emitChange();
- this.updateSection('');
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
-
- handleInviteIdSubmit(e) {
- e.preventDefault();
-
- var state = {serverError: '', clientError: ''};
- let valid = true;
-
- const inviteId = this.state.invite_id.trim();
- if (inviteId) {
- state.clientError = '';
- } else {
- state.clientError = this.props.intl.fromatMessage(holders.required);
- valid = false;
- }
-
- this.setState(state);
-
- if (!valid) {
- return;
- }
-
- var data = this.props.team;
- data.invite_id = this.state.invite_id;
- Client.updateTeam(data,
- (team) => {
- TeamStore.saveTeam(team);
- TeamStore.emitChange();
- this.updateSection('');
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
-
- componentWillReceiveProps(newProps) {
- if (newProps.team && newProps.teamDisplayName) {
- this.setState({name: newProps.teamDisplayName});
- }
- }
-
- handleClose() {
- this.updateSection('');
- }
-
- componentDidMount() {
- $('#team_settings').on('hidden.bs.modal', this.handleClose);
- }
-
- componentWillUnmount() {
- $('#team_settings').off('hidden.bs.modal', this.handleClose);
- }
-
- onUpdateNameSection(e) {
- e.preventDefault();
- if (this.props.activeSection === 'name') {
- this.updateSection('');
- } else {
- this.updateSection('name');
- }
- }
-
- onUpdateInviteIdSection(e) {
- e.preventDefault();
- if (this.props.activeSection === 'invite_id') {
- this.updateSection('');
- } else {
- this.updateSection('invite_id');
- }
- }
-
- onUpdateOpenInviteSection(e) {
- e.preventDefault();
- if (this.props.activeSection === 'open_invite') {
- this.updateSection('');
- } else {
- this.updateSection('open_invite');
- }
- }
-
- onUpdateTeamListingSection(e) {
- e.preventDefault();
- if (this.props.activeSection === 'team_listing') {
- this.updateSection('');
- } else {
- this.updateSection('team_listing');
- }
- }
-
- updateName(e) {
- e.preventDefault();
- this.setState({name: e.target.value});
- }
-
- updateInviteId(e) {
- e.preventDefault();
- this.setState({invite_id: e.target.value});
- }
-
- render() {
- let clientError = null;
- let serverError = null;
- if (this.state.clientError) {
- clientError = this.state.clientError;
- }
- if (this.state.serverError) {
- serverError = this.state.serverError;
- }
-
- const enableTeamListing = global.window.mm_config.EnableTeamListing === 'true';
- const {formatMessage} = this.props.intl;
-
- let teamListingSection;
- if (this.props.activeSection === 'team_listing') {
- const inputs = [];
- let submitHandle = null;
-
- if (enableTeamListing) {
- submitHandle = this.handleTeamListingSubmit;
-
- inputs.push(
- <div key='userTeamListingOptions'>
- <div className='radio'>
- <label>
- <input
- name='userTeamListingOptions'
- type='radio'
- defaultChecked={this.state.allow_team_listing}
- onChange={this.handleTeamListingRadio.bind(this, true)}
- />
- <FormattedMessage
- id='general_tab.yes'
- defaultMessage='Yes'
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- ref='teamListingRadioNo'
- name='userTeamListingOptions'
- type='radio'
- defaultChecked={!this.state.allow_team_listing}
- onChange={this.handleTeamListingRadio.bind(this, false)}
- />
- <FormattedMessage
- id='general_tab.no'
- defaultMessage='No'
- />
- </label>
- <br/>
- </div>
- <div>
- <br/>
- <FormattedMessage
- id='general_tab.includeDirDesc'
- defaultMessage='Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.'
- />
- </div>
- </div>
- );
- } else {
- inputs.push(
- <div key='userTeamListingOptions'>
- <div>
- <br/>
- <FormattedMessage
- id='general_tab.dirContact'
- defaultMessage='Contact your system administrator to turn on the team directory on the system home page.'
- />
- </div>
- </div>
- );
- }
-
- teamListingSection = (
- <SettingItemMax
- title={formatMessage(holders.includeDirTitle)}
- inputs={inputs}
- submit={submitHandle}
- server_error={serverError}
- client_error={clientError}
- updateSection={this.onUpdateTeamListingSection}
- />
- );
- } else {
- let describe = '';
-
- if (enableTeamListing) {
- if (this.state.allow_team_listing === true) {
- describe = formatMessage(holders.yes);
- } else {
- describe = formatMessage(holders.no);
- }
- } else {
- describe = formatMessage(holders.dirOff);
- }
-
- teamListingSection = (
- <SettingItemMin
- title={formatMessage(holders.includeDirTitle)}
- describe={describe}
- updateSection={this.onUpdateTeamListingSection}
- />
- );
- }
-
- let openInviteSection;
- if (this.props.activeSection === 'open_invite') {
- const inputs = [
- <div key='userOpenInviteOptions'>
- <div className='radio'>
- <label>
- <input
- name='userOpenInviteOptions'
- type='radio'
- defaultChecked={this.state.allow_open_invite}
- onChange={this.handleOpenInviteRadio.bind(this, true)}
- />
- <FormattedMessage
- id='general_tab.yes'
- defaultMessage='Yes'
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- name='userOpenInviteOptions'
- type='radio'
- defaultChecked={!this.state.allow_open_invite}
- onChange={this.handleOpenInviteRadio.bind(this, false)}
- />
- <FormattedMessage
- id='general_tab.no'
- defaultMessage='No'
- />
- </label>
- <br/>
- </div>
- <div>
- <br/>
- <FormattedMessage
- id='general_tab.openInviteDesc'
- defaultMessage='When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.'
- />
- </div>
- </div>
- ];
-
- openInviteSection = (
- <SettingItemMax
- title={formatMessage(holders.openInviteTitle)}
- inputs={inputs}
- submit={this.handleOpenInviteSubmit}
- server_error={serverError}
- updateSection={this.onUpdateOpenInviteSection}
- />
- );
- } else {
- let describe = '';
- if (this.state.allow_open_invite === true) {
- describe = formatMessage(holders.yes);
- } else {
- describe = formatMessage(holders.no);
- }
-
- openInviteSection = (
- <SettingItemMin
- title={formatMessage(holders.openInviteTitle)}
- describe={describe}
- updateSection={this.onUpdateOpenInviteSection}
- />
- );
- }
-
- let inviteSection;
-
- if (this.props.activeSection === 'invite_id') {
- const inputs = [];
-
- inputs.push(
- <div key='teamInviteSetting'>
- <div className='row'>
- <label className='col-sm-5 control-label visible-xs-block'>
- </label>
- <div className='col-sm-12'>
- <input
- className='form-control'
- type='text'
- onChange={this.updateInviteId}
- value={this.state.invite_id}
- maxLength='32'
- />
- <div className='padding-top x2'>
- <a
- href='#'
- onClick={this.handleGenerateInviteId}
- >
- <FormattedMessage
- id='general_tab.regenerate'
- defaultMessage='Re-Generate'
- />
- </a>
- </div>
- </div>
- </div>
- <div className='setting-list__hint'>
- <FormattedMessage
- id='general_tab.codeLongDesc'
- defaultMessage='The Invite Code is used as part of the URL in the team invitation link created by **Get Team Invite Link** in the main menu. Regenerating creates a new team invitation link and invalidates the previous link.'
- />
- </div>
- </div>
- );
-
- inviteSection = (
- <SettingItemMax
- title={formatMessage(holders.codeTitle)}
- inputs={inputs}
- submit={this.handleInviteIdSubmit}
- server_error={serverError}
- client_error={clientError}
- updateSection={this.onUpdateInviteIdSection}
- />
- );
- } else {
- inviteSection = (
- <SettingItemMin
- title={formatMessage(holders.codeTitle)}
- describe={formatMessage(holders.codeDesc)}
- updateSection={this.onUpdateInviteIdSection}
- />
- );
- }
-
- let nameSection;
-
- if (this.props.activeSection === 'name') {
- const inputs = [];
-
- let teamNameLabel = (
- <FormattedMessage
- id='general_tab.teamName'
- defaultMessage='Team Name'
- />
- );
- if (Utils.isMobile()) {
- teamNameLabel = '';
- }
-
- inputs.push(
- <div
- key='teamNameSetting'
- className='form-group'
- >
- <label className='col-sm-5 control-label'>{teamNameLabel}</label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='text'
- maxLength='22'
- onChange={this.updateName}
- value={this.state.name}
- />
- </div>
- </div>
- );
-
- const nameExtraInfo = <span>{formatMessage(holders.teamNameInfo)}</span>;
-
- nameSection = (
- <SettingItemMax
- title={formatMessage({id: 'general_tab.teamName'})}
- inputs={inputs}
- submit={this.handleNameSubmit}
- server_error={serverError}
- client_error={clientError}
- updateSection={this.onUpdateNameSection}
- extraInfo={nameExtraInfo}
- />
- );
- } else {
- var describe = this.state.name;
-
- nameSection = (
- <SettingItemMin
- title={formatMessage({id: 'general_tab.teamName'})}
- describe={describe}
- updateSection={this.onUpdateNameSection}
- />
- );
- }
-
- return (
- <div>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>&times;</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- <div className='modal-back'>
- <i className='fa fa-angle-left'></i>
- </div>
- <FormattedMessage
- id='general_tab.title'
- defaultMessage='General Settings'
- />
- </h4>
- </div>
- <div
- ref='wrapper'
- className='user-settings'
- >
- <h3 className='tab-header'>
- <FormattedMessage
- id='general_tab.title'
- defaultMessage='General Settings'
- />
- </h3>
- <div className='divider-dark first'/>
- {nameSection}
- <div className='divider-light'/>
- {openInviteSection}
- <div className='divider-light'/>
- {teamListingSection}
- <div className='divider-light'/>
- {inviteSection}
- <div className='divider-dark'/>
- </div>
- </div>
- );
- }
-}
-
-GeneralTab.propTypes = {
- intl: intlShape.isRequired,
- updateSection: React.PropTypes.func.isRequired,
- team: React.PropTypes.object.isRequired,
- activeSection: React.PropTypes.string.isRequired
-};
-
-export default injectIntl(GeneralTab); \ No newline at end of file
diff --git a/web/react/components/team_import_tab.jsx b/web/react/components/team_import_tab.jsx
deleted file mode 100644
index c9a8afe6c..000000000
--- a/web/react/components/team_import_tab.jsx
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as utils from '../utils/utils.jsx';
-import SettingUpload from './setting_upload.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const holders = defineMessages({
- importSlack: {
- id: 'team_import_tab.importSlack',
- defaultMessage: 'Import from Slack (Beta)'
- }
-});
-
-class TeamImportTab extends React.Component {
- constructor(props) {
- super(props);
-
- this.onImportFailure = this.onImportFailure.bind(this);
- this.onImportSuccess = this.onImportSuccess.bind(this);
- this.doImportSlack = this.doImportSlack.bind(this);
-
- this.state = {
- status: 'ready',
- link: ''
- };
- }
-
- onImportFailure() {
- this.setState({status: 'fail', link: ''});
- }
-
- onImportSuccess(data) {
- this.setState({status: 'done', link: 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(data)});
- }
-
- doImportSlack(file) {
- this.setState({status: 'in-progress', link: ''});
- utils.importSlack(file, this.onImportSuccess, this.onImportFailure);
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var uploadHelpText = (
- <div>
- <FormattedHTMLMessage
- id='team_import_tab.importHelp'
- defaultMessage="<p>To import a team from Slack go to Slack > Team Settings > Import/Export Data > Export > Start Export. Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team's public channels.</p><p>The Slack import to Mattermost is in 'Beta'. Slack bot posts do not yet import and Slack @mentions are not currently supported.</p>"
- />
- </div>
- );
-
- var uploadSection = (
- <SettingUpload
- title={formatMessage(holders.importSlack)}
- submit={this.doImportSlack}
- helpText={uploadHelpText}
- fileTypesAccepted='.zip'
- />
- );
-
- var messageSection;
- switch (this.state.status) {
-
- case 'ready':
- messageSection = '';
- break;
- case 'in-progress':
- messageSection = (
- <p className='confirm-import alert alert-warning'><i className='fa fa-spinner fa-pulse'></i>
- <FormattedMessage
- id='team_import_tab.importing'
- defaultMessage=' Importing...'
- />
- </p>
- );
- break;
- case 'done':
- messageSection = (
- <p className='confirm-import alert alert-success'>
- <i className='fa fa-check'/>
- <FormattedMessage
- id='team_import_tab.successful'
- defaultMessage=' Import successful: '
- />
- <a
- href={this.state.link}
- download='MattermostImportSummary.txt'
- >
- <FormattedMessage
- id='team_import_tab.summary'
- defaultMessage='View Summary'
- />
- </a>
- </p>
- );
- break;
- case 'fail':
- messageSection = (
- <p className='confirm-import alert alert-warning'>
- <i className='fa fa-warning'/>
- <FormattedMessage
- id='team_import_tab.failure'
- defaultMessage=' Import failure: '
- />
- <a
- href={this.state.link}
- download='MattermostImportSummary.txt'
- >
- <FormattedMessage
- id='team_import_tab.summary'
- defaultMessage='View Summary'
- />
- </a>
- </p>
- );
- break;
- }
-
- return (
- <div>
- <div className='modal-header'>
- <button type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>{'×'}</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- <div className='modal-back'>
- <i className='fa fa-angle-left'></i>
- </div>
- <FormattedMessage
- id='team_import_tab.import'
- defaultMessage='Import'
- />
- </h4>
- </div>
- <div
- ref='wrapper'
- className='user-settings'
- >
- <h3 className='tab-header'>
- <FormattedMessage
- id='team_import_tab.import'
- defaultMessage='Import'
- />
- </h3>
- <div className='divider-dark first'/>
- {uploadSection}
- <div className='divider-dark'/>
- {messageSection}
- </div>
- </div>
- );
- }
-}
-
-TeamImportTab.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(TeamImportTab); \ No newline at end of file
diff --git a/web/react/components/team_members_dropdown.jsx b/web/react/components/team_members_dropdown.jsx
deleted file mode 100644
index cd7585d94..000000000
--- a/web/react/components/team_members_dropdown.jsx
+++ /dev/null
@@ -1,332 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../stores/user_store.jsx';
-import ChannelStore from '../stores/channel_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 ConfirmModal from './confirm_modal.jsx';
-import TeamStore from '../stores/team_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class TeamMembersDropdown extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleMakeMember = this.handleMakeMember.bind(this);
- this.handleMakeActive = this.handleMakeActive.bind(this);
- this.handleMakeNotActive = this.handleMakeNotActive.bind(this);
- this.handleMakeAdmin = this.handleMakeAdmin.bind(this);
- this.handleDemote = this.handleDemote.bind(this);
- this.handleDemoteSubmit = this.handleDemoteSubmit.bind(this);
- this.handleDemoteCancel = this.handleDemoteCancel.bind(this);
-
- this.state = {
- serverError: null,
- showDemoteModal: false,
- user: null,
- role: null
- };
- }
- handleMakeMember() {
- const me = UserStore.getCurrentUser();
- if (this.props.user.id === me.id) {
- this.handleDemote(this.props.user, '');
- } else {
- const data = {
- user_id: this.props.user.id,
- new_roles: ''
- };
- Client.updateRoles(data,
- () => {
- AsyncClient.getProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- }
- handleMakeActive() {
- Client.updateActive(this.props.user.id, true,
- () => {
- AsyncClient.getProfiles();
- AsyncClient.getChannelExtraInfo(ChannelStore.getCurrentId());
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- handleMakeNotActive() {
- Client.updateActive(this.props.user.id, false,
- () => {
- AsyncClient.getProfiles();
- AsyncClient.getChannelExtraInfo(ChannelStore.getCurrentId());
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- handleMakeAdmin() {
- const me = UserStore.getCurrentUser();
- if (this.props.user.id === me.id) {
- this.handleDemote(this.props.user, 'admin');
- } else {
- const data = {
- user_id: this.props.user.id,
- new_roles: 'admin'
- };
-
- Client.updateRoles(data,
- () => {
- AsyncClient.getProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- }
- handleDemote(user, role) {
- this.setState({
- serverError: this.state.serverError,
- showDemoteModal: true,
- user,
- role
- });
- }
- handleDemoteCancel() {
- this.setState({
- serverError: null,
- showDemoteModal: false,
- user: null,
- role: null
- });
- }
- handleDemoteSubmit() {
- const data = {
- user_id: this.props.user.id,
- new_roles: this.state.role
- };
-
- Client.updateRoles(data,
- () => {
- const teamUrl = TeamStore.getCurrentTeamUrl();
- if (teamUrl) {
- window.location.href = teamUrl;
- } else {
- window.location.href = '/';
- }
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- render() {
- let serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='has-error'>
- <label className='has-error control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- const user = this.props.user;
- let currentRoles = (
- <FormattedMessage
- id='team_members_dropdown.member'
- defaultMessage='Member'
- />
- );
-
- if (user.roles.length > 0) {
- if (Utils.isSystemAdmin(user.roles)) {
- currentRoles = (
- <FormattedMessage
- id='team_members_dropdown.systemAdmin'
- defaultMessage='System Admin'
- />
- );
- } else if (Utils.isAdmin(user.roles)) {
- currentRoles = (
- <FormattedMessage
- id='team_members_dropdown.teamAdmin'
- defaultMessage='Team Admin'
- />
- );
- } else {
- currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
- }
- }
-
- let showMakeMember = user.roles === 'admin' || user.roles === 'system_admin';
- let showMakeAdmin = user.roles === '' || user.roles === 'system_admin';
- let showMakeActive = false;
- let showMakeNotActive = user.roles !== 'system_admin';
-
- if (user.delete_at > 0) {
- currentRoles = (
- <FormattedMessage
- id='team_members_dropdown.inactive'
- defaultMessage='Inactive'
- />
- );
- showMakeMember = false;
- showMakeAdmin = false;
- showMakeActive = true;
- showMakeNotActive = false;
- }
-
- let makeAdmin = null;
- if (showMakeAdmin) {
- makeAdmin = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeAdmin}
- >
- <FormattedMessage
- id='team_members_dropdown.makeAdmin'
- defaultMessage='Make Team Admin'
- />
- </a>
- </li>
- );
- }
-
- let makeMember = null;
- if (showMakeMember) {
- makeMember = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeMember}
- >
- <FormattedMessage
- id='team_members_dropdown.makeMember'
- defaultMessage='Make Member'
- />
- </a>
- </li>
- );
- }
-
- let makeActive = null;
- if (showMakeActive) {
- makeActive = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeActive}
- >
- <FormattedMessage
- id='team_members_dropdown.makeActive'
- defaultMessage='Make Active'
- />
- </a>
- </li>
- );
- }
-
- let makeNotActive = null;
- if (showMakeNotActive) {
- makeNotActive = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeNotActive}
- >
- <FormattedMessage
- id='team_members_dropdown.makeInactive'
- defaultMessage='Make Inactive'
- />
- </a>
- </li>
- );
- }
- const me = UserStore.getCurrentUser();
- let makeDemoteModal = null;
- if (this.props.user.id === me.id) {
- const title = (
- <FormattedMessage
- id='team_members_dropdown.confirmDemoteRoleTitle'
- defaultMessage='Confirm demotion from System Admin role'
- />
- );
-
- const message = (
- <div>
- <FormattedMessage
- id='team_members_dropdown.confirmDemoteDescription'
- defaultMessage="If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you'll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command."
- />
- <br/>
- <br/>
- <FormattedMessage
- id='team_members_dropdown.confirmDemotionCmd'
- defaultMessage='platform -assign_role -team_name="yourteam" -email="name@yourcompany.com" -role="system_admin"'
- />
- {serverError}
- </div>
- );
-
- const confirmButton = (
- <FormattedMessage
- id='team_members_dropdown.confirmDemotion'
- defaultMessage='Confirm Demotion'
- />
- );
-
- makeDemoteModal = (
- <ConfirmModal
- show={this.state.showDemoteModal}
- title={title}
- message={message}
- confirmButton={confirmButton}
- onConfirm={this.handleDemoteSubmit}
- onCancel={this.handleDemoteCancel}
- />
- );
- }
-
- return (
- <div className='dropdown member-drop'>
- <a
- href='#'
- className='dropdown-toggle theme'
- type='button'
- data-toggle='dropdown'
- aria-expanded='true'
- >
- <span>{currentRoles} </span>
- <span className='fa fa-chevron-down'></span>
- </a>
- <ul
- className='dropdown-menu member-menu'
- role='menu'
- >
- {makeAdmin}
- {makeMember}
- {makeActive}
- {makeNotActive}
- </ul>
- {makeDemoteModal}
- {serverError}
- </div>
- );
- }
-}
-
-TeamMembersDropdown.propTypes = {
- user: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/team_members_modal.jsx b/web/react/components/team_members_modal.jsx
deleted file mode 100644
index 786e8f947..000000000
--- a/web/react/components/team_members_modal.jsx
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import MemberListTeam from './member_list_team.jsx';
-import TeamStore from '../stores/team_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-export default class TeamMembersModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.teamChanged = this.teamChanged.bind(this);
-
- this.state = {
- team: TeamStore.getCurrent()
- };
- }
- componentDidMount() {
- if (this.props.show) {
- this.onShow();
- }
-
- TeamStore.addChangeListener(this.teamChanged);
- }
-
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.teamChanged);
- }
-
- teamChanged() {
- this.setState({team: TeamStore.getCurrent()});
- }
-
- render() {
- let teamDisplayName = '';
- if (this.state.team) {
- teamDisplayName = this.state.team.display_name;
- }
-
- let maxHeight = 1000;
- if (Utils.windowHeight() <= 1200) {
- maxHeight = Utils.windowHeight() - 300;
- }
-
- return (
- <Modal
- dialogClassName='more-modal'
- show={this.props.show}
- onHide={this.props.onHide}
- >
- <Modal.Header closeButton={true}>
- <FormattedMessage
- id='team_member_modal.members'
- defaultMessage='{team} Members'
- values={{
- team: teamDisplayName
- }}
- />
- </Modal.Header>
- <Modal.Body>
- <MemberListTeam style={{maxHeight}}/>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onHide}
- >
- <FormattedMessage
- id='team_member_modal.close'
- defaultMessage='Close'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-TeamMembersModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/team_settings.jsx b/web/react/components/team_settings.jsx
deleted file mode 100644
index 0eb9d1211..000000000
--- a/web/react/components/team_settings.jsx
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import TeamStore from '../stores/team_store.jsx';
-import ImportTab from './team_import_tab.jsx';
-import ExportTab from './team_export_tab.jsx';
-import GeneralTab from './team_general_tab.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-export default class TeamSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
-
- this.state = {team: TeamStore.getCurrent()};
- }
- componentDidMount() {
- TeamStore.addChangeListener(this.onChange);
- }
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.onChange);
- }
- onChange() {
- var team = TeamStore.getCurrent();
- if (!Utils.areObjectsEqual(this.state.team, team)) {
- this.setState({team});
- }
- }
- render() {
- if (!this.state.team) {
- return null;
- }
- var result;
- switch (this.props.activeTab) {
- case 'general':
- result = (
- <div>
- <GeneralTab
- team={this.state.team}
- activeSection={this.props.activeSection}
- updateSection={this.props.updateSection}
- />
- </div>
- );
- break;
- case 'import':
- result = (
- <div>
- <ImportTab
- team={this.state.team}
- activeSection={this.props.activeSection}
- updateSection={this.props.updateSection}
- />
- </div>
- );
- break;
- case 'export':
- result = (
- <div>
- <ExportTab/>
- </div>
- );
- break;
- default:
- result = (
- <div/>
- );
- break;
- }
- return result;
- }
-}
-
-TeamSettings.defaultProps = {
- activeTab: '',
- activeSection: ''
-};
-
-TeamSettings.propTypes = {
- activeTab: React.PropTypes.string.isRequired,
- activeSection: React.PropTypes.string.isRequired,
- updateSection: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/team_settings_modal.jsx b/web/react/components/team_settings_modal.jsx
deleted file mode 100644
index bef3ebf24..000000000
--- a/web/react/components/team_settings_modal.jsx
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SettingsSidebar from './settings_sidebar.jsx';
-import TeamSettings from './team_settings.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- generalTab: {
- id: 'team_settings_modal.generalTab',
- defaultMessage: 'General'
- },
- importTab: {
- id: 'team_settings_modal.importTab',
- defaultMessage: 'Import'
- },
- exportTab: {
- id: 'team_settings_modal.exportTab',
- defaultMessage: 'Export'
- }
-});
-
-class TeamSettingsModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateTab = this.updateTab.bind(this);
- this.updateSection = this.updateSection.bind(this);
-
- this.state = {
- activeTab: 'general',
- activeSection: ''
- };
- }
- componentDidMount() {
- const modal = $(ReactDOM.findDOMNode(this.refs.modal));
-
- modal.on('click', '.modal-back', function handleBackClick() {
- $(this).closest('.modal-dialog').removeClass('display--content');
- $(this).closest('.modal-dialog').find('.settings-table .nav li.active').removeClass('active');
- });
- modal.on('click', '.modal-header .close', () => {
- setTimeout(() => {
- $('.modal-dialog.display--content').removeClass('display--content');
- }, 500);
- });
- }
- updateTab(tab) {
- this.setState({activeTab: tab, activeSection: ''});
- }
- updateSection(section) {
- this.setState({activeSection: section});
- }
- render() {
- const {formatMessage} = this.props.intl;
- const tabs = [];
- tabs.push({name: 'general', uiName: formatMessage(holders.generalTab), icon: 'glyphicon glyphicon-cog'});
- tabs.push({name: 'import', uiName: formatMessage(holders.importTab), icon: 'glyphicon glyphicon-upload'});
-
- // To enable export uncomment this line
- //tabs.push({name: 'export', uiName: formatMessage(holders.exportTab), icon: 'glyphicon glyphicon-download'});
-
- return (
- <div
- className='modal fade'
- ref='modal'
- id='team_settings'
- role='dialog'
- tabIndex='-1'
- aria-hidden='true'
- >
- <div className='modal-dialog settings-modal'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>&times;</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- <FormattedMessage
- id='team_settings_modal.title'
- defaultMessage='Team Settings'
- />
- </h4>
- </div>
- <div className='modal-body settings-modal__body'>
- <div className='settings-table'>
- <div className='settings-links'>
- <SettingsSidebar
- tabs={tabs}
- activeTab={this.state.activeTab}
- updateTab={this.updateTab}
- />
- </div>
- <div className='settings-content minimize-settings'>
- <TeamSettings
- activeTab={this.state.activeTab}
- activeSection={this.state.activeSection}
- updateSection={this.updateSection}
- />
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-TeamSettingsModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(TeamSettingsModal); \ No newline at end of file
diff --git a/web/react/components/team_signup_choose_auth.jsx b/web/react/components/team_signup_choose_auth.jsx
deleted file mode 100644
index 5db83fe7d..000000000
--- a/web/react/components/team_signup_choose_auth.jsx
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class ChooseAuthPage extends React.Component {
- constructor(props) {
- super(props);
- this.state = {};
- }
- render() {
- var buttons = [];
- if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
- buttons.push(
- <a
- className='btn btn-custom-login gitlab btn-full'
- key='gitlab'
- href='#'
- onClick={
- function clickGit(e) {
- e.preventDefault();
- this.props.updatePage('gitlab');
- }.bind(this)
- }
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='choose_auth_page.gitlabCreate'
- defaultMessage='Create new team with GitLab Account'
- />
- </span>
- </a>
- );
- }
-
- if (global.window.mm_config.EnableSignUpWithGoogle === 'true') {
- buttons.push(
- <a
- className='btn btn-custom-login google btn-full'
- key='google'
- href='#'
- onClick={
- (e) => {
- e.preventDefault();
- this.props.updatePage('google');
- }
- }
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='choose_auth_page.googleCreate'
- defaultMessage='Create new team with Google Apps Account'
- />
- </span>
- </a>
- );
- }
-
- if (global.window.mm_config.EnableLdap === 'true') {
- buttons.push(
- <a
- className='btn btn-custom-login ldap btn-full'
- key='ldap'
- href='#'
- onClick={
- (e) => {
- e.preventDefault();
- this.props.updatePage('ldap');
- }
- }
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='choose_auth_page.ldapCreate'
- defaultMessage='Create new team with LDAP Account'
- />
- </span>
- </a>
- );
- }
-
- if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
- buttons.push(
- <a
- className='btn btn-custom-login email btn-full'
- key='email'
- href='#'
- onClick={
- function clickEmail(e) {
- e.preventDefault();
- this.props.updatePage('email');
- }.bind(this)
- }
- >
- <span className='fa fa-envelope'/>
- <span>
- <FormattedMessage
- id='choose_auth_page.emailCreate'
- defaultMessage='Create new team with email address'
- />
- </span>
- </a>
- );
- }
-
- if (buttons.length === 0) {
- buttons = (
- <span>
- <FormattedMessage
- id='choose_auth_page.noSignup'
- defaultMessage='No sign-up methods configured, please contact your system administrator.'
- />
- </span>
- );
- }
-
- return (
- <div>
- {buttons}
- <div className='form-group margin--extra-2x'>
- <span><a href='/find_team'>
- <FormattedMessage
- id='choose_auth_page.find'
- defaultMessage='Find my teams'
- />
- </a></span>
- </div>
- </div>
- );
- }
-}
-
-ChooseAuthPage.propTypes = {
- updatePage: React.PropTypes.func
-};
diff --git a/web/react/components/team_signup_with_email.jsx b/web/react/components/team_signup_with_email.jsx
deleted file mode 100644
index a81b22d90..000000000
--- a/web/react/components/team_signup_with_email.jsx
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-import {browserHistory} from 'react-router';
-
-const holders = defineMessages({
- emailError: {
- id: 'email_signup.emailError',
- defaultMessage: 'Please enter a valid email address'
- },
- address: {
- id: 'email_signup.address',
- defaultMessage: 'Email Address'
- }
-});
-
-class EmailSignUpPage extends React.Component {
- constructor() {
- super();
-
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {};
- }
- handleSubmit(e) {
- e.preventDefault();
- const team = {};
- const state = {serverError: null};
- let isValid = true;
-
- team.email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
- if (!team.email || !Utils.isEmail(team.email)) {
- state.emailError = this.props.intl.formatMessage(holders.emailError);
- isValid = false;
- } else {
- state.emailError = null;
- }
-
- if (!isValid) {
- this.setState(state);
- return;
- }
-
- Client.signupTeam(team.email,
- (data) => {
- if (data.follow_link) {
- browserHistory.push(data.follow_link);
- } else {
- browserHistory.push(`/signup_team_confirm/?email=${encodeURIComponent(team.email)}`);
- }
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
- render() {
- let serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- let emailError = null;
- if (this.state.emailError) {
- emailError = <div className='form-group has-error'><label className='control-label'>{this.state.emailError}</label></div>;
- }
-
- return (
- <form
- role='form'
- onSubmit={this.handleSubmit}
- >
- <div className='form-group'>
- <input
- autoFocus={true}
- type='email'
- ref='email'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.address)}
- maxLength='128'
- spellCheck='false'
- />
- {emailError}
- </div>
- <div className='form-group'>
- <button
- className='btn btn-md btn-primary'
- type='submit'
- >
- <FormattedMessage
- id='email_signup.createTeam'
- defaultMessage='Create Team'
- />
- </button>
- {serverError}
- </div>
- <div className='form-group margin--extra-2x'>
- <span><a href='/find_team'>
- <FormattedMessage
- id='email_signup.find'
- defaultMessage='Find my teams'
- />
- </a></span>
- </div>
- </form>
- );
- }
-}
-
-EmailSignUpPage.defaultProps = {
-};
-EmailSignUpPage.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(EmailSignUpPage);
diff --git a/web/react/components/team_signup_with_ldap.jsx b/web/react/components/team_signup_with_ldap.jsx
deleted file mode 100644
index 8c808fa88..000000000
--- a/web/react/components/team_signup_with_ldap.jsx
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- team_error: {
- id: 'ldap_signup.team_error',
- defaultMessage: 'Please enter a team name'
- },
- length_error: {
- id: 'ldap_signup.length_error',
- defaultMessage: 'Name must be 3 or more characters up to a maximum of 15'
- },
- teamName: {
- id: 'ldap_signup.teamName',
- defaultMessage: 'Enter name of new team'
- },
- badTeam: {
- id: 'login_ldap.badTeam',
- defaultMessage: 'Bad team name'
- },
- idReq: {
- id: 'login_ldap.idlReq',
- defaultMessage: 'An LDAP ID is required'
- },
- pwdReq: {
- id: 'login_ldap.pwdReq',
- defaultMessage: 'An LDAP password is required'
- },
- username: {
- id: 'login_ldap.username',
- defaultMessage: 'LDAP Username'
- },
- pwd: {
- id: 'login_ldap.pwd',
- defaultMessage: 'LDAP Password'
- }
-});
-
-class LdapSignUpPage extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.valueChange = this.valueChange.bind(this);
-
- this.state = {name: '', id: '', password: ''};
- }
-
- handleSubmit(e) {
- e.preventDefault();
- const {formatMessage} = this.props.intl;
- var teamSignup = {};
- teamSignup.user = {};
- teamSignup.team = {};
- var state = this.state;
- state.serverError = null;
-
- teamSignup.team.display_name = this.state.name;
-
- if (!teamSignup.team.display_name) {
- state.serverError = formatMessage(holders.team_error);
- this.setState(state);
- return;
- }
-
- if (teamSignup.team.display_name.length <= 2) {
- state.serverError = formatMessage(holders.length_error);
- this.setState(state);
- return;
- }
-
- const id = this.refs.id.value.trim();
- if (!id) {
- state.serverError = formatMessage(holders.idReq);
- this.setState(state);
- return;
- }
-
- const password = this.refs.password.value.trim();
- if (!password) {
- state.serverError = formatMessage(holders.pwdReq);
- this.setState(state);
- return;
- }
-
- state.serverError = '';
- this.setState(state);
-
- teamSignup.team.name = utils.cleanUpUrlable(teamSignup.team.display_name);
- teamSignup.team.type = 'O';
-
- teamSignup.user.username = ReactDOM.findDOMNode(this.refs.id).value.trim();
- teamSignup.user.password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- teamSignup.user.allow_marketing = true;
- teamSignup.user.ldap = true;
- teamSignup.user.auth_service = 'ldap';
-
- $('#ldap-button').button('loading');
-
- Client.createTeamWithLdap(teamSignup,
- () => {
- Client.track('signup', 'signup_team_ldap_complete');
-
- Client.loginByLdap(teamSignup.team.name, id, password,
- () => {
- window.location.href = '/' + teamSignup.team.name + '/channels/town-square';
- },
- (err) => {
- $('#ldap-button').button('reset');
- this.setState({serverError: err.message});
- }
- );
- },
- (err) => {
- $('#ldap-button').button('reset');
- this.setState({serverError: err.message});
- }
- );
- }
-
- valueChange() {
- this.setState({
- name: ReactDOM.findDOMNode(this.refs.teamname).value.trim(),
- id: ReactDOM.findDOMNode(this.refs.id).value.trim(),
- password: ReactDOM.findDOMNode(this.refs.password).value.trim()
- });
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var nameError = null;
- var nameDivClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameDivClass += ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var disabled = false;
- if (this.state.name.length <= 2) {
- disabled = true;
- }
-
- if (this.state.id.length <= 1) {
- disabled = true;
- }
-
- if (this.state.password.length <= 1) {
- disabled = true;
- }
-
- return (
- <form
- role='form'
- onSubmit={this.handleSubmit}
- >
- <div className={nameDivClass}>
- <input
- autoFocus={true}
- type='text'
- ref='teamname'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.teamName)}
- maxLength='128'
- onChange={this.valueChange}
- spellCheck='false'
- />
- {nameError}
- </div>
- <div className={nameDivClass}>
- <input
- className='form-control'
- ref='id'
- placeholder={formatMessage(holders.username)}
- spellCheck='false'
- onChange={this.valueChange}
- />
- </div>
- <div className={nameDivClass}>
- <input
- type='password'
- className='form-control'
- ref='password'
- placeholder={formatMessage(holders.pwd)}
- spellCheck='false'
- onChange={this.valueChange}
- />
- </div>
- <div className='form-group'>
- <a
- className='btn btn-custom-login ldap btn-full'
- key='ldap'
- id='ldap-button'
- href='#'
- onClick={this.handleSubmit}
- disabled={disabled}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='ldap_signup.ldap'
- defaultMessage='Create team with LDAP Account'
- />
- </span>
- </a>
- {serverError}
- </div>
- <div className='form-group margin--extra-2x'>
- <span><a href='/find_team'>
- <FormattedMessage
- id='ldap_signup.find'
- defaultMessage='Find my teams'
- />
- </a></span>
- </div>
- </form>
- );
- }
-}
-
-LdapSignUpPage.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(LdapSignUpPage); \ No newline at end of file
diff --git a/web/react/components/team_signup_with_sso.jsx b/web/react/components/team_signup_with_sso.jsx
deleted file mode 100644
index 465f73fd2..000000000
--- a/web/react/components/team_signup_with_sso.jsx
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as utils from '../utils/utils.jsx';
-import * as client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- team_error: {
- id: 'sso_signup.team_error',
- defaultMessage: 'Please enter a team name'
- },
- length_error: {
- id: 'sso_signup.length_error',
- defaultMessage: 'Name must be 3 or more characters up to a maximum of 15'
- },
- teamName: {
- id: 'sso_signup.teamName',
- defaultMessage: 'Enter name of new team'
- }
-});
-
-class SSOSignUpPage extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.nameChange = this.nameChange.bind(this);
-
- this.state = {name: ''};
- }
- handleSubmit(e) {
- e.preventDefault();
- const {formatMessage} = this.props.intl;
- var team = {};
- var state = this.state;
- state.nameError = null;
- state.serverError = null;
-
- team.display_name = this.state.name;
-
- if (!team.display_name) {
- state.nameError = formatMessage(holders.team_error);
- this.setState(state);
- return;
- }
-
- if (team.display_name.length <= 2) {
- state.nameError = formatMessage(holders.length_error);
- this.setState(state);
- return;
- }
-
- team.name = utils.cleanUpUrlable(team.display_name);
- team.type = 'O';
-
- client.createTeamWithSSO(team,
- this.props.service,
- (data) => {
- if (data.follow_link) {
- window.location.href = data.follow_link;
- } else {
- window.location.href = '/' + team.name + '/channels/town-square';
- }
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
- nameChange() {
- this.setState({name: ReactDOM.findDOMNode(this.refs.teamname).value.trim()});
- }
- render() {
- var nameError = null;
- var nameDivClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameDivClass += ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var disabled = false;
- if (this.state.name.length <= 2) {
- disabled = true;
- }
-
- var button = null;
-
- if (this.props.service === Constants.GITLAB_SERVICE) {
- button = (
- <a
- className='btn btn-custom-login gitlab btn-full'
- key='gitlab'
- href='#'
- onClick={this.handleSubmit}
- disabled={disabled}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='sso_signup.gitlab'
- defaultMessage='Create team with GitLab Account'
- />
- </span>
- </a>
- );
- } else if (this.props.service === Constants.GOOGLE_SERVICE) {
- button = (
- <a
- className='btn btn-custom-login google btn-full'
- key='google'
- href='#'
- onClick={this.handleSubmit}
- disabled={disabled}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='sso_signup.google'
- defaultMessage='Create team with Google Apps Account'
- />
- </span>
- </a>
- );
- }
-
- return (
- <form
- role='form'
- onSubmit={this.handleSubmit}
- >
- <div className={nameDivClass}>
- <input
- autoFocus={true}
- type='text'
- ref='teamname'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.teamName)}
- maxLength='128'
- onChange={this.nameChange}
- spellCheck='false'
- />
- {nameError}
- </div>
- <div className='form-group'>
- {button}
- {serverError}
- </div>
- <div className='form-group margin--extra-2x'>
- <span><a href='/find_team'>
- <FormattedMessage
- id='sso_signup.find'
- defaultMessage='Find my teams'
- />
- </a></span>
- </div>
- </form>
- );
- }
-}
-
-SSOSignUpPage.defaultProps = {
- service: ''
-};
-SSOSignUpPage.propTypes = {
- intl: intlShape.isRequired,
- service: React.PropTypes.string
-};
-
-export default injectIntl(SSOSignUpPage); \ No newline at end of file
diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx
deleted file mode 100644
index d4eb60676..000000000
--- a/web/react/components/textbox.jsx
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AtMentionProvider from './suggestion/at_mention_provider.jsx';
-import CommandProvider from './suggestion/command_provider.jsx';
-import EmoticonProvider from './suggestion/emoticon_provider.jsx';
-import SuggestionList from './suggestion/suggestion_list.jsx';
-import SuggestionBox from './suggestion/suggestion_box.jsx';
-import ErrorStore from '../stores/error_store.jsx';
-
-import * as TextFormatting from '../utils/text_formatting.jsx';
-import * as Utils from '../utils/utils.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
-
-export default class Textbox extends React.Component {
- constructor(props) {
- super(props);
-
- this.focus = this.focus.bind(this);
- this.getStateFromStores = this.getStateFromStores.bind(this);
- this.onRecievedError = this.onRecievedError.bind(this);
- this.handleKeyPress = this.handleKeyPress.bind(this);
- this.handleKeyDown = this.handleKeyDown.bind(this);
- this.resize = this.resize.bind(this);
- this.showPreview = this.showPreview.bind(this);
-
- this.state = {
- connection: ''
- };
-
- this.suggestionProviders = [new AtMentionProvider(), new EmoticonProvider()];
- if (props.supportsCommands) {
- this.suggestionProviders.push(new CommandProvider());
- }
- }
-
- getStateFromStores() {
- const error = ErrorStore.getLastError();
-
- if (error) {
- return {message: error.message};
- }
-
- return {message: null};
- }
-
- componentDidMount() {
- ErrorStore.addChangeListener(this.onRecievedError);
-
- this.resize();
- }
-
- componentWillUnmount() {
- ErrorStore.removeChangeListener(this.onRecievedError);
- }
-
- onRecievedError() {
- const errorCount = ErrorStore.getConnectionErrorCount();
-
- if (errorCount > 1) {
- this.setState({connection: 'bad-connection'});
- } else {
- this.setState({connection: ''});
- }
- }
-
- componentDidUpdate() {
- this.resize();
- }
-
- handleKeyPress(e) {
- this.props.onKeyPress(e);
- }
-
- handleKeyDown(e) {
- if (this.props.onKeyDown) {
- this.props.onKeyDown(e);
- }
- }
-
- focus() {
- this.refs.message.getTextbox().focus();
- }
-
- resize() {
- const textbox = this.refs.message.getTextbox();
- const $textbox = $(textbox);
- const $wrapper = $(ReactDOM.findDOMNode(this.refs.wrapper));
-
- const padding = parseInt($textbox.css('padding-bottom'), 10) + parseInt($textbox.css('padding-top'), 10);
- const borders = parseInt($textbox.css('border-bottom-width'), 10) + parseInt($textbox.css('border-top-width'), 10);
- const maxHeight = parseInt($textbox.css('max-height'), 10) - borders;
-
- // set the height to auto and remove the scrollbar so we can get the actual size of the contents
- $textbox.css('height', 'auto').css('overflow-y', 'hidden');
-
- let height = textbox.scrollHeight - padding;
-
- if (height + padding > maxHeight) {
- height = maxHeight - padding;
-
- // turn scrollbar on and move over attachment icon to compensate for that
- $textbox.css('overflow-y', 'scroll');
- $wrapper.closest('.post-body__cell').addClass('scroll');
- } else {
- $wrapper.closest('.post-body__cell').removeClass('scroll');
- }
-
- // set the textarea to be the proper height
- $textbox.height(height);
-
- // set the wrapper height to match the height of the textbox including padding and borders
- $wrapper.height(height + padding + borders);
-
- if (this.state.preview) {
- $(ReactDOM.findDOMNode(this.refs.preview)).height(height + borders);
- }
- }
-
- showPreview(e) {
- e.preventDefault();
- e.target.blur();
- this.setState({preview: !this.state.preview});
- this.resize();
- }
-
- render() {
- const hasText = this.props.messageText.length > 0;
-
- let previewLink = null;
- if (Utils.isFeatureEnabled(PreReleaseFeatures.MARKDOWN_PREVIEW)) {
- previewLink = (
- <a
- onClick={this.showPreview}
- className='textbox-preview-link'
- >
- {this.state.preview ? (
- <FormattedMessage
- id='textbox.edit'
- defaultMessage='Edit message'
- />
- ) : (
- <FormattedMessage
- id='textbox.preview'
- defaultMessage='Preview'
- />
- )}
- </a>
- );
- }
-
- let helpText = (
- <div
- style={{visibility: hasText ? 'visible' : 'hidden', opacity: hasText ? '0.5' : '0'}}
- className='help_format_text'
- >
- <b>
- <FormattedMessage
- id='textbox.bold'
- defaultMessage='**bold**'
- />
- </b>
- <i>
- <FormattedMessage
- id='textbox.italic'
- defaultMessage='_italic_'
- />
- </i>
- <span>~~<strike>
- <FormattedMessage
- id='textbox.strike'
- defaultMessage='strike'
- />
- </strike>~~ </span>
- <code>
- <FormattedMessage
- id='textbox.inlinecode'
- defaultMessage='`inline code`'
- />
- </code>
- <code>
- <FormattedMessage
- id='textbox.preformatted'
- defaultMessage='```preformatted```'
- />
- </code>
- <span>
- <FormattedMessage
- id='textbox.quote'
- defaultMessage='>quote'
- />
- </span>
- </div>
- );
-
- return (
- <div
- ref='wrapper'
- className='textarea-wrapper'
- >
- <SuggestionBox
- id={this.props.id}
- ref='message'
- className={`form-control custom-textarea ${this.state.connection}`}
- type='textarea'
- spellCheck='true'
- autoComplete='off'
- autoCorrect='off'
- rows='1'
- maxLength={Constants.MAX_POST_LEN}
- placeholder={this.props.createMessage}
- value={this.props.messageText}
- onUserInput={this.props.onUserInput}
- onKeyPress={this.handleKeyPress}
- onKeyDown={this.handleKeyDown}
- style={{visibility: this.state.preview ? 'hidden' : 'visible'}}
- listComponent={SuggestionList}
- providers={this.suggestionProviders}
- />
- <div
- ref='preview'
- className='form-control custom-textarea textbox-preview-area'
- style={{display: this.state.preview ? 'block' : 'none'}}
- dangerouslySetInnerHTML={{__html: this.state.preview ? TextFormatting.formatText(this.props.messageText) : ''}}
- >
- </div>
- {helpText}
- <div className='help__text'>
- {previewLink}
- <a
- target='_blank'
- href='http://docs.mattermost.com/help/getting-started/messaging-basics.html'
- className='textbox-help-link'
- >
- <FormattedMessage
- id='textbox.help'
- defaultMessage='Help'
- />
- </a>
- </div>
- </div>
- );
- }
-}
-
-Textbox.defaultProps = {
- supportsCommands: true
-};
-
-Textbox.propTypes = {
- id: React.PropTypes.string.isRequired,
- channelId: React.PropTypes.string,
- messageText: React.PropTypes.string.isRequired,
- onUserInput: React.PropTypes.func.isRequired,
- onKeyPress: React.PropTypes.func.isRequired,
- createMessage: React.PropTypes.string.isRequired,
- onKeyDown: React.PropTypes.func,
- supportsCommands: React.PropTypes.bool.isRequired
-};
diff --git a/web/react/components/time_since.jsx b/web/react/components/time_since.jsx
deleted file mode 100644
index d5891cc57..000000000
--- a/web/react/components/time_since.jsx
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../utils/constants.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedRelative, FormattedDate} from 'mm-intl';
-
-var Tooltip = ReactBootstrap.Tooltip;
-var OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-export default class TimeSince extends React.Component {
- componentDidMount() {
- this.intervalId = setInterval(() => {
- this.forceUpdate();
- }, Constants.TIME_SINCE_UPDATE_INTERVAL);
- }
- componentWillUnmount() {
- clearInterval(this.intervalId);
- }
- render() {
- if (this.props.sameUser) {
- return (
- <time className='post__time'>
- {Utils.displayTimeFormatted(this.props.eventTime)}
- </time>
- );
- }
-
- const tooltip = (
- <Tooltip id={'time-since-tooltip-' + this.props.eventTime}>
- <FormattedDate
- value={this.props.eventTime}
- month='long'
- day='numeric'
- year='numeric'
- hour12={true}
- hour='numeric'
- minute='2-digit'
- />
- </Tooltip>
- );
-
- return (
- <OverlayTrigger
- delayShow={Constants.OVERLAY_TIME_DELAY}
- placement='top'
- overlay={tooltip}
- >
- <time className='post__time'>
- <FormattedRelative value={this.props.eventTime}/>
- </time>
- </OverlayTrigger>
- );
- }
-}
-
-TimeSince.defaultProps = {
- eventTime: 0,
- sameUser: false
-};
-
-TimeSince.propTypes = {
- eventTime: React.PropTypes.number.isRequired,
- sameUser: React.PropTypes.bool
-};
diff --git a/web/react/components/toggle_modal_button.jsx b/web/react/components/toggle_modal_button.jsx
deleted file mode 100644
index 10f9a4736..000000000
--- a/web/react/components/toggle_modal_button.jsx
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-export default class ModalToggleButton extends React.Component {
- constructor(props) {
- super(props);
-
- this.show = this.show.bind(this);
- this.hide = this.hide.bind(this);
-
- this.state = {
- show: false
- };
- }
-
- show() {
- this.setState({show: true});
- }
-
- hide() {
- this.setState({show: false});
- }
-
- render() {
- const {children, dialogType, dialogProps, onClick, ...props} = this.props; // eslint-disable-line no-use-before-define
-
- // allow callers to provide an onClick which will be called before the modal is shown
- let clickHandler = this.show;
- if (onClick) {
- clickHandler = () => {
- onClick();
-
- this.show();
- };
- }
-
- // this assumes that all modals will have a show property and an onHide event
- const dialog = React.createElement(this.props.dialogType, Object.assign({}, dialogProps, {
- show: this.state.show,
- onHide: () => {
- this.hide();
-
- if (dialogProps.onHide) {
- dialogProps.onHide();
- }
- }
- }));
-
- // nesting the dialog in the anchor tag looks like it shouldn't work, but it does due to how react-bootstrap
- // renders modals at the top level of the DOM instead of where you specify in the virtual DOM
- return (
- <a
- {...props}
- href='#'
- onClick={clickHandler}
- >
- {children}
- {dialog}
- </a>
- );
- }
-}
-
-ModalToggleButton.propTypes = {
- children: React.PropTypes.node.isRequired,
- dialogType: React.PropTypes.func.isRequired,
- dialogProps: React.PropTypes.object,
- onClick: React.PropTypes.func
-};
-
-ModalToggleButton.defaultProps = {
- dialogProps: {}
-};
diff --git a/web/react/components/tutorial/tutorial_intro_screens.jsx b/web/react/components/tutorial/tutorial_intro_screens.jsx
deleted file mode 100644
index 45ebceb28..000000000
--- a/web/react/components/tutorial/tutorial_intro_screens.jsx
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../../stores/user_store.jsx';
-import ChannelStore from '../../stores/channel_store.jsx';
-import TeamStore from '../../stores/team_store.jsx';
-import PreferenceStore from '../../stores/preference_store.jsx';
-import * as Utils from '../../utils/utils.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import Constants from '../../utils/constants.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const Preferences = Constants.Preferences;
-
-const NUM_SCREENS = 3;
-
-export default class TutorialIntroScreens extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleNext = this.handleNext.bind(this);
- this.createScreen = this.createScreen.bind(this);
- this.createCircles = this.createCircles.bind(this);
-
- this.state = {currentScreen: 0};
- }
- handleNext() {
- if (this.state.currentScreen < 2) {
- this.setState({currentScreen: this.state.currentScreen + 1});
- return;
- }
-
- Utils.switchChannel(ChannelStore.getByName(Constants.DEFAULT_CHANNEL));
-
- let preference = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
-
- const newValue = (parseInt(preference.value, 10) + 1).toString();
-
- preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), newValue);
- AsyncClient.savePreferences([preference]);
- }
- componentDidMount() {
- $('.tutorials__scroll').perfectScrollbar();
- }
- skipTutorial(e) {
- e.preventDefault();
- const preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), '999');
- AsyncClient.savePreferences([preference]);
- }
- createScreen() {
- switch (this.state.currentScreen) {
- case 0:
- return this.createScreenOne();
- case 1:
- return this.createScreenTwo();
- case 2:
- return this.createScreenThree();
- }
- return null;
- }
- createScreenOne() {
- const circles = this.createCircles();
-
- return (
- <div>
- <FormattedHTMLMessage
- id='tutorial_intro.screenOne'
- defaultMessage='<h3>Welcome to:</h3>
- <h1>Mattermost</h1>
- <p>Your team communication all in one place, instantly searchable and available anywhere</p>
- <p>Keep your team connected to help them achieve what matters most.</p>'
- />
- {circles}
- </div>
- );
- }
- createScreenTwo() {
- const circles = this.createCircles();
-
- return (
- <div>
- <FormattedHTMLMessage
- id='tutorial_intro.screenTwo'
- defaultMessage='<h3>How Mattermost works:</h3>
- <p>Communication happens in public discussion channels, private groups and direct messages.</p>
- <p>Everything is archived and searchable from any web-enabled desktop, laptop or phone.</p>'
- />
- {circles}
- </div>
- );
- }
- createScreenThree() {
- const team = TeamStore.getCurrent();
- let inviteModalLink;
- if (team.type === Constants.INVITE_TEAM) {
- inviteModalLink = (
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#invite_member'
- >
- <FormattedMessage
- id='tutorial_intro.invite'
- defaultMessage='Invite teammates'
- />
- </a>
- );
- } else {
- inviteModalLink = (
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#get_link'
- data-title='Team Invite'
- data-value={Utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + team.id}
- >
- <FormattedMessage
- id='tutorial_intro.teamInvite'
- defaultMessage='Team Invite'
- />
- </a>
- );
- }
-
- const circles = this.createCircles();
-
- let supportInfo = null;
- if (global.window.mm_config.SupportEmail) {
- supportInfo = (
- <p>
- <FormattedMessage
- id='tutorial_intro.support'
- defaultMessage='Need anything, just email us at '
- />
- <a
- href={'mailto:' + global.window.mm_config.SupportEmail}
- target='_blank'
- >
- {global.window.mm_config.SupportEmail}
- </a>
- {'.'}
- </p>
- );
- }
-
- return (
- <div>
- <h3>
- <FormattedMessage
- id='tutorial_intro.allSet'
- defaultMessage='You’re all set'
- />
- </h3>
- <p>
- {inviteModalLink}
- <FormattedMessage
- id='tutorial_intro.whenReady'
- defaultMessage=' when you’re ready.'
- />
- </p>
- {supportInfo}
- <FormattedMessage
- id='tutorial_intro.end'
- defaultMessage='Click “Next” to enter Town Square. This is the first channel teammates see when they sign up. Use it for posting updates everyone needs to know.'
- />
- {circles}
- </div>
- );
- }
- createCircles() {
- const circles = [];
- for (let i = 0; i < NUM_SCREENS; i++) {
- let className = 'circle';
- if (i === this.state.currentScreen) {
- className += ' active';
- }
-
- circles.push(
- <a
- href='#'
- key={'circle' + i}
- className={className}
- onClick={(e) => { //eslint-disable-line no-loop-func
- e.preventDefault();
- this.setState({currentScreen: i});
- }}
- />
- );
- }
-
- return (
- <div className='tutorial__circles'>
- {circles}
- </div>
- );
- }
- render() {
- const height = Utils.windowHeight() - 100;
- const screen = this.createScreen();
-
- return (
- <div
- className='tutorials__scroll'
- style={{height}}
- >
- <div className='tutorial-steps__container'>
- <div className='tutorial__content'>
- <div className='tutorial__steps'>
- {screen}
- <div className='tutorial__footer'>
- <button
- className='btn btn-primary'
- tabIndex='1'
- onClick={this.handleNext}
- >
- <FormattedMessage
- id='tutorial_intro.next'
- defaultMessage='Next'
- />
- </button>
- <a
- className='tutorial-skip'
- href='#'
- onClick={this.skipTutorial}
- >
- <FormattedMessage
- id='tutorial_intro.skip'
- defaultMessage='Skip tutorial'
- />
- </a>
- </div>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
diff --git a/web/react/components/tutorial/tutorial_tip.jsx b/web/react/components/tutorial/tutorial_tip.jsx
deleted file mode 100644
index 6bd7d89a4..000000000
--- a/web/react/components/tutorial/tutorial_tip.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 PreferenceStore from '../../stores/preference_store.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import Constants from '../../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const Preferences = Constants.Preferences;
-
-const Overlay = ReactBootstrap.Overlay;
-
-export default class TutorialTip extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleNext = this.handleNext.bind(this);
- this.toggle = this.toggle.bind(this);
-
- this.state = {currentScreen: 0, show: false};
- }
- toggle() {
- const show = !this.state.show;
- this.setState({show});
-
- if (!show && this.state.currentScreen >= this.props.screens.length - 1) {
- let preference = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
-
- const newValue = (parseInt(preference.value, 10) + 1).toString();
-
- preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), newValue);
- AsyncClient.savePreferences([preference]);
- }
- }
- handleNext() {
- if (this.state.currentScreen < this.props.screens.length - 1) {
- this.setState({currentScreen: this.state.currentScreen + 1});
- return;
- }
-
- this.toggle();
- }
- skipTutorial(e) {
- e.preventDefault();
- const preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), '999');
- AsyncClient.savePreferences([preference]);
- }
- render() {
- const buttonText = this.state.currentScreen === this.props.screens.length - 1 ? (
- <FormattedMessage
- id='tutorial_tip.ok'
- defaultMessage='Okay'
- />
- ) : (
- <FormattedMessage
- id='tutorial_tip.next'
- defaultMessage='Next'
- />
- );
-
- const dots = [];
- if (this.props.screens.length > 1) {
- for (let i = 0; i < this.props.screens.length; i++) {
- let className = 'circle';
- if (i === this.state.currentScreen) {
- className += ' active';
- }
-
- dots.push(
- <a
- href='#'
- key={'dotactive' + i}
- className={className}
- onClick={(e) => { //eslint-disable-line no-loop-func
- e.preventDefault();
- this.setState({currentScreen: i});
- }}
- />
- );
- }
- }
-
- var tipColor = '';
- if (this.props.overlayClass === 'tip-overlay--header' || this.props.overlayClass === 'tip-overlay--sidebar') {
- tipColor = 'White';
- }
-
- return (
- <div className={'tip-div ' + this.props.overlayClass}>
- <img
- className='tip-button'
- src={'/static/images/tutorialTip' + tipColor + '.gif'}
- width='35'
- onClick={this.toggle}
- ref='target'
- />
-
- <Overlay
- show={this.state.show}
- >
- <div className='tip-backdrop'/>
- </Overlay>
-
- <Overlay
- placement={this.props.placement}
- show={this.state.show}
- rootClose={true}
- onHide={this.toggle}
- target={() => this.refs.target}
- >
- <div className={'tip-overlay ' + this.props.overlayClass}>
- <div className='arrow'></div>
- {this.props.screens[this.state.currentScreen]}
- <div className='tutorial__footer'>
- <div className='tutorial__circles'>{dots}</div>
- <div className='text-right'>
- <button
- className='btn btn-primary'
- onClick={this.handleNext}
- >
- {buttonText}
- </button>
- <div className='tip-opt'>
- <FormattedMessage
- id='tutorial_tip.seen'
- defaultMessage='Seen this before? '
- />
- <a
- href='#'
- onClick={this.skipTutorial}
- >
- <FormattedMessage
- id='tutorial_tip.out'
- defaultMessage='Opt out of these tips.'
- />
- </a>
- </div>
- </div>
- </div>
- </div>
- </Overlay>
- </div>
- );
- }
-}
-
-TutorialTip.defaultProps = {
- overlayClass: ''
-};
-
-TutorialTip.propTypes = {
- screens: React.PropTypes.array.isRequired,
- placement: React.PropTypes.string.isRequired,
- overlayClass: React.PropTypes.string
-};
diff --git a/web/react/components/unread_channel_indicator.jsx b/web/react/components/unread_channel_indicator.jsx
deleted file mode 100644
index f897cdea3..000000000
--- a/web/react/components/unread_channel_indicator.jsx
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-// Indicator for the left sidebar which indicate if there's unread posts in a channel that is not shown
-// because it is either above or below the screen
-export default class UnreadChannelIndicator extends React.Component {
- render() {
- let displayValue = 'none';
- if (this.props.show) {
- displayValue = 'block';
- }
- return (
- <div
- className={'nav-pills__unread-indicator ' + this.props.extraClass}
- style={{display: displayValue}}
- >
- {this.props.text}
- </div>
- );
- }
-}
-
-UnreadChannelIndicator.defaultProps = {
- show: false,
- extraClass: '',
- text: ''
-};
-UnreadChannelIndicator.propTypes = {
- show: React.PropTypes.bool,
- extraClass: React.PropTypes.string,
- text: React.PropTypes.object
-};
diff --git a/web/react/components/user_list.jsx b/web/react/components/user_list.jsx
deleted file mode 100644
index 783d552dc..000000000
--- a/web/react/components/user_list.jsx
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-import UserListRow from './user_list_row.jsx';
-
-export default class UserList extends React.Component {
- render() {
- const users = this.props.users;
-
- let content;
- if (users.length > 0) {
- content = users.map((user) => {
- return (
- <UserListRow
- key={user.id}
- user={user}
- actions={this.props.actions}
- />
- );
- });
- } else {
- content = (
- <div key='no-users-found'>
- <FormattedMessage
- id='user_list.notFound'
- defaultMessage='No users found :('
- />
- </div>
- );
- }
-
- return (
- <div>
- {content}
- </div>
- );
- }
-}
-
-UserList.defaultProps = {
- users: [],
- actions: []
-};
-
-UserList.propTypes = {
- users: React.PropTypes.arrayOf(React.PropTypes.object),
- actions: React.PropTypes.arrayOf(React.PropTypes.func)
-};
diff --git a/web/react/components/user_list_row.jsx b/web/react/components/user_list_row.jsx
deleted file mode 100644
index 8c664ba82..000000000
--- a/web/react/components/user_list_row.jsx
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../utils/constants.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-export default function UserListRow({user, actions}) {
- const nameFormat = PreferenceStore.get(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', '');
-
- let name = user.username;
- if (user.nickname && nameFormat === Constants.Preferences.DISPLAY_PREFER_NICKNAME) {
- name = `${user.nickname} (@${user.username})`;
- } else if ((user.first_name || user.last_name) && (nameFormat === Constants.Preferences.DISPLAY_PREFER_NICKNAME || nameFormat === Constants.Preferences.DISPLAY_PREFER_FULL_NAME)) {
- name = `${Utils.getFullName(user)} (@${user.username})`;
- }
-
- const buttons = actions.map((Action, index) => {
- return (
- <Action
- key={index.toString()}
- user={user}
- />
- );
- });
-
- return (
- <div
- key={user.id}
- className='more-modal__row'
- >
- <img
- className='more-modal__image'
- width='38'
- height='38'
- src={`/api/v1/users/${user.id}/image?time=${user.update_at}`}
- />
- <div
- className='more-modal__details'
- >
- <div className='more-modal__name'>
- {name}
- </div>
- <div className='more-modal__description'>
- {user.email}
- </div>
- </div>
- <div
- className='more-modal__actions'
- >
- {buttons}
- </div>
- </div>
- );
-}
-
-UserListRow.defaultProps = {
- actions: []
-};
-
-UserListRow.propTypes = {
- user: React.PropTypes.object.isRequired,
- actions: React.PropTypes.arrayOf(React.PropTypes.func)
-};
diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx
deleted file mode 100644
index e7a286b77..000000000
--- a/web/react/components/user_profile.jsx
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-var Popover = ReactBootstrap.Popover;
-var OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-var id = 0;
-
-function nextId() {
- id = id + 1;
- return id;
-}
-
-export default class UserProfile extends React.Component {
- constructor(props) {
- super(props);
- this.uniqueId = nextId();
- }
- componentDidMount() {
- if (!this.props.disablePopover) {
- $('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'});
- }
- }
- render() {
- let name = '...';
- let email = '';
- let profileImg = '';
- if (this.props.user) {
- name = Utils.displayUsername(this.props.user.id);
- email = this.props.user.email;
- profileImg = '/api/v1/users/' + this.props.user.id + '/image?time=' + this.props.user.update_at;
- }
-
- if (this.props.overwriteName) {
- name = this.props.overwriteName;
- }
-
- if (this.props.overwriteImage) {
- profileImg = this.props.overwriteImage;
- }
-
- if (this.props.disablePopover) {
- return <div>{name}</div>;
- }
-
- var dataContent = [];
- dataContent.push(
- <img
- className='user-popover__image'
- src={profileImg}
- height='128'
- width='128'
- key='user-popover-image'
- />
- );
-
- if (!global.window.mm_config.ShowEmailAddress === 'true') {
- dataContent.push(
- <div
- className='text-nowrap'
- key='user-popover-no-email'
- >
- <FormattedMessage
- id='user_profile.notShared'
- defaultMessage='Email not shared'
- />
- </div>
- );
- } else {
- dataContent.push(
- <div
- data-toggle='tooltip'
- title={email}
- key='user-popover-email'
- >
- <a
- href={'mailto:' + email}
- className='text-nowrap text-lowercase user-popover__email'
- >
- {email}
- </a>
- </div>
- );
- }
-
- return (
- <OverlayTrigger
- trigger='click'
- placement='right'
- rootClose={true}
- overlay={
- <Popover
- title={name}
- id='user-profile-popover'
- >
- {dataContent}
- </Popover>
- }
- >
- <div
- className='user-popover'
- id={'profile_' + this.uniqueId}
- >
- {name}
- </div>
- </OverlayTrigger>
- );
- }
-}
-
-UserProfile.defaultProps = {
- user: {},
- overwriteName: '',
- overwriteImage: '',
- disablePopover: false
-};
-UserProfile.propTypes = {
- user: React.PropTypes.object,
- overwriteName: React.PropTypes.string,
- overwriteImage: React.PropTypes.string,
- disablePopover: React.PropTypes.bool
-};
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);
diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx
deleted file mode 100644
index 713fbf80b..000000000
--- a/web/react/components/view_image.jsx
+++ /dev/null
@@ -1,422 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-import * as Utils from '../utils/utils.jsx';
-import AudioVideoPreview from './audio_video_preview.jsx';
-import Constants from '../utils/constants.jsx';
-import FileInfoPreview from './file_info_preview.jsx';
-import FileStore from '../stores/file_store.jsx';
-import ViewImagePopoverBar from './view_image_popover_bar.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-const KeyCodes = Constants.KeyCodes;
-
-const holders = defineMessages({
- loading: {
- id: 'view_image.loading',
- defaultMessage: 'Loading '
- }
-});
-
-class ViewImageModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.showImage = this.showImage.bind(this);
- this.loadImage = this.loadImage.bind(this);
-
- this.handleNext = this.handleNext.bind(this);
- this.handlePrev = this.handlePrev.bind(this);
- this.handleKeyPress = this.handleKeyPress.bind(this);
-
- this.onModalShown = this.onModalShown.bind(this);
- this.onModalHidden = this.onModalHidden.bind(this);
-
- this.onFileStoreChange = this.onFileStoreChange.bind(this);
-
- this.getPublicLink = this.getPublicLink.bind(this);
- this.onMouseEnterImage = this.onMouseEnterImage.bind(this);
- this.onMouseLeaveImage = this.onMouseLeaveImage.bind(this);
-
- this.state = {
- imgId: this.props.startId,
- fileInfo: null,
- imgHeight: '100%',
- loaded: Utils.fillArray(false, this.props.filenames.length),
- progress: Utils.fillArray(0, this.props.filenames.length),
- showFooter: false
- };
- }
-
- handleNext(e) {
- if (e) {
- e.stopPropagation();
- }
- let id = this.state.imgId + 1;
- if (id > this.props.filenames.length - 1) {
- id = 0;
- }
- this.showImage(id);
- }
-
- handlePrev(e) {
- if (e) {
- e.stopPropagation();
- }
- let id = this.state.imgId - 1;
- if (id < 0) {
- id = this.props.filenames.length - 1;
- }
- this.showImage(id);
- }
-
- handleKeyPress(e) {
- if (e.keyCode === KeyCodes.RIGHT) {
- this.handleNext();
- } else if (e.keyCode === KeyCodes.LEFT) {
- this.handlePrev();
- }
- }
-
- onModalShown(nextProps) {
- $(window).on('keyup', this.handleKeyPress);
-
- this.showImage(nextProps.startId);
-
- FileStore.addChangeListener(this.onFileStoreChange);
- }
-
- onModalHidden() {
- $(window).off('keyup', this.handleKeyPress);
-
- if (this.refs.video) {
- this.refs.video.stop();
- }
-
- FileStore.removeChangeListener(this.onFileStoreChange);
- }
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.show === true && this.props.show === false) {
- this.onModalShown(nextProps);
- } else if (nextProps.show === false && this.props.show === true) {
- this.onModalHidden();
- }
-
- if (!Utils.areObjectsEqual(this.props.filenames, nextProps.filenames)) {
- this.setState({
- loaded: Utils.fillArray(false, nextProps.filenames.length),
- progress: Utils.fillArray(0, nextProps.filenames.length)
- });
- }
- }
-
- onFileStoreChange(filename) {
- const id = this.props.filenames.indexOf(filename);
-
- if (id !== -1) {
- if (id === this.state.imgId) {
- this.setState({
- fileInfo: FileStore.getInfo(filename)
- });
- }
-
- if (!this.state.loaded[id]) {
- this.loadImage(id, filename);
- }
- }
- }
-
- showImage(id) {
- this.setState({imgId: id});
-
- const imgHeight = $(window).height() - 100;
- this.setState({imgHeight});
-
- const filename = this.props.filenames[id];
-
- if (!FileStore.hasInfo(filename)) {
- // the image will actually be loaded once we know what we need to load
- AsyncClient.getFileInfo(filename);
- return;
- }
-
- this.setState({
- fileInfo: FileStore.getInfo(filename)
- });
-
- if (!this.state.loaded[id]) {
- this.loadImage(id, filename);
- }
- }
-
- loadImage(id, filename) {
- const fileInfo = FileStore.getInfo(filename);
- const fileType = Utils.getFileType(fileInfo.extension);
-
- if (fileType === 'image') {
- let previewUrl;
- if (fileInfo.has_image_preview) {
- previewUrl = Utils.getPreviewImagePath(filename);
- } else {
- // some images (eg animated gifs) just show the file itself and not a preview
- previewUrl = Utils.getFileUrl(filename);
- }
-
- const img = new Image();
- img.load(
- previewUrl,
- () => {
- const progress = this.state.progress;
- progress[id] = img.completedPercentage;
- this.setState({progress});
- }
- );
- img.onload = () => {
- const loaded = this.state.loaded;
- loaded[id] = true;
- this.setState({loaded});
- };
- } else {
- // there's nothing to load for non-image files
- var loaded = this.state.loaded;
- loaded[id] = true;
- this.setState({loaded});
- }
- }
-
- getPublicLink() {
- var data = {};
- data.channel_id = this.props.channelId;
- data.user_id = this.props.userId;
- data.filename = this.props.filenames[this.state.imgId];
- Client.getPublicLink(
- data,
- (serverData) => {
- if (Utils.isMobile()) {
- window.location.href = serverData.public_link;
- } else {
- window.open(serverData.public_link);
- }
- },
- () => {
- //Do Nothing on error
- }
- );
- }
-
- onMouseEnterImage() {
- this.setState({showFooter: true});
- }
-
- onMouseLeaveImage() {
- this.setState({showFooter: false});
- }
-
- render() {
- if (this.props.filenames.length < 1 || this.props.filenames.length - 1 < this.state.imgId) {
- return <div/>;
- }
-
- const filename = this.props.filenames[this.state.imgId];
- const fileUrl = Utils.getFileUrl(filename, true);
-
- var content;
- if (this.state.loaded[this.state.imgId]) {
- // this.state.fileInfo is for the current image and we shoudl have it before we load the image
- const fileInfo = this.state.fileInfo;
- const fileType = Utils.getFileType(fileInfo.extension);
-
- if (fileType === 'image') {
- content = (
- <ImagePreview
- filename={filename}
- fileUrl={fileUrl}
- fileInfo={fileInfo}
- maxHeight={this.state.imgHeight}
- />
- );
- } else if (fileType === 'video' || fileType === 'audio') {
- content = (
- <AudioVideoPreview
- filename={filename}
- fileUrl={fileUrl}
- fileInfo={this.state.fileInfo}
- maxHeight={this.state.imgHeight}
- formatMessage={this.props.intl.formatMessage}
- />
- );
- } else {
- content = (
- <FileInfoPreview
- filename={filename}
- fileUrl={fileUrl}
- fileInfo={fileInfo}
- formatMessage={this.props.intl.formatMessage}
- />
- );
- }
- } else {
- // display a progress indicator when the preview for an image is still loading
- const progress = Math.floor(this.state.progress[this.state.imgId]);
-
- content = (
- <LoadingImagePreview
- progress={progress}
- loading={this.props.intl.formatMessage(holders.loading)}
- />
- );
- }
-
- let leftArrow = null;
- let rightArrow = null;
- if (this.props.filenames.length > 1) {
- leftArrow = (
- <a
- ref='previewArrowLeft'
- className='modal-prev-bar'
- href='#'
- onClick={this.handlePrev}
- >
- <i className='image-control image-prev'/>
- </a>
- );
-
- rightArrow = (
- <a
- ref='previewArrowRight'
- className='modal-next-bar'
- href='#'
- onClick={this.handleNext}
- >
- <i className='image-control image-next'/>
- </a>
- );
- }
-
- let closeButtonClass = 'modal-close';
- if (this.state.showFooter) {
- closeButtonClass += ' modal-close--show';
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.props.onModalDismissed}
- className='image_modal'
- dialogClassName='modal-image'
- >
- <Modal.Body
- modalClassName='image-body'
- onClick={this.props.onModalDismissed}
- >
- <div
- className={'image-wrapper'}
- onClick={this.props.onModalDismissed}
- >
- <div
- onMouseEnter={this.onMouseEnterImage}
- onMouseLeave={this.onMouseLeaveImage}
- onClick={(e) => e.stopPropagation()}
- >
- <div
- className={closeButtonClass}
- onClick={this.props.onModalDismissed}
- />
- {content}
- <ViewImagePopoverBar
- show={this.state.showFooter}
- fileId={this.state.imgId}
- totalFiles={this.props.filenames.length}
- filename={name}
- fileURL={fileUrl}
- getPublicLink={this.getPublicLink}
- />
- </div>
- </div>
- {leftArrow}
- {rightArrow}
- </Modal.Body>
- </Modal>
- );
- }
-}
-
-ViewImageModal.defaultProps = {
- show: false,
- filenames: [],
- channelId: '',
- userId: '',
- startId: 0
-};
-ViewImageModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- onModalDismissed: React.PropTypes.func.isRequired,
- filenames: React.PropTypes.array,
- modalId: React.PropTypes.string,
- channelId: React.PropTypes.string,
- userId: React.PropTypes.string,
- startId: React.PropTypes.number
-};
-
-function LoadingImagePreview({progress, loading}) {
- let progressView = null;
- if (progress) {
- progressView = (
- <span className='loader-percent'>
- {loading + progress + '%'}
- </span>
- );
- }
-
- return (
- <div className='view-image__loading'>
- <img
- className='loader-image'
- src='/static/images/load.gif'
- />
- {progressView}
- </div>
- );
-}
-
-LoadingImagePreview.propTypes = {
- progress: React.PropTypes.number,
- loading: React.PropTypes.string
-};
-
-function ImagePreview({filename, fileUrl, fileInfo, maxHeight}) {
- let previewUrl;
- if (fileInfo.has_preview_image) {
- previewUrl = Utils.getPreviewImagePath(filename);
- } else {
- previewUrl = fileUrl;
- }
-
- return (
- <a
- href={fileUrl}
- target='_blank'
- download={true}
- >
- <img
- style={{maxHeight}}
- src={previewUrl}
- />
- </a>
- );
-}
-
-ImagePreview.propTypes = {
- filename: React.PropTypes.string.isRequired,
- fileUrl: React.PropTypes.string.isRequired,
- fileInfo: React.PropTypes.object.isRequired,
- maxHeight: React.PropTypes.number.isRequired
-};
-
-export default injectIntl(ViewImageModal);
diff --git a/web/react/components/view_image_popover_bar.jsx b/web/react/components/view_image_popover_bar.jsx
deleted file mode 100644
index 18be5a3c5..000000000
--- a/web/react/components/view_image_popover_bar.jsx
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class ViewImagePopoverBar extends React.Component {
- render() {
- var publicLink = '';
- if (global.window.mm_config.EnablePublicLink === 'true') {
- publicLink = (
- <div>
- <a
- href='#'
- className='public-link text'
- data-title='Public Image'
- onClick={this.props.getPublicLink}
- >
- <FormattedMessage
- id='view_image_popover.publicLink'
- defaultMessage='Get Public Link'
- />
- </a>
- <span className='text'>{' | '}</span>
- </div>
- );
- }
-
- var footerClass = 'modal-button-bar';
- if (this.props.show) {
- footerClass += ' footer--show';
- }
-
- return (
- <div
- ref='imageFooter'
- className={footerClass}
- >
- <span className='pull-left text'>
- <FormattedMessage
- id='view_image_popover.file'
- defaultMessage='File {count} of {total}'
- values={{
- count: (this.props.fileId + 1),
- total: this.props.totalFiles
- }}
- />
- </span>
- <div className='image-links'>
- {publicLink}
- <a
- href={this.props.fileURL}
- download={this.props.filename}
- className='text'
- target='_blank'
- >
- <FormattedMessage
- id='view_image_popover.download'
- defaultMessage='Download'
- />
- </a>
- </div>
- </div>
- );
- }
-}
-ViewImagePopoverBar.defaultProps = {
- show: false,
- imgId: 0,
- totalFiles: 0,
- filename: '',
- fileURL: ''
-};
-
-ViewImagePopoverBar.propTypes = {
- show: React.PropTypes.bool.isRequired,
- fileId: React.PropTypes.number.isRequired,
- totalFiles: React.PropTypes.number.isRequired,
- filename: React.PropTypes.string.isRequired,
- fileURL: React.PropTypes.string.isRequired,
- getPublicLink: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/youtube_video.jsx b/web/react/components/youtube_video.jsx
deleted file mode 100644
index fae846afb..000000000
--- a/web/react/components/youtube_video.jsx
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChannelStore from '../stores/channel_store.jsx';
-
-const ytRegex = /(?:http|https):\/\/(?:www\.)?(?:(?:youtube\.com\/(?:(?:v\/)|(\/u\/\w\/)|(?:(?:watch|embed\/watch)(?:\/|.*v=))|(?:embed\/)|(?:user\/[^\/]+\/u\/[0-9]\/)))|(?:youtu\.be\/))([^#\&\?]*)/;
-
-export default class YoutubeVideo extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateStateFromProps = this.updateStateFromProps.bind(this);
- this.handleReceivedMetadata = this.handleReceivedMetadata.bind(this);
-
- this.play = this.play.bind(this);
- this.stop = this.stop.bind(this);
- this.stopOnChannelChange = this.stopOnChannelChange.bind(this);
-
- this.state = {
- playing: false,
- title: ''
- };
- }
-
- componentWillMount() {
- this.updateStateFromProps(this.props);
- }
-
- componentWillReceiveProps(nextProps) {
- this.updateStateFromProps(nextProps);
- }
-
- updateStateFromProps(props) {
- const link = props.link;
-
- const match = link.trim().match(ytRegex);
- if (!match || match[2].length !== 11) {
- return;
- }
-
- this.setState({
- videoId: match[2],
- time: this.handleYoutubeTime(link)
- });
- }
-
- handleYoutubeTime(link) {
- const timeRegex = /[\\?&]t=([0-9hms]+)/;
-
- const time = link.match(timeRegex);
- if (!time || !time[1]) {
- return '';
- }
-
- const hours = time[1].match(/([0-9]+)h/);
- const minutes = time[1].match(/([0-9]+)m/);
- const seconds = time[1].match(/([0-9]+)s/);
-
- let ticks = 0;
-
- if (hours && hours[1]) {
- ticks += parseInt(hours[1], 10) * 3600;
- }
-
- if (minutes && minutes[1]) {
- ticks += parseInt(minutes[1], 10) * 60;
- }
-
- if (seconds && seconds[1]) {
- ticks += parseInt(seconds[1], 10);
- }
-
- return '&start=' + ticks.toString();
- }
-
- componentDidMount() {
- if (global.window.mm_config.GoogleDeveloperKey) {
- $.ajax({
- async: true,
- url: 'https://www.googleapis.com/youtube/v3/videos',
- type: 'GET',
- data: {part: 'snippet', id: this.state.videoId, key: global.window.mm_config.GoogleDeveloperKey},
- success: this.handleReceivedMetadata
- });
- }
- }
-
- handleReceivedMetadata(data) {
- if (!data.items.length || !data.items[0].snippet) {
- return null;
- }
- var metadata = data.items[0].snippet;
- this.setState({
- receivedYoutubeData: true,
- title: metadata.title
- });
- return null;
- }
-
- play() {
- this.setState({playing: true});
-
- if (ChannelStore.getCurrentId() === this.props.channelId) {
- ChannelStore.addChangeListener(this.stopOnChannelChange);
- }
- }
-
- stop() {
- this.setState({playing: false});
- }
-
- stopOnChannelChange() {
- if (ChannelStore.getCurrentId() !== this.props.channelId) {
- this.stop();
- }
- }
-
- render() {
- let header = 'Youtube';
- if (this.state.title) {
- header = header + ' - ';
- }
-
- let content;
- if (this.state.playing) {
- content = (
- <iframe
- src={'https://www.youtube.com/embed/' + this.state.videoId + '?autoplay=1&autohide=1&border=0&wmode=opaque&fs=1&enablejsapi=1' + this.state.time}
- width='480px'
- height='360px'
- type='text/html'
- frameBorder='0'
- allowFullScreen='allowfullscreen'
- />
- );
- } else {
- content = (
- <div className='embed-responsive embed-responsive-4by3 video-div__placeholder'>
- <div className='video-thumbnail__container'>
- <img
- className='video-thumbnail'
- src={'https://i.ytimg.com/vi/' + this.state.videoId + '/hqdefault.jpg'}
- />
- <div className='block'>
- <span className='play-button'><span/></span>
- </div>
- </div>
- </div>
- );
- }
-
- return (
- <div>
- <h4>
- <span className='video-type'>{header}</span>
- <span className='video-title'><a href={this.props.link}>{this.state.title}</a></span>
- </h4>
- <div
- className='video-div embed-responsive-item'
- onClick={this.play}
- >
- {content}
- </div>
- </div>
- );
- }
-
- static isYoutubeLink(link) {
- return link.trim().match(ytRegex);
- }
-}
-
-YoutubeVideo.propTypes = {
- channelId: React.PropTypes.string.isRequired,
- link: React.PropTypes.string.isRequired
-};