summaryrefslogtreecommitdiffstats
path: root/webapp
diff options
context:
space:
mode:
Diffstat (limited to 'webapp')
-rw-r--r--webapp/components/access_history_modal.jsx8
-rw-r--r--webapp/components/activity_log_modal.jsx8
-rw-r--r--webapp/components/admin_console/admin_sidebar.jsx467
-rw-r--r--webapp/components/channel_header.jsx1
-rw-r--r--webapp/components/claim/claim.jsx (renamed from webapp/components/claim/claim_account.jsx)56
-rw-r--r--webapp/components/claim/components/email_to_ldap.jsx169
-rw-r--r--webapp/components/claim/components/email_to_oauth.jsx (renamed from webapp/components/claim/email_to_sso.jsx)61
-rw-r--r--webapp/components/claim/components/ldap_to_email.jsx167
-rw-r--r--webapp/components/claim/components/oauth_to_email.jsx (renamed from webapp/components/claim/sso_to_email.jsx)61
-rw-r--r--webapp/components/edit_post_modal.jsx24
-rw-r--r--webapp/components/invite_member_modal.jsx7
-rw-r--r--webapp/components/logged_in.jsx5
-rw-r--r--webapp/components/popover_list_members.jsx6
-rw-r--r--webapp/components/rhs_thread.jsx4
-rw-r--r--webapp/components/search_bar.jsx1
-rw-r--r--webapp/components/search_results.jsx3
-rw-r--r--webapp/components/sidebar.jsx3
-rw-r--r--webapp/components/suggestion/suggestion_box.jsx27
-rw-r--r--webapp/components/team_settings_modal.jsx8
-rw-r--r--webapp/components/textbox.jsx74
-rw-r--r--webapp/components/tutorial/tutorial_intro_screens.jsx6
-rw-r--r--webapp/components/tutorial/tutorial_tip.jsx12
-rw-r--r--webapp/components/user_list.jsx15
-rw-r--r--webapp/components/user_settings/user_settings_modal.jsx22
-rw-r--r--webapp/components/user_settings/user_settings_security.jsx45
-rw-r--r--webapp/i18n/en.json54
-rw-r--r--webapp/i18n/es.json54
-rw-r--r--webapp/i18n/pt.json30
-rw-r--r--webapp/package.json7
-rw-r--r--webapp/root.jsx33
-rw-r--r--webapp/sass/components/_buttons.scss10
-rw-r--r--webapp/sass/components/_modal.scss1
-rw-r--r--webapp/sass/components/_search.scss16
-rw-r--r--webapp/sass/components/_suggestion-list.scss1
-rw-r--r--webapp/sass/layout/_headers.scss4
-rw-r--r--webapp/sass/layout/_navigation.scss2
-rw-r--r--webapp/sass/layout/_post.scss2
-rw-r--r--webapp/sass/layout/_sidebar-left.scss1
-rw-r--r--webapp/sass/responsive/_mobile.scss113
-rw-r--r--webapp/sass/routes/_admin-console.scss17
-rw-r--r--webapp/stores/browser_store.jsx47
-rw-r--r--webapp/stores/error_store.jsx12
-rw-r--r--webapp/stores/search_store.jsx40
-rw-r--r--webapp/stores/user_store.jsx50
-rw-r--r--webapp/utils/client.jsx50
-rw-r--r--webapp/utils/constants.jsx1
-rw-r--r--webapp/utils/utils.jsx2
47 files changed, 1104 insertions, 703 deletions
diff --git a/webapp/components/access_history_modal.jsx b/webapp/components/access_history_modal.jsx
index 94a10c97f..9c49c3879 100644
--- a/webapp/components/access_history_modal.jsx
+++ b/webapp/components/access_history_modal.jsx
@@ -2,7 +2,6 @@
// See License.txt for license information.
import $ from 'jquery';
-import ReactDOM from 'react-dom';
import {Modal} from 'react-bootstrap';
import LoadingScreen from './loading_screen.jsx';
import AuditTable from './audit_table.jsx';
@@ -36,11 +35,8 @@ class AccessHistoryModal extends React.Component {
}
onShow() {
AsyncClient.getAudits();
-
- if ($(window).width() > 768) {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
- } else {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 150);
+ if (!Utils.isMobile()) {
+ $('.modal-body').perfectScrollbar();
}
}
onHide() {
diff --git a/webapp/components/activity_log_modal.jsx b/webapp/components/activity_log_modal.jsx
index 9a4ff3ef2..f1dd4a26a 100644
--- a/webapp/components/activity_log_modal.jsx
+++ b/webapp/components/activity_log_modal.jsx
@@ -2,7 +2,6 @@
// See License.txt for license information.
import $ from 'jquery';
-import ReactDOM from 'react-dom';
import UserStore from 'stores/user_store.jsx';
import * as Client from 'utils/client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
@@ -56,11 +55,8 @@ export default class ActivityLogModal extends React.Component {
}
onShow() {
AsyncClient.getSessions();
-
- if ($(window).width() > 768) {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
- } else {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 150);
+ if (!Utils.isMobile()) {
+ $('.modal-body').perfectScrollbar();
}
}
onHide() {
diff --git a/webapp/components/admin_console/admin_sidebar.jsx b/webapp/components/admin_console/admin_sidebar.jsx
index 8ee75e2ef..9f9e85de1 100644
--- a/webapp/components/admin_console/admin_sidebar.jsx
+++ b/webapp/components/admin_console/admin_sidebar.jsx
@@ -1,8 +1,11 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+import $ from 'jquery';
+
import AdminSidebarHeader from './admin_sidebar_header.jsx';
import SelectTeamModal from './select_team_modal.jsx';
+import * as Utils from 'utils/utils.jsx';
import {FormattedMessage} from 'react-intl';
@@ -27,6 +30,12 @@ export default class AdminSidebar extends React.Component {
};
}
+ componentDidUpdate() {
+ if (!Utils.isMobile()) {
+ $('.sidebar--left .nav-pills__container').perfectScrollbar();
+ }
+ }
+
handleClick(name, teamId, e) {
e.preventDefault();
this.props.selectTab(name, teamId);
@@ -242,244 +251,242 @@ export default class AdminSidebar extends React.Component {
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)}
- >
+ <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.privacy'
- defaultMessage='Privacy Settings'
+ id='admin.sidebar.reports'
+ defaultMessage='SITE REPORTS'
/>
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('gitlab_settings')}
- onClick={this.handleClick.bind(this, 'gitlab_settings', null)}
- >
+ </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.gitlab'
- defaultMessage='GitLab Settings'
+ id='admin.sidebar.settings'
+ defaultMessage='SETTINGS'
/>
- </a>
- </li>
- {ldapSettings}
- {complianceSettings}
- <li>
- <a
- href='#'
- className={this.isSelected('legal_and_support_settings')}
- onClick={this.handleClick.bind(this, 'legal_and_support_settings', null)}
- >
+ </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}
+ {complianceSettings}
+ <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.support'
- defaultMessage='Legal and Support Settings'
+ id='admin.sidebar.teams'
+ defaultMessage='TEAMS ({count})'
+ values={{
+ count: count
+ }}
/>
- </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)}
- >
+ </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.logs'
- defaultMessage='Logs'
+ id='admin.sidebar.other'
+ defaultMessage='OTHER'
/>
- </a>
- </li>
- </ul>
- </li>
- </ul>
- </div>
+ </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>
<SelectTeamModal
diff --git a/webapp/components/channel_header.jsx b/webapp/components/channel_header.jsx
index 7cd713942..369fa2dbb 100644
--- a/webapp/components/channel_header.jsx
+++ b/webapp/components/channel_header.jsx
@@ -80,6 +80,7 @@ export default class ChannelHeader extends React.Component {
SearchStore.addSearchChangeListener(this.onListenerChange);
PreferenceStore.addChangeListener(this.onListenerChange);
UserStore.addChangeListener(this.onListenerChange);
+ $('.sidebar--left .dropdown-menu').perfectScrollbar();
}
componentWillUnmount() {
ChannelStore.removeChangeListener(this.onListenerChange);
diff --git a/webapp/components/claim/claim_account.jsx b/webapp/components/claim/claim.jsx
index b6495e283..464187c37 100644
--- a/webapp/components/claim/claim_account.jsx
+++ b/webapp/components/claim/claim.jsx
@@ -1,16 +1,14 @@
// 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 React from 'react';
import {FormattedMessage} from 'react-intl';
-import React from 'react';
import logoImage from 'images/logo.png';
-export default class ClaimAccount extends React.Component {
+export default class Claim extends React.Component {
constructor(props) {
super(props);
@@ -39,7 +37,7 @@ export default class ClaimAccount extends React.Component {
const team = TeamStore.getByName(this.state.teamName);
let displayName = '';
if (team) {
- displayName = team.displayName;
+ displayName = team.display_name;
}
this.setState({
teamDisplayName: displayName
@@ -49,39 +47,6 @@ export default class ClaimAccount extends React.Component {
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'>
@@ -99,7 +64,13 @@ export default class ClaimAccount extends React.Component {
src={logoImage}
/>
<div id='claim'>
- {content}
+ {React.cloneElement(this.props.children, {
+ teamName: this.state.teamName,
+ teamDisplayName: this.state.teamDisplayName,
+ currentType: this.state.oldType,
+ newType: this.state.newType,
+ email: this.state.email
+ })}
</div>
</div>
</div>
@@ -108,9 +79,10 @@ export default class ClaimAccount extends React.Component {
}
}
-ClaimAccount.defaultProps = {
+Claim.defaultProps = {
};
-ClaimAccount.propTypes = {
+Claim.propTypes = {
params: React.PropTypes.object.isRequired,
- location: React.PropTypes.object.isRequired
+ location: React.PropTypes.object.isRequired,
+ children: React.PropTypes.node
};
diff --git a/webapp/components/claim/components/email_to_ldap.jsx b/webapp/components/claim/components/email_to_ldap.jsx
new file mode 100644
index 000000000..f3046fa74
--- /dev/null
+++ b/webapp/components/claim/components/email_to_ldap.jsx
@@ -0,0 +1,169 @@
+// 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 React from 'react';
+import ReactDOM from 'react-dom';
+import {FormattedMessage} from 'react-intl';
+
+export default class EmailToLDAP extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.submit = this.submit.bind(this);
+
+ this.state = {};
+ }
+ submit(e) {
+ e.preventDefault();
+ var state = {};
+
+ const password = ReactDOM.findDOMNode(this.refs.password).value.trim();
+ if (!password) {
+ state.error = Utils.localizeMessage('claim.email_to_ldap.pwdError', 'Please enter your password.');
+ this.setState(state);
+ return;
+ }
+
+ const ldapId = ReactDOM.findDOMNode(this.refs.ldapid).value.trim();
+ if (!ldapId) {
+ state.error = Utils.localizeMessage('claim.email_to_ldap.ldapIdError', 'Please enter your LDAP ID.');
+ this.setState(state);
+ return;
+ }
+
+ const ldapPassword = ReactDOM.findDOMNode(this.refs.ldappassword).value.trim();
+ if (!ldapPassword) {
+ state.error = Utils.localizeMessage('claim.email_to_ldap.ldapPasswordError', 'Please enter your LDAP password.');
+ this.setState(state);
+ return;
+ }
+
+ state.error = null;
+ this.setState(state);
+
+ var postData = {};
+ postData.email_password = password;
+ postData.ldap_id = ldapId;
+ postData.ldap_password = ldapPassword;
+ postData.email = this.props.email;
+ postData.team_name = this.props.teamName;
+
+ Client.emailToLDAP(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';
+ }
+
+ return (
+ <div>
+ <h3>
+ <FormattedMessage
+ id='claim.email_to_ldap.title'
+ defaultMessage='Switch Email/Password Account to LDAP'
+ />
+ </h3>
+ <form onSubmit={this.submit}>
+ <p>
+ <FormattedMessage
+ id='claim.email_to_ldap.ssoType'
+ defaultMessage='Upon claiming your account, you will only be able to login with LDAP'
+ />
+ </p>
+ <p>
+ <FormattedMessage
+ id='claim.email_to_ldap.ssoNote'
+ defaultMessage='You must already have a valid LDAP account'
+ />
+ </p>
+ <p>
+ <FormattedMessage
+ id='claim.email_to_ldap.enterPwd'
+ defaultMessage='Enter the password for your {team} {site} email 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={Utils.localizeMessage('claim.email_to_ldap.pwd', 'Password')}
+ spellCheck='false'
+ />
+ </div>
+ <p>
+ <FormattedMessage
+ id='claim.email_to_ldap.enterLdapPwd'
+ defaultMessage='Enter the ID and password for your LDAP account'
+ values={{
+ team: this.props.teamDisplayName,
+ site: global.window.mm_config.SiteName
+ }}
+ />
+ </p>
+ <div className={formClass}>
+ <input
+ type='text'
+ className='form-control'
+ name='ldapId'
+ ref='ldapid'
+ placeholder={Utils.localizeMessage('claim.email_to_ldap.ldapId', 'LDAP ID')}
+ spellCheck='false'
+ />
+ </div>
+ <div className={formClass}>
+ <input
+ type='password'
+ className='form-control'
+ name='ldapPassword'
+ ref='ldappassword'
+ placeholder={Utils.localizeMessage('claim.email_to_ldap.ldapPwd', 'LDAP Password')}
+ spellCheck='false'
+ />
+ </div>
+ {error}
+ <button
+ type='submit'
+ className='btn btn-primary'
+ >
+ <FormattedMessage
+ id='claim.email_to_ldap.switchTo'
+ defaultMessage='Switch account to LDAP'
+ />
+ </button>
+ </form>
+ </div>
+ );
+ }
+}
+
+EmailToLDAP.defaultProps = {
+};
+EmailToLDAP.propTypes = {
+ email: React.PropTypes.string,
+ teamName: React.PropTypes.string,
+ teamDisplayName: React.PropTypes.string
+};
diff --git a/webapp/components/claim/email_to_sso.jsx b/webapp/components/claim/components/email_to_oauth.jsx
index d09449247..f3e370a4a 100644
--- a/webapp/components/claim/email_to_sso.jsx
+++ b/webapp/components/claim/components/email_to_oauth.jsx
@@ -1,26 +1,14 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-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'
- }
-});
-
import React from 'react';
+import ReactDOM from 'react-dom';
+import {FormattedMessage} from 'react-intl';
-class EmailToSSO extends React.Component {
+export default class EmailToOAuth extends React.Component {
constructor(props) {
super(props);
@@ -34,7 +22,7 @@ class EmailToSSO extends React.Component {
var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
if (!password) {
- state.error = this.props.intl.formatMessage(holders.pwdError);
+ state.error = Utils.localizeMessage('claim.email_to_oauth.pwdError', 'Please enter your password.');
this.setState(state);
return;
}
@@ -46,9 +34,9 @@ class EmailToSSO extends React.Component {
postData.password = password;
postData.email = this.props.email;
postData.team_name = this.props.teamName;
- postData.service = this.props.type;
+ postData.service = this.props.newType;
- Client.switchToSSO(postData,
+ Client.emailToOAuth(postData,
(data) => {
if (data.follow_link) {
window.location.href = data.follow_link;
@@ -70,41 +58,41 @@ class EmailToSSO extends React.Component {
formClass += ' has-error';
}
- const uiType = Utils.toTitleCase(this.props.type) + ' SSO';
+ const uiType = Utils.toTitleCase(this.props.newType) + ' SSO';
return (
<div>
<h3>
<FormattedMessage
- id='claim.email_to_sso.title'
+ id='claim.email_to_oauth.title'
defaultMessage='Switch Email/Password Account to {uiType}'
values={{
- uiType: uiType
+ uiType
}}
/>
</h3>
<form onSubmit={this.submit}>
<p>
<FormattedMessage
- id='claim.email_to_sso.ssoType'
+ id='claim.email_to_oauth.ssoType'
defaultMessage='Upon claiming your account, you will only be able to login with {type} SSO'
values={{
- type: Utils.toTitleCase(this.props.type)
+ type: Utils.toTitleCase(this.props.newType)
}}
/>
</p>
<p>
<FormattedMessage
- id='claim.email_to_sso.ssoNote'
+ id='claim.email_to_oauth.ssoNote'
defaultMessage='You must already have a valid {type} account'
values={{
- type: Utils.toTitleCase(this.props.type)
+ type: Utils.toTitleCase(this.props.newType)
}}
/>
</p>
<p>
<FormattedMessage
- id='claim.email_to_sso.enterPwd'
+ id='claim.email_to_oauth.enterPwd'
defaultMessage='Enter the password for your {team} {site} account'
values={{
team: this.props.teamDisplayName,
@@ -118,7 +106,7 @@ class EmailToSSO extends React.Component {
className='form-control'
name='password'
ref='password'
- placeholder={this.props.intl.formatMessage(holders.pwd)}
+ placeholder={Utils.localizeMessage('claim.email_to_oauth.pwd', 'Password')}
spellCheck='false'
/>
</div>
@@ -128,10 +116,10 @@ class EmailToSSO extends React.Component {
className='btn btn-primary'
>
<FormattedMessage
- id='claim.email_to_sso.switchTo'
+ id='claim.email_to_oauth.switchTo'
defaultMessage='Switch account to {uiType}'
values={{
- uiType: uiType
+ uiType
}}
/>
</button>
@@ -141,14 +129,11 @@ class EmailToSSO extends React.Component {
}
}
-EmailToSSO.defaultProps = {
+EmailToOAuth.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
+EmailToOAuth.propTypes = {
+ newType: React.PropTypes.string,
+ email: React.PropTypes.string,
+ teamName: React.PropTypes.string,
+ teamDisplayName: React.PropTypes.string
};
-
-export default injectIntl(EmailToSSO);
diff --git a/webapp/components/claim/components/ldap_to_email.jsx b/webapp/components/claim/components/ldap_to_email.jsx
new file mode 100644
index 000000000..b4ffd4944
--- /dev/null
+++ b/webapp/components/claim/components/ldap_to_email.jsx
@@ -0,0 +1,167 @@
+// 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 React from 'react';
+import ReactDOM from 'react-dom';
+import {FormattedMessage} from 'react-intl';
+
+export default class LDAPToEmail extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.submit = this.submit.bind(this);
+
+ this.state = {};
+ }
+ submit(e) {
+ e.preventDefault();
+ var state = {};
+
+ const password = ReactDOM.findDOMNode(this.refs.password).value.trim();
+ if (!password) {
+ state.error = Utils.localizeMessage('claim.ldap_to_email.pwdError', 'Please enter your password.');
+ this.setState(state);
+ return;
+ }
+
+ const confirmPassword = ReactDOM.findDOMNode(this.refs.passwordconfirm).value.trim();
+ if (!confirmPassword || password !== confirmPassword) {
+ state.error = Utils.localizeMessage('claim.ldap_to_email.pwdNotMatch', 'Passwords do not match.');
+ this.setState(state);
+ return;
+ }
+
+ const ldapPassword = ReactDOM.findDOMNode(this.refs.ldappassword).value.trim();
+ if (!ldapPassword) {
+ state.error = Utils.localizeMessage('claim.ldap_to_email.ldapPasswordError', 'Please enter your LDAP password.');
+ this.setState(state);
+ return;
+ }
+
+ state.error = null;
+ this.setState(state);
+
+ var postData = {};
+ postData.email_password = password;
+ postData.ldap_password = ldapPassword;
+ postData.email = this.props.email;
+ postData.team_name = this.props.teamName;
+
+ Client.ldapToEmail(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';
+ }
+
+ return (
+ <div>
+ <h3>
+ <FormattedMessage
+ id='claim.ldap_to_email.title'
+ defaultMessage='Switch LDAP Account to Email/Password'
+ />
+ </h3>
+ <form onSubmit={this.submit}>
+ <p>
+ <FormattedMessage
+ id='claim.ldap_to_email.ssoType'
+ defaultMessage='Upon claiming your account, you will only be able to login with your email and password'
+ />
+ </p>
+ <p>
+ <FormattedMessage
+ id='claim.ldap_to_email.email'
+ defaultMessage='You will use the email {email} to login'
+ values={{
+ email: this.props.email
+ }}
+ />
+ </p>
+ <p>
+ <FormattedMessage
+ id='claim.ldap_to_email.enterLdapPwd'
+ defaultMessage='Enter your LDAP password for your {team} {site} email account'
+ values={{
+ team: this.props.teamDisplayName,
+ site: global.window.mm_config.SiteName
+ }}
+ />
+ </p>
+ <div className={formClass}>
+ <input
+ type='password'
+ className='form-control'
+ name='ldapPassword'
+ ref='ldappassword'
+ placeholder={Utils.localizeMessage('claim.ldap_to_email.ldapPwd', 'LDAP Password')}
+ spellCheck='false'
+ />
+ </div>
+ <p>
+ <FormattedMessage
+ id='claim.ldap_to_email.enterPwd'
+ defaultMessage='Enter a new password for your email account'
+ />
+ </p>
+ <div className={formClass}>
+ <input
+ type='password'
+ className='form-control'
+ name='password'
+ ref='password'
+ placeholder={Utils.localizeMessage('claim.ldap_to_email.pwd', 'Password')}
+ spellCheck='false'
+ />
+ </div>
+ <div className={formClass}>
+ <input
+ type='password'
+ className='form-control'
+ name='passwordconfirm'
+ ref='passwordconfirm'
+ placeholder={Utils.localizeMessage('claim.ldap_to_email.confirm', 'Confirm Password')}
+ spellCheck='false'
+ />
+ </div>
+ {error}
+ <button
+ type='submit'
+ className='btn btn-primary'
+ >
+ <FormattedMessage
+ id='claim.ldap_to_email.switchTo'
+ defaultMessage='Switch account to email/password'
+ />
+ </button>
+ </form>
+ </div>
+ );
+ }
+}
+
+LDAPToEmail.defaultProps = {
+};
+LDAPToEmail.propTypes = {
+ email: React.PropTypes.string,
+ teamName: React.PropTypes.string,
+ teamDisplayName: React.PropTypes.string
+};
diff --git a/webapp/components/claim/sso_to_email.jsx b/webapp/components/claim/components/oauth_to_email.jsx
index a41e09969..476677aeb 100644
--- a/webapp/components/claim/sso_to_email.jsx
+++ b/webapp/components/claim/components/oauth_to_email.jsx
@@ -1,34 +1,14 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-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'
- }
-});
-
import React from 'react';
+import ReactDOM from 'react-dom';
+import {FormattedMessage} from 'react-intl';
-class SSOToEmail extends React.Component {
+export default class OAuthToEmail extends React.Component {
constructor(props) {
super(props);
@@ -37,20 +17,19 @@ class SSOToEmail extends React.Component {
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);
+ state.error = Utils.localizeMessage('claim.oauth_to_email.enterPwd', 'Please enter a password.');
this.setState(state);
return;
}
const confirmPassword = ReactDOM.findDOMNode(this.refs.passwordconfirm).value.trim();
if (!confirmPassword || password !== confirmPassword) {
- state.error = formatMessage(holders.pwdNotMatch);
+ state.error = Utils.localizeMessage('claim.oauth_to_email.pwdNotMatch', 'Password do not match.');
this.setState(state);
return;
}
@@ -63,7 +42,7 @@ class SSOToEmail extends React.Component {
postData.email = this.props.email;
postData.team_name = this.props.teamName;
- Client.switchToEmail(postData,
+ Client.oauthToEmail(postData,
(data) => {
if (data.follow_link) {
window.location.href = data.follow_link;
@@ -75,7 +54,6 @@ class SSOToEmail extends React.Component {
);
}
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>;
@@ -92,7 +70,7 @@ class SSOToEmail extends React.Component {
<div>
<h3>
<FormattedMessage
- id='claim.sso_to_email.title'
+ id='claim.oauth_to_email.title'
defaultMessage='Switch {type} Account to Email'
values={{
type: uiType
@@ -102,13 +80,13 @@ class SSOToEmail extends React.Component {
<form onSubmit={this.submit}>
<p>
<FormattedMessage
- id='claim.sso_to_email.description'
+ id='claim.oauth_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'
+ id='claim.oauth_to_email_newPwd'
defaultMessage='Enter a new password for your {team} {site} account'
values={{
team: this.props.teamDisplayName,
@@ -122,7 +100,7 @@ class SSOToEmail extends React.Component {
className='form-control'
name='password'
ref='password'
- placeholder={formatMessage(holders.newPwd)}
+ placeholder={Utils.localizeMessage('claim.oauth_to_email.newPwd', 'New Password')}
spellCheck='false'
/>
</div>
@@ -132,7 +110,7 @@ class SSOToEmail extends React.Component {
className='form-control'
name='passwordconfirm'
ref='passwordconfirm'
- placeholder={formatMessage(holders.confirm)}
+ placeholder={Utils.localizeMessage('claim.oauth_to_email.confirm', 'Confirm Password')}
spellCheck='false'
/>
</div>
@@ -142,7 +120,7 @@ class SSOToEmail extends React.Component {
className='btn btn-primary'
>
<FormattedMessage
- id='claim.sso_to_email.switchTo'
+ id='claim.oauth_to_email.switchTo'
defaultMessage='Switch {type} to email and password'
values={{
type: uiType
@@ -155,14 +133,11 @@ class SSOToEmail extends React.Component {
}
}
-SSOToEmail.defaultProps = {
+OAuthToEmail.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
+OAuthToEmail.propTypes = {
+ teamName: React.PropTypes.string,
+ teamDisplayName: React.PropTypes.string,
+ currentType: React.PropTypes.string,
+ email: React.PropTypes.string
};
-
-export default injectIntl(SSOToEmail);
diff --git a/webapp/components/edit_post_modal.jsx b/webapp/components/edit_post_modal.jsx
index 0a55b2968..caf9a0ee5 100644
--- a/webapp/components/edit_post_modal.jsx
+++ b/webapp/components/edit_post_modal.jsx
@@ -27,8 +27,8 @@ const holders = defineMessages({
import React from 'react';
class EditPostModal extends React.Component {
- constructor() {
- super();
+ constructor(props) {
+ super(props);
this.handleEdit = this.handleEdit.bind(this);
this.handleEditInput = this.handleEditInput.bind(this);
@@ -56,12 +56,13 @@ class EditPostModal extends React.Component {
updatedPost.id = this.state.post_id;
updatedPost.channel_id = this.state.channel_id;
- Client.updatePost(updatedPost,
- function success() {
+ Client.updatePost(
+ updatedPost,
+ () => {
AsyncClient.getPosts(updatedPost.channel_id);
window.scrollTo(0, 0);
},
- function error(err) {
+ (err) => {
AsyncClient.dispatchError(err, 'updatePost');
}
);
@@ -106,11 +107,11 @@ class EditPostModal extends React.Component {
componentDidMount() {
var self = this;
- $(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function onHidden() {
+ $(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', () => {
self.setState({editText: '', title: '', channel_id: '', post_id: '', comments: 0, refocusId: '', error: ''});
});
- $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function onShow(e) {
+ $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', (e) => {
var button = e.relatedTarget;
if (!button) {
return;
@@ -118,12 +119,11 @@ class EditPostModal extends React.Component {
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('shown.bs.modal', () => {
+ self.refs.editbox.focus();
});
- $(ReactDOM.findDOMNode(this.refs.modal)).on('hide.bs.modal', function onShown() {
+ $(ReactDOM.findDOMNode(this.refs.modal)).on('hide.bs.modal', () => {
if (self.state.refocusId !== '') {
setTimeout(() => {
$(self.state.refocusId).get(0).focus();
@@ -221,4 +221,4 @@ EditPostModal.propTypes = {
intl: intlShape.isRequired
};
-export default injectIntl(EditPostModal); \ No newline at end of file
+export default injectIntl(EditPostModal);
diff --git a/webapp/components/invite_member_modal.jsx b/webapp/components/invite_member_modal.jsx
index d567183ac..1f8fd6133 100644
--- a/webapp/components/invite_member_modal.jsx
+++ b/webapp/components/invite_member_modal.jsx
@@ -1,7 +1,6 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
@@ -176,12 +175,6 @@ class InviteMemberModal extends React.Component {
});
}
- componentDidUpdate(prevProps, prevState) {
- if (!prevState.show && this.state.show) {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
- }
- }
-
addInviteFields() {
var count = this.state.idCount + 1;
var inviteIds = this.state.inviteIds;
diff --git a/webapp/components/logged_in.jsx b/webapp/components/logged_in.jsx
index 7ddb6b83d..c6f7b50b1 100644
--- a/webapp/components/logged_in.jsx
+++ b/webapp/components/logged_in.jsx
@@ -6,6 +6,7 @@ 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 ChannelStore from 'stores/channel_store.jsx';
+import BrowserStore from 'stores/browser_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
@@ -84,7 +85,7 @@ export default class LoggedIn extends React.Component {
// 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)) {
+ if (BrowserStore.isSignallingLogout(e.originalEvent.newValue)) {
return;
}
@@ -94,7 +95,7 @@ export default class LoggedIn extends React.Component {
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)) {
+ if (BrowserStore.isSignallingLogin(e.originalEvent.newValue)) {
return;
}
diff --git a/webapp/components/popover_list_members.jsx b/webapp/components/popover_list_members.jsx
index 7d048019c..819c7f590 100644
--- a/webapp/components/popover_list_members.jsx
+++ b/webapp/components/popover_list_members.jsx
@@ -1,6 +1,8 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+import $ from 'jquery';
+
import UserStore from 'stores/user_store.jsx';
import {Popover, Overlay} from 'react-bootstrap';
import * as Utils from 'utils/utils.jsx';
@@ -20,6 +22,10 @@ export default class PopoverListMembers extends React.Component {
this.closePopover = this.closePopover.bind(this);
}
+ componentDidUpdate() {
+ $('.member-list__popover .popover-content').perfectScrollbar();
+ }
+
componentWillMount() {
this.setState({showPopover: false});
}
diff --git a/webapp/components/rhs_thread.jsx b/webapp/components/rhs_thread.jsx
index 2760765eb..bbd9f8b28 100644
--- a/webapp/components/rhs_thread.jsx
+++ b/webapp/components/rhs_thread.jsx
@@ -46,11 +46,15 @@ export default class RhsThread extends React.Component {
window.addEventListener('resize', this.handleResize);
this.mounted = true;
+ if (!Utils.isMobile()) {
+ $('.sidebar--right .post-right__scroll').perfectScrollbar();
+ }
}
componentDidUpdate() {
if ($('.post-right__scroll')[0]) {
$('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight);
}
+ $('.sidebar--right .post-right__scroll').perfectScrollbar('update');
this.resize();
}
componentWillUnmount() {
diff --git a/webapp/components/search_bar.jsx b/webapp/components/search_bar.jsx
index c8dbb9d3f..caaf0f844 100644
--- a/webapp/components/search_bar.jsx
+++ b/webapp/components/search_bar.jsx
@@ -184,6 +184,7 @@ class SearchBar extends React.Component {
onUserInput={this.handleUserInput}
listComponent={SearchSuggestionList}
providers={this.suggestionProviders}
+ type='search'
/>
{isSearching}
<Popover
diff --git a/webapp/components/search_results.jsx b/webapp/components/search_results.jsx
index 7619e41cd..c5baf50ef 100644
--- a/webapp/components/search_results.jsx
+++ b/webapp/components/search_results.jsx
@@ -61,6 +61,9 @@ export default class SearchResults extends React.Component {
UserStore.addChangeListener(this.onUserChange);
this.resize();
window.addEventListener('resize', this.handleResize);
+ if (!Utils.isMobile()) {
+ $('.sidebar--right .search-items-container').perfectScrollbar();
+ }
}
shouldComponentUpdate(nextProps, nextState) {
diff --git a/webapp/components/sidebar.jsx b/webapp/components/sidebar.jsx
index d8d825fd4..0e1b7dd0e 100644
--- a/webapp/components/sidebar.jsx
+++ b/webapp/components/sidebar.jsx
@@ -163,6 +163,9 @@ export default class Sidebar extends React.Component {
componentDidUpdate() {
this.updateTitle();
this.updateUnreadIndicators();
+ if (!Utils.isMobile()) {
+ $('.sidebar--left .nav-pills__container').perfectScrollbar();
+ }
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
diff --git a/webapp/components/suggestion/suggestion_box.jsx b/webapp/components/suggestion/suggestion_box.jsx
index e3ec63194..dbec024ac 100644
--- a/webapp/components/suggestion/suggestion_box.jsx
+++ b/webapp/components/suggestion/suggestion_box.jsx
@@ -3,11 +3,14 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
+
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';
+import TextareaAutosize from 'react-textarea-autosize';
+
const KeyCodes = Constants.KeyCodes;
import React from 'react';
@@ -44,7 +47,13 @@ export default class SuggestionBox extends React.Component {
getTextbox() {
// this is to support old code that looks at the input/textarea DOM nodes
- return ReactDOM.findDOMNode(this.refs.textbox);
+ let textbox = this.refs.textbox;
+
+ if (!(textbox instanceof HTMLElement)) {
+ textbox = ReactDOM.findDOMNode(textbox);
+ }
+
+ return textbox;
}
handleDocumentClick(e) {
@@ -130,9 +139,18 @@ export default class SuggestionBox extends React.Component {
{...newProps}
/>
);
+ } else if (this.props.type === 'search') {
+ textbox = (
+ <input
+ ref='textbox'
+ type='search'
+ {...newProps}
+ />
+ );
} else if (this.props.type === 'textarea') {
textbox = (
- <textarea
+ <TextareaAutosize
+ id={this.suggestionId}
ref='textbox'
{...newProps}
/>
@@ -156,12 +174,13 @@ SuggestionBox.defaultProps = {
SuggestionBox.propTypes = {
listComponent: React.PropTypes.func.isRequired,
- type: React.PropTypes.oneOf(['input', 'textarea']).isRequired,
+ type: React.PropTypes.oneOf(['input', 'textarea', 'search']).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
+ onKeyDown: React.PropTypes.func,
+ onHeightChange: React.PropTypes.func
};
diff --git a/webapp/components/team_settings_modal.jsx b/webapp/components/team_settings_modal.jsx
index 7dbbd680a..c19787993 100644
--- a/webapp/components/team_settings_modal.jsx
+++ b/webapp/components/team_settings_modal.jsx
@@ -5,6 +5,7 @@ import $ from 'jquery';
import ReactDOM from 'react-dom';
import SettingsSidebar from './settings_sidebar.jsx';
import TeamSettings from './team_settings.jsx';
+import * as Utils from 'utils/utils.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
@@ -49,9 +50,16 @@ class TeamSettingsModal extends React.Component {
$('.modal-dialog.display--content').removeClass('display--content');
}, 500);
});
+
+ if (!Utils.isMobile()) {
+ $('.settings-modal .settings-content').perfectScrollbar();
+ }
}
updateTab(tab) {
this.setState({activeTab: tab, activeSection: ''});
+ if (!Utils.isMobile()) {
+ $('.settings-modal .modal-body').scrollTop(0).perfectScrollbar('update');
+ }
}
updateSection(section) {
this.setState({activeSection: section});
diff --git a/webapp/components/textbox.jsx b/webapp/components/textbox.jsx
index 1a395072e..371c581e5 100644
--- a/webapp/components/textbox.jsx
+++ b/webapp/components/textbox.jsx
@@ -2,7 +2,6 @@
// See License.txt for license information.
import $ from 'jquery';
-import ReactDOM from 'react-dom';
import AtMentionProvider from './suggestion/at_mention_provider.jsx';
import CommandProvider from './suggestion/command_provider.jsx';
import EmoticonProvider from './suggestion/emoticon_provider.jsx';
@@ -29,7 +28,7 @@ export default class Textbox extends React.Component {
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.handleHeightChange = this.handleHeightChange.bind(this);
this.showPreview = this.showPreview.bind(this);
this.state = {
@@ -54,8 +53,6 @@ export default class Textbox extends React.Component {
componentDidMount() {
ErrorStore.addChangeListener(this.onRecievedError);
-
- this.resize();
}
componentWillUnmount() {
@@ -72,10 +69,6 @@ export default class Textbox extends React.Component {
}
}
- componentDidUpdate() {
- this.resize();
- }
-
handleKeyPress(e) {
this.props.onKeyPress(e);
}
@@ -86,50 +79,28 @@ export default class Textbox extends React.Component {
}
}
- focus() {
- this.refs.message.getTextbox().focus();
- }
+ handleHeightChange(height) {
+ const textbox = $(this.refs.message.getTextbox());
+ const wrapper = $(this.refs.wrapper);
- resize() {
- const textbox = this.refs.message.getTextbox();
- const $textbox = $(textbox);
- const $wrapper = $(ReactDOM.findDOMNode(this.refs.wrapper));
+ const maxHeight = parseInt(textbox.css('max-height'), 10);
- 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');
+ // move over attachment icon to compensate for the scrollbar
+ if (height > maxHeight) {
+ wrapper.closest('.post-body__cell').addClass('scroll');
} else {
- $wrapper.closest('.post-body__cell').removeClass('scroll');
+ 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);
- }
+ focus() {
+ this.refs.message.getTextbox().focus();
}
showPreview(e) {
e.preventDefault();
e.target.blur();
this.setState({preview: !this.state.preview});
- this.resize();
}
render() {
@@ -157,7 +128,7 @@ export default class Textbox extends React.Component {
);
}
- let helpText = (
+ const helpText = (
<div
style={{visibility: hasText ? 'visible' : 'hidden', opacity: hasText ? '0.5' : '0'}}
className='help_format_text'
@@ -174,12 +145,16 @@ export default class Textbox extends React.Component {
defaultMessage='_italic_'
/>
</i>
- <span>~~<strike>
- <FormattedMessage
- id='textbox.strike'
- defaultMessage='strike'
- />
- </strike>~~ </span>
+ <span>
+ {'~~'}
+ <strike>
+ <FormattedMessage
+ id='textbox.strike'
+ defaultMessage='strike'
+ />
+ </strike>
+ {'~~ '}
+ </span>
<code>
<FormattedMessage
id='textbox.inlinecode'
@@ -214,16 +189,17 @@ export default class Textbox extends React.Component {
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}
+ onHeightChange={this.handleHeightChange}
style={{visibility: this.state.preview ? 'hidden' : 'visible'}}
listComponent={SuggestionList}
providers={this.suggestionProviders}
+ channelId={this.props.channelId}
/>
<div
ref='preview'
diff --git a/webapp/components/tutorial/tutorial_intro_screens.jsx b/webapp/components/tutorial/tutorial_intro_screens.jsx
index 734842cad..913a30483 100644
--- a/webapp/components/tutorial/tutorial_intro_screens.jsx
+++ b/webapp/components/tutorial/tutorial_intro_screens.jsx
@@ -36,12 +36,12 @@ export default class TutorialIntroScreens extends React.Component {
Utils.switchChannel(ChannelStore.getByName(Constants.DEFAULT_CHANNEL));
- let step = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 0);
+ const step = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 0);
AsyncClient.savePreference(
Preferences.TUTORIAL_STEP,
UserStore.getCurrentId(),
- step + 1
+ (step + 1).toString()
);
}
skipTutorial(e) {
@@ -50,7 +50,7 @@ export default class TutorialIntroScreens extends React.Component {
AsyncClient.savePreference(
Preferences.TUTORIAL_STEP,
UserStore.getCurrentId(),
- 999
+ '999'
);
}
createScreen() {
diff --git a/webapp/components/tutorial/tutorial_tip.jsx b/webapp/components/tutorial/tutorial_tip.jsx
index d93fff1b1..3508e29a2 100644
--- a/webapp/components/tutorial/tutorial_tip.jsx
+++ b/webapp/components/tutorial/tutorial_tip.jsx
@@ -29,12 +29,12 @@ export default class TutorialTip extends React.Component {
this.setState({show});
if (!show && this.state.currentScreen >= this.props.screens.length - 1) {
- let step = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 0);
+ const step = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 0);
AsyncClient.savePreference(
Preferences.TUTORIAL_STEP,
UserStore.getCurrentId(),
- step + 1
+ (step + 1).toString()
);
}
}
@@ -48,8 +48,12 @@ export default class TutorialTip extends React.Component {
}
skipTutorial(e) {
e.preventDefault();
- const preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), '999');
- AsyncClient.savePreferences([preference]);
+
+ AsyncClient.savePreference(
+ Preferences.TUTORIAL_STEP,
+ UserStore.getCurrentId(),
+ '999'
+ );
}
render() {
const buttonText = this.state.currentScreen === this.props.screens.length - 1 ? (
diff --git a/webapp/components/user_list.jsx b/webapp/components/user_list.jsx
index b5d42decc..3652723be 100644
--- a/webapp/components/user_list.jsx
+++ b/webapp/components/user_list.jsx
@@ -24,11 +24,16 @@ export default class UserList extends React.Component {
});
} else {
content = (
- <div key='no-users-found'>
- <FormattedMessage
- id='user_list.notFound'
- defaultMessage='No users found :('
- />
+ <div
+ key='no-users-found'
+ className='no-channel-message'
+ >
+ <p className='primary-message'>
+ <FormattedMessage
+ id='user_list.notFound'
+ defaultMessage='No users found :('
+ />
+ </p>
</div>
);
}
diff --git a/webapp/components/user_settings/user_settings_modal.jsx b/webapp/components/user_settings/user_settings_modal.jsx
index bd1df6ea5..d1c1f0fe2 100644
--- a/webapp/components/user_settings/user_settings_modal.jsx
+++ b/webapp/components/user_settings/user_settings_modal.jsx
@@ -64,7 +64,6 @@ 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);
@@ -95,24 +94,13 @@ class UserSettingsModal extends React.Component {
}
componentDidMount() {
- if (this.props.show) {
- this.handleShow();
- }
UserStore.addChangeListener(this.onUserChanged);
}
- componentDidUpdate(prevProps) {
- if (this.props.show && !prevProps.show) {
- this.handleShow();
- }
+ componentDidUpdate() {
UserStore.removeChangeListener(this.onUserChanged);
- }
-
- handleShow() {
- if ($(window).width() > 768) {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
- } else {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 50);
+ if (!Utils.isMobile()) {
+ $('.settings-modal .modal-body').perfectScrollbar();
}
}
@@ -222,6 +210,10 @@ class UserSettingsModal extends React.Component {
active_section: ''
});
}
+
+ if (!Utils.isMobile()) {
+ $('.settings-modal .modal-body').scrollTop(0).perfectScrollbar('update');
+ }
}
updateSection(section, skipConfirm) {
diff --git a/webapp/components/user_settings/user_settings_security.jsx b/webapp/components/user_settings/user_settings_security.jsx
index e42de91ea..283d2c425 100644
--- a/webapp/components/user_settings/user_settings_security.jsx
+++ b/webapp/components/user_settings/user_settings_security.jsx
@@ -15,6 +15,7 @@ import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedTime, FormattedDate} from 'react-intl';
+import {Link} from 'react-router';
const holders = defineMessages({
currentPasswordError: {
@@ -268,17 +269,24 @@ class SecurityTab extends React.Component {
let emailOption;
if (global.window.mm_config.EnableSignUpWithEmail === 'true' && user.auth_service !== '') {
+ let link;
+ if (user.auth_service === Constants.LDAP_SERVICE) {
+ link = '/' + teamName + '/claim/ldap_to_email?email=' + encodeURIComponent(user.email);
+ } else {
+ link = '/' + teamName + '/claim/oauth_to_email?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service;
+ }
+
emailOption = (
<div>
- <a
+ <Link
className='btn btn-primary'
- href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service}
+ to={link}
>
<FormattedMessage
id='user.settings.security.switchEmail'
defaultMessage='Switch to using email and password'
/>
- </a>
+ </Link>
<br/>
</div>
);
@@ -288,15 +296,15 @@ class SecurityTab extends React.Component {
if (global.window.mm_config.EnableSignUpWithGitLab === 'true' && user.auth_service === '') {
gitlabOption = (
<div>
- <a
+ <Link
className='btn btn-primary'
- href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.GITLAB_SERVICE}
+ to={'/' + teamName + '/claim/email_to_oauth?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>
+ </Link>
<br/>
</div>
);
@@ -306,15 +314,33 @@ class SecurityTab extends React.Component {
if (global.window.mm_config.EnableSignUpWithGoogle === 'true' && user.auth_service === '') {
googleOption = (
<div>
- <a
+ <Link
className='btn btn-primary'
- href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.GOOGLE_SERVICE}
+ to={'/' + teamName + '/claim/email_to_oauth?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>
+ </Link>
+ <br/>
+ </div>
+ );
+ }
+
+ let ldapOption;
+ if (global.window.mm_config.EnableLdap === 'true' && user.auth_service === '') {
+ ldapOption = (
+ <div>
+ <Link
+ className='btn btn-primary'
+ to={'/' + teamName + '/claim/email_to_ldap?email=' + encodeURIComponent(user.email)}
+ >
+ <FormattedMessage
+ id='user.settings.security.switchLdap'
+ defaultMessage='Switch to using LDAP'
+ />
+ </Link>
<br/>
</div>
);
@@ -325,6 +351,7 @@ class SecurityTab extends React.Component {
{emailOption}
{gitlabOption}
<br/>
+ {ldapOption}
{googleOption}
</div>
);
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index d9411df07..00f4f333d 100644
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -633,21 +633,44 @@
"choose_auth_page.ldapCreate": "Create new team with LDAP Account",
"choose_auth_page.noSignup": "No sign-up methods configured, please contact your system administrator.",
"claim.account.noEmail": "No email specified",
- "claim.email_to_sso.enterPwd": "Enter the password for your {team} {site} account",
- "claim.email_to_sso.pwd": "Password",
- "claim.email_to_sso.pwdError": "Please enter your password.",
- "claim.email_to_sso.ssoNote": "You must already have a valid {type} account",
- "claim.email_to_sso.ssoType": "Upon claiming your account, you will only be able to login with {type} SSO",
- "claim.email_to_sso.switchTo": "Switch account to {uiType}",
- "claim.email_to_sso.title": "Switch Email/Password Account to {uiType}",
- "claim.sso_to_email.confirm": "Confirm Password",
- "claim.sso_to_email.description": "Upon changing your account type, you will only be able to login with your email and password.",
- "claim.sso_to_email.enterPwd": "Please enter a password.",
- "claim.sso_to_email.newPwd": "New Password",
- "claim.sso_to_email.pwdNotMatch": "Password do not match.",
- "claim.sso_to_email.switchTo": "Switch {type} to email and password",
- "claim.sso_to_email.title": "Switch {type} Account to Email",
- "claim.sso_to_email_newPwd": "Enter a new password for your {team} {site} account",
+ "claim.email_to_ldap.enterLdapPwd": "Enter the ID and password for your LDAP account",
+ "claim.email_to_ldap.enterPwd": "Enter the password for your {team} {site} email account",
+ "claim.email_to_ldap.ldapId": "LDAP ID",
+ "claim.email_to_ldap.ldapIdError": "Please enter your LDAP ID.",
+ "claim.email_to_ldap.ldapPasswordError": "Please enter your LDAP password.",
+ "claim.email_to_ldap.ldapPwd": "LDAP Password",
+ "claim.email_to_ldap.pwd": "Password",
+ "claim.email_to_ldap.pwdError": "Please enter your password.",
+ "claim.email_to_ldap.ssoNote": "You must already have a valid LDAP account",
+ "claim.email_to_ldap.ssoType": "Upon claiming your account, you will only be able to login with LDAP",
+ "claim.email_to_ldap.switchTo": "Switch account to LDAP",
+ "claim.email_to_ldap.title": "Switch Email/Password Account to LDAP",
+ "claim.email_to_oauth.enterPwd": "Enter the password for your {team} {site} account",
+ "claim.email_to_oauth.pwd": "Password",
+ "claim.email_to_oauth.pwdError": "Please enter your password.",
+ "claim.email_to_oauth.ssoNote": "You must already have a valid {type} account",
+ "claim.email_to_oauth.ssoType": "Upon claiming your account, you will only be able to login with {type} SSO",
+ "claim.email_to_oauth.switchTo": "Switch account to {uiType}",
+ "claim.email_to_oauth.title": "Switch Email/Password Account to {uiType}",
+ "claim.ldap_to_email.confirm": "Confirm Password",
+ "claim.ldap_to_email.email": "You will use the email {email} to login",
+ "claim.ldap_to_email.enterLdapPwd": "Enter your LDAP password for your {team} {site} email account",
+ "claim.ldap_to_email.enterPwd": "Enter a new password for your email account",
+ "claim.ldap_to_email.ldapPasswordError": "Please enter your LDAP password.",
+ "claim.ldap_to_email.ldapPwd": "LDAP Password",
+ "claim.ldap_to_email.pwd": "Password",
+ "claim.ldap_to_email.pwdError": "Please enter your password.",
+ "claim.ldap_to_email.pwdNotMatch": "Passwords do not match.",
+ "claim.ldap_to_email.ssoType": "Upon claiming your account, you will only be able to login with your email and password",
+ "claim.ldap_to_email.switchTo": "Switch account to email/password",
+ "claim.ldap_to_email.title": "Switch LDAP Account to Email/Password",
+ "claim.oauth_to_email.confirm": "Confirm Password",
+ "claim.oauth_to_email.description": "Upon changing your account type, you will only be able to login with your email and password.",
+ "claim.oauth_to_email.enterPwd": "Please enter a password.",
+ "claim.oauth_to_email.newPwd": "New Password",
+ "claim.oauth_to_email.pwdNotMatch": "Password do not match.",
+ "claim.oauth_to_email.switchTo": "Switch {type} to email and password",
+ "claim.oauth_to_email.title": "Switch {type} Account to Email",
"confirm_modal.cancel": "Cancel",
"create_comment.addComment": "Add a comment...",
"create_comment.comment": "Add Comment",
@@ -1335,6 +1358,7 @@
"user.settings.security.switchEmail": "Switch to using email and password",
"user.settings.security.switchGitlab": "Switch to using GitLab SSO",
"user.settings.security.switchGoogle": "Switch to using Google SSO",
+ "user.settings.security.switchLda": "Switch to using LDAP",
"user.settings.security.title": "Security Settings",
"user.settings.security.viewHistory": "View Access History",
"user_list.notFound": "No users found :(",
diff --git a/webapp/i18n/es.json b/webapp/i18n/es.json
index 8852dd3c6..20b79fc84 100644
--- a/webapp/i18n/es.json
+++ b/webapp/i18n/es.json
@@ -633,21 +633,44 @@
"choose_auth_page.ldapCreate": "Crea un nuevo equipo con tu cuenta de LDAP",
"choose_auth_page.noSignup": "No hay métodos de inicio de sesión configurad, por favor contacte al administrador de sistemasos",
"claim.account.noEmail": "No se especifico un correo electrónico.",
- "claim.email_to_sso.enterPwd": "Ingresa la contraseña para tu cuenta para {team} {site}",
- "claim.email_to_sso.pwd": "Contraseña",
- "claim.email_to_sso.pwdError": "Por favor introduce tu contraseña.",
- "claim.email_to_sso.ssoNote": "Debes tener una cuenta válida con {type}",
- "claim.email_to_sso.ssoType": "Al reclamar tu cuenta, sólo podrás iniciar sesión con {type} SSO",
- "claim.email_to_sso.switchTo": "Cambiar cuenta a {uiType}",
- "claim.email_to_sso.title": "Cambiar Cuenta de Correo/Contraseña a {uiType}",
- "claim.sso_to_email.confirm": "Confirmar Contraseña",
- "claim.sso_to_email.description": "Al cambiar el tipo de cuenta, sólo podrás iniciar sesión con tu correo electrónico y contraseña.",
- "claim.sso_to_email.enterPwd": "Por favor ingresa una contraseña.",
- "claim.sso_to_email.newPwd": "Nueva Contraseña",
- "claim.sso_to_email.pwdNotMatch": "Las contraseñas no coinciden.",
- "claim.sso_to_email.switchTo": "Cambiar {type} a correo electrónico y contraseña",
- "claim.sso_to_email.title": "Cambiar la cuenta de {type} a Correo Electrónico",
- "claim.sso_to_email_newPwd": "Ingresa una nueva contraseña para tu cuenta de {team} {site}",
+ "claim.email_to_ldap.enterLdapPwd": "Ingresa el ID y la contraseña de tu cuenta LDAP",
+ "claim.email_to_ldap.enterPwd": "Ingresa la contraseña para tu cuenta de correo en {team} {site}",
+ "claim.email_to_ldap.ldapId": "LDAP ID",
+ "claim.email_to_ldap.ldapIdError": "Por favor ingresa tu ID de LDAP.",
+ "claim.email_to_ldap.ldapPasswordError": "Por favor ingresa tu contraseña de LDAP.",
+ "claim.email_to_ldap.ldapPwd": "Contraseña de LDAP",
+ "claim.email_to_ldap.pwd": "Contraseña",
+ "claim.email_to_ldap.pwdError": "Por favor ingresa tu contraseña.",
+ "claim.email_to_ldap.ssoNote": "Debes tener una cuenta de LDAP válida",
+ "claim.email_to_ldap.ssoType": "Al reclamar tu cuenta, sólo podrás iniciar sesión con LDAP",
+ "claim.email_to_ldap.switchTo": "Cambiar cuenta a LDAP",
+ "claim.email_to_ldap.title": "Cambiar Cuenta de Correo/Contraseña a LDAP",
+ "claim.email_to_oauth.enterPwd": "Ingresa la contraseña para tu cuenta para {team} {site}",
+ "claim.email_to_oauth.pwd": "Contraseña",
+ "claim.email_to_oauth.pwdError": "Por favor introduce tu contraseña.",
+ "claim.email_to_oauth.ssoNote": "Debes tener una cuenta válida con {type}",
+ "claim.email_to_oauth.ssoType": "Al reclamar tu cuenta, sólo podrás iniciar sesión con {type} SSO",
+ "claim.email_to_oauth.switchTo": "Cambiar cuenta a {uiType}",
+ "claim.email_to_oauth.title": "Cambiar Cuenta de Correo/Contraseña a {uiType}",
+ "claim.ldap_to_email.confirm": "Confirmar Contraseña",
+ "claim.ldap_to_email.email": "Para iniciar sesión debes utilizar el correo electrónico {email}",
+ "claim.ldap_to_email.enterLdapPwd": "Ingresa tu contraseña de LDAP para tu cuenta de correo en {team} {site}",
+ "claim.ldap_to_email.enterPwd": "Ingresa una nueva contraseña para tu cuenta de correo",
+ "claim.ldap_to_email.ldapPasswordError": "Por favor ingresa tu contraseña LDAP.",
+ "claim.ldap_to_email.ldapPwd": "Contraseña LDAP",
+ "claim.ldap_to_email.pwd": "Contraseña",
+ "claim.ldap_to_email.pwdError": "Por favor ingresa tu contraseña.",
+ "claim.ldap_to_email.pwdNotMatch": "Las contraseñas no coinciden.",
+ "claim.ldap_to_email.ssoType": "Al cambiar el tipo de cuenta, sólo podrás iniciar sesión con tu correo electrónico y contraseña.",
+ "claim.ldap_to_email.switchTo": "Cambiar cuenta a correo/contraseña",
+ "claim.ldap_to_email.title": "Cambiar la cuenta de LDAP a Correo/Contraseña",
+ "claim.oauth_to_email.confirm": "Confirmar Contraseña",
+ "claim.oauth_to_email.description": "Al cambiar el tipo de cuenta, sólo podrás iniciar sesión con tu correo electrónico y contraseña.",
+ "claim.oauth_to_email.enterPwd": "Por favor ingresa una contraseña.",
+ "claim.oauth_to_email.newPwd": "Nueva Contraseña",
+ "claim.oauth_to_email.pwdNotMatch": "Las contraseñas no coinciden.",
+ "claim.oauth_to_email.switchTo": "Cambiar {type} a correo electrónico y contraseña",
+ "claim.oauth_to_email.title": "Cambiar la cuenta de {type} a Correo Electrónico",
"confirm_modal.cancel": "Cancelar",
"create_comment.addComment": "Agregar un comentario...",
"create_comment.comment": "Agregar Comentario",
@@ -1335,6 +1358,7 @@
"user.settings.security.switchEmail": "Cambiar para utilizar correo electrónico y contraseña",
"user.settings.security.switchGitlab": "Cambiar para utilizar GitLab SSO",
"user.settings.security.switchGoogle": "Cambiar para utilizar Google SSO",
+ "user.settings.security.switchLda": "Cambiar a utilizar LDAP",
"user.settings.security.title": "Configuración de Seguridad",
"user.settings.security.viewHistory": "Visualizar historial de acceso",
"user_list.notFound": "No se encontraron usuarios :(",
diff --git a/webapp/i18n/pt.json b/webapp/i18n/pt.json
index 41d3bbc1c..0b06b77af 100644
--- a/webapp/i18n/pt.json
+++ b/webapp/i18n/pt.json
@@ -639,21 +639,21 @@
"choose_auth_page.ldapCreate": "Criar uma nova equipe com uma conta LDAP",
"choose_auth_page.noSignup": "Nenhum método de inscrição configurado, por favor contate seu administrador do sistema.",
"claim.account.noEmail": "Nenhum email específicado",
- "claim.email_to_sso.enterPwd": "Entre a senha para o sua conta {team} {site}",
- "claim.email_to_sso.pwd": "Senha",
- "claim.email_to_sso.pwdError": "Por favor digite a sua senha.",
- "claim.email_to_sso.ssoNote": "Você precisa já ter uma conta {type} válida",
- "claim.email_to_sso.ssoType": "Ao retirar a sua conta, você só vai ser capaz de logar com SSO {type}",
- "claim.email_to_sso.switchTo": "Trocar a conta para {uiType}",
- "claim.email_to_sso.title": "Trocar E-mail/Senha da Conta para {uiType}",
- "claim.sso_to_email.confirm": "Confirmar senha",
- "claim.sso_to_email.description": "Após a alteração do tipo de conta, você só vai ser capaz de logar com seu e-mail e senha.",
- "claim.sso_to_email.enterPwd": "Por favor entre uma senha.",
- "claim.sso_to_email.newPwd": "Nova Senha",
- "claim.sso_to_email.pwdNotMatch": "As senha não correspondem.",
- "claim.sso_to_email.switchTo": "Trocar {type} para email e senha",
- "claim.sso_to_email.title": "Trocar Conta {type} para E-mail",
- "claim.sso_to_email_newPwd": "Entre a nova senha para o sua conta {team} {site}",
+ "claim.email_to_oauth.enterPwd": "Entre a senha para o sua conta {team} {site}",
+ "claim.email_to_oauth.pwd": "Senha",
+ "claim.email_to_oauth.pwdError": "Por favor digite a sua senha.",
+ "claim.email_to_oauth.ssoNote": "Você precisa já ter uma conta {type} válida",
+ "claim.email_to_oauth.ssoType": "Ao retirar a sua conta, você só vai ser capaz de logar com SSO {type}",
+ "claim.email_to_oauth.switchTo": "Trocar a conta para {uiType}",
+ "claim.email_to_oauth.title": "Trocar E-mail/Senha da Conta para {uiType}",
+ "claim.oauth_to_email.confirm": "Confirmar senha",
+ "claim.oauth_to_email.description": "Após a alteração do tipo de conta, você só vai ser capaz de logar com seu e-mail e senha.",
+ "claim.oauth_to_email.enterPwd": "Por favor entre uma senha.",
+ "claim.oauth_to_email.newPwd": "Nova Senha",
+ "claim.oauth_to_email.pwdNotMatch": "As senha não correspondem.",
+ "claim.oauth_to_email.switchTo": "Trocar {type} para email e senha",
+ "claim.oauth_to_email.title": "Trocar Conta {type} para E-mail",
+ "claim.oauth_to_email.newPwd": "Entre a nova senha para o sua conta {team} {site}",
"confirm_modal.cancel": "Cancelar",
"create_comment.addComment": "Adicionar um comentário...",
"create_comment.comment": "Adicionar Comentário",
diff --git a/webapp/package.json b/webapp/package.json
index cdfba8ef0..6f50962a4 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -24,6 +24,7 @@
"react-dom": "0.14.7",
"react-intl": "2.0.0-rc-1",
"react-router": "2.0.1",
+ "react-textarea-autosize": "3.3.0",
"twemoji": "1.4.1",
"velocity-animate": "1.2.3"
},
@@ -53,8 +54,8 @@
"webpack": "webpack/webpack#master"
},
"scripts": {
- "check": "eslint --ext \".jsx\" --ignore-pattern node_modules --quiet .",
- "build": "webpack",
- "run": "webpack --progress --watch"
+ "check": "eslint --ext \".jsx\" --ignore-pattern node_modules --quiet .",
+ "build": "webpack",
+ "run": "webpack --progress --watch"
}
}
diff --git a/webapp/root.jsx b/webapp/root.jsx
index 6ab5e80a0..f4d9a0e47 100644
--- a/webapp/root.jsx
+++ b/webapp/root.jsx
@@ -2,6 +2,7 @@
// See License.txt for license information.
import $ from 'jquery';
+require('perfect-scrollbar/jquery')($);
import 'bootstrap/dist/css/bootstrap.css';
import 'jasny-bootstrap/dist/css/jasny-bootstrap.css';
@@ -35,7 +36,6 @@ import SignupUserComplete from 'components/signup_user_complete.jsx';
import ShouldVerifyEmail from 'components/should_verify_email.jsx';
import DoVerifyEmail from 'components/do_verify_email.jsx';
import AdminConsole from 'components/admin_console/admin_controller.jsx';
-import ClaimAccount from 'components/claim/claim_account.jsx';
import SignupTeamComplete from 'components/signup_team_complete/components/signup_team_complete.jsx';
import WelcomePage from 'components/signup_team_complete/components/team_signup_welcome_page.jsx';
@@ -46,6 +46,12 @@ import UsernamePage from 'components/signup_team_complete/components/team_signup
import PasswordPage from 'components/signup_team_complete/components/team_signup_password_page.jsx';
import FinishedPage from 'components/signup_team_complete/components/team_signup_finished.jsx';
+import Claim from 'components/claim/claim.jsx';
+import EmailToOAuth from 'components/claim/components/email_to_oauth.jsx';
+import OAuthToEmail from 'components/claim/components/oauth_to_email.jsx';
+import LDAPToEmail from 'components/claim/components/ldap_to_email.jsx';
+import EmailToLDAP from 'components/claim/components/email_to_ldap.jsx';
+
import {addLocaleData} from 'react-intl';
import enLocaleData from 'react-intl/locale-data/en';
import esLocaleData from 'react-intl/locale-data/es';
@@ -308,10 +314,6 @@ function renderRootComponent() {
component={Login}
/>
<Route
- path='claim'
- component={ClaimAccount}
- />
- <Route
path='reset_password'
component={PasswordResetSendLink}
/>
@@ -319,6 +321,27 @@ function renderRootComponent() {
path='reset_password_complete'
component={PasswordResetForm}
/>
+ <Route
+ path='claim'
+ component={Claim}
+ >
+ <Route
+ path='oauth_to_email'
+ component={OAuthToEmail}
+ />
+ <Route
+ path='email_to_oauth'
+ component={EmailToOAuth}
+ />
+ <Route
+ path='email_to_ldap'
+ component={EmailToLDAP}
+ />
+ <Route
+ path='ldap_to_email'
+ component={LDAPToEmail}
+ />
+ </Route>
</Route>
</Route>
</Route>
diff --git a/webapp/sass/components/_buttons.scss b/webapp/sass/components/_buttons.scss
index efef11ce9..5d95759a2 100644
--- a/webapp/sass/components/_buttons.scss
+++ b/webapp/sass/components/_buttons.scss
@@ -15,6 +15,16 @@
}
}
+ &.btn-danger {
+ color: $white;
+
+ &:hover,
+ &:focus,
+ &:active {
+ color: $white;
+ }
+ }
+
&.btn-inactive {
background: $light-gray;
border-color: transparent;
diff --git a/webapp/sass/components/_modal.scss b/webapp/sass/components/_modal.scss
index 94378aabe..4e2049857 100644
--- a/webapp/sass/components/_modal.scss
+++ b/webapp/sass/components/_modal.scss
@@ -9,6 +9,7 @@
}
.modal-body {
+ max-height: calc(90vh - 62px);
overflow: auto;
padding: 20px 15px;
diff --git a/webapp/sass/components/_search.scss b/webapp/sass/components/_search.scss
index 499c4fad4..98c27ecdc 100644
--- a/webapp/sass/components/_search.scss
+++ b/webapp/sass/components/_search.scss
@@ -6,17 +6,19 @@
}
}
-.search-bar__container {
- @include flex(0 0 56px);
- padding: 12px 8px 0 0;
-
- .sidebar--right {
- &.move--left & {
+.sidebar--right {
+ &.move--left {
+ .search-bar__container {
padding-right: 42px;
}
}
}
+.search-bar__container {
+ @include flex(0 0 56px);
+ padding: 12px 8px 0 0;
+}
+
.glyphicon-refresh-animate {
@include animation(spin .7s infinite linear);
}
@@ -95,7 +97,7 @@
}
.glyphicon-refresh-animate {
- color: $dark-gray;
+ @include opacity(0.5);
position: absolute;
right: 27px;
top: 27px;
diff --git a/webapp/sass/components/_suggestion-list.scss b/webapp/sass/components/_suggestion-list.scss
index 0100026ca..39269642d 100644
--- a/webapp/sass/components/_suggestion-list.scss
+++ b/webapp/sass/components/_suggestion-list.scss
@@ -8,6 +8,7 @@
.suggestion-list--top {
position: absolute;
+ bottom: 100%;
}
.suggestion-list__content {
diff --git a/webapp/sass/layout/_headers.scss b/webapp/sass/layout/_headers.scss
index 9b631d00f..e75d2556b 100644
--- a/webapp/sass/layout/_headers.scss
+++ b/webapp/sass/layout/_headers.scss
@@ -134,6 +134,10 @@
@include alpha-property(background, $black, .1);
}
+ a {
+ text-decoration: none;
+ }
+
.user__name {
color: $white;
}
diff --git a/webapp/sass/layout/_navigation.scss b/webapp/sass/layout/_navigation.scss
index 87e4b4d27..3daf6e56b 100644
--- a/webapp/sass/layout/_navigation.scss
+++ b/webapp/sass/layout/_navigation.scss
@@ -89,7 +89,7 @@
&.info-popover {
@include background-size(100% 100%);
- background: url('../images/info__icon.png');
+ background-image: url('../images/info__icon.png');
cursor: pointer;
height: 19px;
position: relative;
diff --git a/webapp/sass/layout/_post.scss b/webapp/sass/layout/_post.scss
index 9d5be239a..4170483db 100644
--- a/webapp/sass/layout/_post.scss
+++ b/webapp/sass/layout/_post.scss
@@ -7,9 +7,7 @@
line-height: 20px;
min-height: 36px;
overflow-x: hidden;
- position: absolute;
resize: none;
- top: 0px;
white-space: pre-wrap;
word-wrap: break-word;
diff --git a/webapp/sass/layout/_sidebar-left.scss b/webapp/sass/layout/_sidebar-left.scss
index 4c65d0a65..3a5e74570 100644
--- a/webapp/sass/layout/_sidebar-left.scss
+++ b/webapp/sass/layout/_sidebar-left.scss
@@ -1,7 +1,6 @@
@charset 'UTF-8';
.sidebar--left {
- background: #fafafa;
border-right: $border-gray;
height: 100%;
left: 0;
diff --git a/webapp/sass/responsive/_mobile.scss b/webapp/sass/responsive/_mobile.scss
index 53ea6d329..0e1a471cf 100644
--- a/webapp/sass/responsive/_mobile.scss
+++ b/webapp/sass/responsive/_mobile.scss
@@ -14,23 +14,23 @@
.post {
.post__dropdown {
+ display: inline-block;
+ height: 20px;
line-height: 9px;
+ text-align: center;
text-decoration: none;
width: 20px;
- display: inline-block;
- text-align: center;
- height: 20px;
&:after {
- font-size: 20px;
content: '...';
+ font-size: 20px;
top: -3px;
}
}
.comment-icon__container {
- visibility: visible;
display: none;
+ visibility: visible;
&.icon--show {
display: inline-block;
@@ -57,8 +57,8 @@
}
.post__reply {
- margin-right: 20px;
float: right;
+ margin-right: 20px;
svg {
top: 1px;
@@ -97,15 +97,15 @@
width: 40px;
img {
- width: 32px;
height: 32px;
+ width: 32px;
}
}
}
.post-image__column {
- width: 200px;
height: 95px;
+ width: 200px;
}
.textarea-wrapper {
@@ -114,6 +114,7 @@
display: none;
}
}
+
.form-control {
&.min-height {
min-height: 100px;
@@ -132,6 +133,7 @@
.tip-overlay {
&.tip-overlay--chat {
margin-left: 10px;
+
.arrow {
left: 32px;
}
@@ -142,20 +144,21 @@
display: block;
.file-details__preview {
+ border-bottom: 1px solid #dddddd;
+ border-right: none;
display: block;
- width: 100%;
height: 150px;
- border-right: none;
- border-bottom: 1px solid #ddd;
+ width: 100%;
+
img {
- width: 64px;
height: 64px;
+ width: 64px;
}
}
.file-details {
- width: 100%;
height: auto;
+ width: 100%;
}
}
@@ -165,9 +168,9 @@
.modal-direct-channels {
.member-count {
+ display: block;
float: none;
margin-top: 10px;
- display: block;
}
}
@@ -181,9 +184,9 @@
}
.overlay__circle {
- width: 300px;
height: 300px;
margin: -150px 0 0 -150px;
+ width: 300px;
}
.overlay__files {
@@ -212,9 +215,9 @@
}
.signup-team__container {
- padding: 30px 0;
- margin-bottom: 30px;
font-size: .9em;
+ margin-bottom: 30px;
+ padding: 30px 0;
.signup-team__name {
font-size: 2em;
@@ -240,8 +243,8 @@
.modal {
.info__label {
- text-align: left;
padding-bottom: 5px;
+ text-align: left;
}
.modal-header {
@@ -250,8 +253,8 @@
}
button.close {
- font-weight: normal;
font-size: 27px;
+ font-weight: normal;
}
.modal-title {
@@ -262,9 +265,9 @@
.btn {
&.btn-primary {
display: block;
+ float: none;
margin: 10px 0 6px;
width: 100%;
- float: none;
}
}
}
@@ -304,8 +307,8 @@
z-index: 5;
.modal-title {
- width: 100%;
text-align: center;
+ width: 100%;
}
}
@@ -323,6 +326,7 @@
.settings-table {
padding: 0;
+
.nav {
position: relative;
top: auto;
@@ -339,6 +343,7 @@
top: 14px;
right: 0;
padding-right: 0;
+
.fa {
display: inline-block;
}
@@ -347,6 +352,7 @@
&.minimize-settings {
padding: 0;
display: none;
+
.user-settings {
padding: 70px 20px 30px;
}
@@ -360,10 +366,10 @@
.nav {
> li {
> a {
- border-top: 1px solid #ddd;
+ border-top: 1px solid #dddddd;
font-size: 1.1em;
line-height: 2.7;
- color: #555;
+ color: #555555;
.glyphicon {
margin-left: 7px;
@@ -438,25 +444,24 @@
}
.file-preview__remove {
- width: 28px;
+ @include alpha-property(background, $black, .5);
height: 28px;
left: auto;
right: 0;
- top: 0;
- background: #444;
- background: rgba(#000, .5);
text-align: center;
+ top: 0;
+ width: 28px;
&:after {
display: none;
}
i {
+ font-size: 16px;
line-height: 29px;
- top: auto;
- right: auto;
position: relative;
- font-size: 16px;
+ right: auto;
+ top: auto;
}
}
@@ -467,10 +472,10 @@
margin: 0 -15px;
.dropdown__icon {
- width: 4px;
- height: 16px;
@include background-size(100% 100%);
display: inline-block;
+ height: 16px;
+ width: 4px;
}
}
@@ -502,10 +507,11 @@
.footer-pane {
.footer-link {
+ line-height: 1.7;
padding: 0;
- width: 100%;
text-align: right;
- line-height: 1.7;
+ width: 100%;
+
&.copyright {
width: 100%;
}
@@ -517,10 +523,10 @@
}
.search-bar__container {
- padding: 0;
@include flex(0 0 44px);
background: $primary-color;
- color: #fff;
+ color: $white;
+ padding: 0;
&.focused {
.sidebar__collapse {
@@ -534,40 +540,40 @@
}
.search__clear {
- @include translateX(0px);
+ @include translateX(0);
}
}
+
.search__form {
+ @include single-transition(all, .2s, linear);
+ @include translateX(0);
border: none;
- @include translateX(0px);
- padding: 7px 20px 0 49px;
height: 45px;
+ padding: 7px 20px 0 49px;
position: relative;
- @include single-transition(all, .2s, linear);
.glyphicon-refresh-animate {
+ color: $black;
right: 33px;
top: 15px;
- color: #fff;
- color: rgba(255,255,255,.5);
}
.form-control {
+ @include border-radius(3px);
+ background: $white;
border: none;
+ color: $dark-gray;
padding: 0 10px 0 31px;
- background: rgba(black, .2);
- @include border-radius(3px);
- color: #444;
- background: #fff;
}
}
}
+
.sidebar--menu {
@include single-transition(transform, .5s, ease);
@include translate3d(290px, 0, 0);
- width: 290px;
border: none;
display: block;
+ width: 290px;
&.move--left {
@include translate3d(0, 0, 0);
@@ -627,7 +633,7 @@
ul {
clear: both;
- background: #fff;
+ background: #ffffff;
border-radius: 3px;
top: 5px;
position: relative;
@@ -795,6 +801,7 @@
}
}
}
+
.activity-log__table {
> div {
display: block;
@@ -805,8 +812,8 @@
}
.activity-log__action {
- text-align: left;
margin-top: 10px;
+ text-align: left;
}
}
}
@@ -816,8 +823,8 @@
.settings-modal {
.settings-table {
.security-links {
- margin-bottom: 10px;
display: block;
+ margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
@@ -850,11 +857,11 @@
.member-drop,
.member-role {
- position: relative;
- margin: 0px 0 0 44px;
+ margin: 0 0 0 44px;
padding: 5px 0;
- top: 0;
+ position: relative;
right: 0;
+ top: 0;
}
.open > .dropdown-menu {
diff --git a/webapp/sass/routes/_admin-console.scss b/webapp/sass/routes/_admin-console.scss
index 0b47e5ab6..7e53713c0 100644
--- a/webapp/sass/routes/_admin-console.scss
+++ b/webapp/sass/routes/_admin-console.scss
@@ -10,12 +10,8 @@
width: 100%;
}
- .row {
- margin: 0;
- }
-
h3 {
- border-bottom: 1px solid #ddd;
+ border-bottom: 1px solid alpha-color($black, .1);
font-weight: 600;
margin: 1em 0;
padding-bottom: .5em;
@@ -75,11 +71,18 @@
width: 17px;
}
+ &.divider {
+ @include alpha-property(background, $black, .1);
+ }
+
> a {
&:hover,
- &.active,
&:focus {
- background-color: #eaeaea;
+ @include alpha-property(background, $black, .1);
+ }
+
+ &.active {
+ background-color: transparent;
}
}
diff --git a/webapp/stores/browser_store.jsx b/webapp/stores/browser_store.jsx
index 66190f6a2..bba146e38 100644
--- a/webapp/stores/browser_store.jsx
+++ b/webapp/stores/browser_store.jsx
@@ -8,6 +8,8 @@ function getPrefix() {
return global.window.mm_current_user_id + '_';
}
+ console.log('BrowserStore tried to operate without user present'); //eslint-disable-line no-console
+
return 'unknown_';
}
@@ -34,46 +36,35 @@ class BrowserStoreClass {
}
checkVersion() {
- var currentVersion = sessionStorage.getItem('storage_version');
+ var currentVersion = this.getGlobalItem('storage_version');
if (currentVersion !== global.window.mm_config.Version) {
- sessionStorage.clear();
+ this.clearAll();
try {
- sessionStorage.setItem('storage_version', global.window.mm_config.Version);
+ this.setGlobalItem('storage_version', global.window.mm_config.Version);
} catch (e) {
// Do nothing
}
}
}
- getItem(name, defaultValue) {
- var result = null;
- try {
- result = JSON.parse(sessionStorage.getItem(getPrefix() + name));
- } catch (err) {
- result = null;
- }
-
- if (result === null && typeof defaultValue !== 'undefined') {
- result = defaultValue;
- }
-
- return result;
+ setItem(name, value) {
+ this.setGlobalItem(getPrefix() + name, value);
}
- setItem(name, value) {
- sessionStorage.setItem(getPrefix() + name, JSON.stringify(value));
+ getItem(name, defaultValue) {
+ return this.getGlobalItem(getPrefix() + name, defaultValue);
}
removeItem(name) {
- sessionStorage.removeItem(getPrefix() + name);
+ this.removeGlobalItem(getPrefix() + name);
}
setGlobalItem(name, value) {
try {
if (this.isLocalStorageSupported()) {
- localStorage.setItem(getPrefix() + name, JSON.stringify(value));
+ localStorage.setItem(name, JSON.stringify(value));
} else {
- sessionStorage.setItem(getPrefix() + name, JSON.stringify(value));
+ sessionStorage.setItem(name, JSON.stringify(value));
}
} catch (err) {
console.log('An error occurred while setting local storage, clearing all props'); //eslint-disable-line no-console
@@ -87,9 +78,9 @@ class BrowserStoreClass {
var result = null;
try {
if (this.isLocalStorageSupported()) {
- result = JSON.parse(localStorage.getItem(getPrefix() + name));
+ result = JSON.parse(localStorage.getItem(name));
} else {
- result = JSON.parse(sessionStorage.getItem(getPrefix() + name));
+ result = JSON.parse(sessionStorage.getItem(name));
}
} catch (err) {
result = null;
@@ -104,18 +95,18 @@ class BrowserStoreClass {
removeGlobalItem(name) {
if (this.isLocalStorageSupported()) {
- localStorage.removeItem(getPrefix() + name);
+ localStorage.removeItem(name);
} else {
- sessionStorage.removeItem(getPrefix() + name);
+ sessionStorage.removeItem(name);
}
}
getLastServerVersion() {
- return sessionStorage.getItem('last_server_version');
+ return this.getGlobalItem('last_server_version');
}
setLastServerVersion(version) {
- sessionStorage.setItem('last_server_version', version);
+ this.setGlobalItem('last_server_version', version);
}
signalLogout() {
@@ -185,6 +176,7 @@ class BrowserStoreClass {
const logoutId = sessionStorage.getItem('__logout__');
sessionStorage.clear();
+ localStorage.clear();
if (logoutId) {
sessionStorage.setItem('__logout__', logoutId);
@@ -222,4 +214,3 @@ class BrowserStoreClass {
var BrowserStore = new BrowserStoreClass();
export default BrowserStore;
-window.BrowserStore = BrowserStore;
diff --git a/webapp/stores/error_store.jsx b/webapp/stores/error_store.jsx
index 776375a82..7c695a335 100644
--- a/webapp/stores/error_store.jsx
+++ b/webapp/stores/error_store.jsx
@@ -35,15 +35,15 @@ class ErrorStoreClass extends EventEmitter {
}
getLastError() {
- return BrowserStore.getItem('last_error');
+ return BrowserStore.getGlobalItem('last_error');
}
storeLastError(error) {
- BrowserStore.setItem('last_error', error);
+ BrowserStore.setGlobalItem('last_error', error);
}
getConnectionErrorCount() {
- var count = BrowserStore.getItem('last_error_conn');
+ var count = BrowserStore.getGlobalItem('last_error_conn');
if (count == null) {
return 0;
@@ -53,12 +53,12 @@ class ErrorStoreClass extends EventEmitter {
}
setConnectionErrorCount(count) {
- BrowserStore.setItem('last_error_conn', count);
+ BrowserStore.setGlobalItem('last_error_conn', count);
}
clearLastError() {
- BrowserStore.removeItem('last_error');
- BrowserStore.removeItem('last_error_conn');
+ BrowserStore.removeGlobalItem('last_error');
+ BrowserStore.removeGlobalItem('last_error_conn');
}
}
diff --git a/webapp/stores/search_store.jsx b/webapp/stores/search_store.jsx
index c7818a858..acaa9e52f 100644
--- a/webapp/stores/search_store.jsx
+++ b/webapp/stores/search_store.jsx
@@ -4,8 +4,6 @@
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import EventEmitter from 'events';
-import BrowserStore from 'stores/browser_store.jsx';
-
import Constants from 'utils/constants.jsx';
var ActionTypes = Constants.ActionTypes;
@@ -18,29 +16,9 @@ class SearchStoreClass extends EventEmitter {
constructor() {
super();
- this.emitChange = this.emitChange.bind(this);
- this.addChangeListener = this.addChangeListener.bind(this);
- this.removeChangeListener = this.removeChangeListener.bind(this);
-
- this.emitSearchChange = this.emitSearchChange.bind(this);
- this.addSearchChangeListener = this.addSearchChangeListener.bind(this);
- this.removeSearchChangeListener = this.removeSearchChangeListener.bind(this);
-
- this.emitSearchTermChange = this.emitSearchTermChange.bind(this);
- this.addSearchTermChangeListener = this.addSearchTermChangeListener.bind(this);
- this.removeSearchTermChangeListener = this.removeSearchTermChangeListener.bind(this);
-
- this.emitShowSearch = this.emitShowSearch.bind(this);
- this.addShowSearchListener = this.addShowSearchListener.bind(this);
- this.removeShowSearchListener = this.removeShowSearchListener.bind(this);
-
- this.getSearchResults = this.getSearchResults.bind(this);
- this.getIsMentionSearch = this.getIsMentionSearch.bind(this);
-
- this.storeSearchTerm = this.storeSearchTerm.bind(this);
- this.getSearchTerm = this.getSearchTerm.bind(this);
-
- this.storeSearchResults = this.storeSearchResults.bind(this);
+ this.searchResults = {};
+ this.isMentionSearch = false;
+ this.searchTerm = '';
}
emitChange() {
@@ -92,24 +70,24 @@ class SearchStoreClass extends EventEmitter {
}
getSearchResults() {
- return BrowserStore.getItem('search_results');
+ return this.searchResults;
}
getIsMentionSearch() {
- return BrowserStore.getItem('is_mention_search');
+ return this.isMentionSearch;
}
storeSearchTerm(term) {
- BrowserStore.setItem('search_term', term);
+ this.searchTerm = term;
}
getSearchTerm() {
- return BrowserStore.getItem('search_term');
+ return this.searchTerm;
}
storeSearchResults(results, isMentionSearch) {
- BrowserStore.setItem('search_results', results);
- BrowserStore.setItem('is_mention_search', Boolean(isMentionSearch));
+ this.searchResults = results;
+ this.isMentionSearch = isMentionSearch;
}
}
diff --git a/webapp/stores/user_store.jsx b/webapp/stores/user_store.jsx
index 98cc2f3f1..4213e6e8c 100644
--- a/webapp/stores/user_store.jsx
+++ b/webapp/stores/user_store.jsx
@@ -16,7 +16,10 @@ const CHANGE_EVENT_STATUSES = 'change_statuses';
class UserStoreClass extends EventEmitter {
constructor() {
super();
- this.profileCache = null;
+ this.profiles = {};
+ this.statuses = {};
+ this.sessions = {};
+ this.audits = {};
this.currentUserId = '';
}
@@ -135,11 +138,7 @@ class UserStoreClass extends EventEmitter {
}
getProfiles() {
- if (this.profileCache !== null) {
- return this.profileCache;
- }
-
- return BrowserStore.getItem('profiles', {});
+ return this.profiles;
}
getActiveOnlyProfiles(skipCurrent) {
@@ -171,47 +170,38 @@ class UserStoreClass extends EventEmitter {
}
saveProfile(profile) {
- var ps = this.getProfiles();
- ps[profile.id] = profile;
- this.profileCache = ps;
- BrowserStore.setItem('profiles', ps);
+ this.profiles[profile.id] = profile;
}
saveProfiles(profiles) {
const currentId = this.getCurrentId();
- if (this.profileCache) {
- const currentUser = this.profileCache[currentId];
- if (currentUser) {
- if (currentId in profiles) {
- delete profiles[currentId];
- }
-
- this.profileCache = profiles;
- this.profileCache[currentId] = currentUser;
- } else {
- this.profileCache = profiles;
+ const currentUser = this.profiles[currentId];
+ if (currentUser) {
+ if (currentId in this.profiles) {
+ delete this.profiles[currentId];
}
+
+ this.profiles = profiles;
+ this.profiles[currentId] = currentUser;
} else {
- this.profileCache = profiles;
+ this.profiles = profiles;
}
-
- BrowserStore.setItem('profiles', profiles);
}
setSessions(sessions) {
- BrowserStore.setItem('sessions', sessions);
+ this.sessions = sessions;
}
getSessions() {
- return BrowserStore.getItem('sessions', {loading: true});
+ return this.sessions;
}
setAudits(audits) {
- BrowserStore.setItem('audits', audits);
+ this.audits = audits;
}
getAudits() {
- return BrowserStore.getItem('audits', {loading: true});
+ return this.audits;
}
getCurrentMentionKeys() {
@@ -252,7 +242,7 @@ class UserStoreClass extends EventEmitter {
}
pSetStatuses(statuses) {
- BrowserStore.setItem('statuses', statuses);
+ this.statuses = statuses;
}
setStatus(userId, status) {
@@ -263,7 +253,7 @@ class UserStoreClass extends EventEmitter {
}
getStatuses() {
- return BrowserStore.getItem('statuses', {});
+ return this.statuses;
}
getStatus(id) {
diff --git a/webapp/utils/client.jsx b/webapp/utils/client.jsx
index e29cf71d3..d42767d31 100644
--- a/webapp/utils/client.jsx
+++ b/webapp/utils/client.jsx
@@ -256,38 +256,72 @@ export function resetPassword(data, success, error) {
track('api', 'api_users_reset_password');
}
-export function switchToSSO(data, success, error) {
+export function emailToOAuth(data, success, error) {
$.ajax({
- url: '/api/v1/users/switch_to_sso',
+ url: '/api/v1/users/claim/email_to_oauth',
dataType: 'json',
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success,
error: function onError(xhr, status, err) {
- var e = handleError('switchToSSO', xhr, status, err);
+ var e = handleError('emailToOAuth', xhr, status, err);
error(e);
}
});
- track('api', 'api_users_switch_to_sso');
+ track('api', 'api_users_email_to_oauth');
}
-export function switchToEmail(data, success, error) {
+export function oauthToEmail(data, success, error) {
$.ajax({
- url: '/api/v1/users/switch_to_email',
+ url: '/api/v1/users/claim/oauth_to_email',
dataType: 'json',
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(data),
success,
error: function onError(xhr, status, err) {
- var e = handleError('switchToEmail', xhr, status, err);
+ var e = handleError('oauthToEmail', xhr, status, err);
error(e);
}
});
- track('api', 'api_users_switch_to_email');
+ track('api', 'api_users_oauth_to_email');
+}
+
+export function emailToLDAP(data, success, error) {
+ $.ajax({
+ url: '/api/v1/users/claim/email_to_ldap',
+ dataType: 'json',
+ contentType: 'application/json',
+ type: 'POST',
+ data: JSON.stringify(data),
+ success,
+ error: function onError(xhr, status, err) {
+ var e = handleError('emailToLDAP', xhr, status, err);
+ error(e);
+ }
+ });
+
+ track('api', 'api_users_email_to_ldap');
+}
+
+export function ldapToEmail(data, success, error) {
+ $.ajax({
+ url: '/api/v1/users/claim/ldap_to_email',
+ dataType: 'json',
+ contentType: 'application/json',
+ type: 'POST',
+ data: JSON.stringify(data),
+ success,
+ error: function onError(xhr, status, err) {
+ var e = handleError('ldapToEmail', xhr, status, err);
+ error(e);
+ }
+ });
+
+ track('api', 'api_users_ldap_to_email');
}
export function logout(success, error) {
diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx
index 28c9df60b..bcd2fadb9 100644
--- a/webapp/utils/constants.jsx
+++ b/webapp/utils/constants.jsx
@@ -185,6 +185,7 @@ export default {
OFFTOPIC_CHANNEL: 'off-topic',
GITLAB_SERVICE: 'gitlab',
GOOGLE_SERVICE: 'google',
+ LDAP_SERVICE: 'ldap',
EMAIL_SERVICE: 'email',
SIGNIN_CHANGE: 'signin_change',
SIGNIN_VERIFIED: 'verified',
diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx
index 5f16baf1f..1379455ca 100644
--- a/webapp/utils/utils.jsx
+++ b/webapp/utils/utils.jsx
@@ -687,7 +687,7 @@ export function applyTheme(theme) {
}
if (theme.centerChannelBg) {
- changeCss('.app__content, .markdown__table, .markdown__table tbody tr, .suggestion-list__content, .modal .modal-content, .modal .modal-back', 'background:' + theme.centerChannelBg, 1);
+ changeCss('.app__content, .markdown__table, .markdown__table tbody tr, .suggestion-list__content, .modal .modal-content', 'background:' + theme.centerChannelBg, 1);
changeCss('#post-list .post-list-holder-by-time', 'background:' + theme.centerChannelBg, 1);
changeCss('#post-create', 'background:' + theme.centerChannelBg, 1);
changeCss('.date-separator .separator__text, .new-separator .separator__text', 'background:' + theme.centerChannelBg, 1);