PLT-2057 User as a first class object (#2648)

* Adding TeamMember to system

* Fixing all unit tests on the backend

* Fixing merge conflicts

* Fixing merge conflict

* Adding javascript unit tests

* Adding TeamMember to system

* Fixing all unit tests on the backend

* Fixing merge conflicts

* Fixing merge conflict

* Adding javascript unit tests

* Adding client side unit test

* Cleaning up the clint side tests

* Fixing msg

* Adding more client side unit tests

* Adding more using tests

* Adding last bit of client side unit tests and adding make cmd

* Fixing bad merge

* Fixing libraries

* Updating to new client side API

* Fixing borken unit test

* Fixing unit tests

* ugg...trying to beat gofmt

* ugg...trying to beat gofmt

* Cleaning up remainder of the server side routes

* Adding inital load api

* Increased coverage of webhook unit tests (#2660)

* Adding loading ... to root html

* Fixing bad merge

* Removing explicit content type so superagent will guess corectly (#2685)

* Fixing merge and unit tests

* Adding create team UI

* Fixing signup flows

* Adding LDAP unit tests and enterprise unit test helper (#2702)

* Add the ability to reset MFA from the commandline (#2706)

* Fixing compliance unit tests

* Fixing client side tests

* Adding open server to system console

* Moving websocket connection

* Fixing unit test

* Fixing unit tests

* Fixing unit tests

* Adding nickname and more LDAP unit tests (#2717)

* Adding join open teams

* Cleaning up all TODOs in the code

* Fixing web sockets

* Removing unused webockets file

* PLT-2533 Add the ability to reset a user's MFA from the system console (#2715)

* Add the ability to reset a user's MFA from the system console

* Add client side unit test for adminResetMfa

* Reorganizing authentication to fix LDAP error message (#2723)

* Fixing failing unit test

* Initial upgrade db code

* Adding upgrade script

* Fixing upgrade script after running on core

* Update OAuth and Claim routes to work with user model changes (#2739)

* Fixing perminant deletion. Adding ability to delete all user and the entire database (#2740)

* Fixing team invite ldap login call (#2741)

* Fixing bluebar and some img stuff

* Fix all the different file upload web utils (#2743)

* Fixing invalid session redirect (#2744)

* Redirect on bad channel name (#2746)

* Fixing a bunch of issue and removing dead code

* Patch to fix error message on leave channel (#2747)

* Setting EnableOpenServer to false by default

* Fixing config

* Fixing upgrade

* Fixing reported bugs

* Bug fixes for PLT-2057

* PLT-2563 Redo password recovery to use a database table (#2745)

* Redo password recovery to use a database table

* Update reset password audits

* Split out admin and user reset password APIs to be separate

* Delete password recovery when user is permanently deleted

* Consolidate password resetting into a single function

* Removed private channels as an option for outgoing webhooks (#2752)

* PLT-2577/PLT-2552 Fixes for backstage (#2753)

* Added URL to incoming webhook list

* Fixed client functions for adding/removing integrations

* Disallowed slash commands without trigger words

* Fixed clientside handling of errors on AddCommand page

* Minor auth cleanup (#2758)

* Changed EditPostModal to just close if you save without making any changes (#2759)

* Renamed client -> Client in async_client.jsx and fixed eslint warnings (#2756)

* Fixed url in channel info modal (#2755)

* Fixing reported issues

* Moving to version 3 of the apis

* Fixing command unit tests (#2760)

* Adding team admins

* Fixing DM issue

* Fixing eslint error

* Properly set EditPostModal's originalText state in all cases (#2762)

* Update client config check to assume features is defined if server is licensed (#2772)

* Fixing url link

* Fixing issue with websocket crashing when sending messages to different teams
Этот коммит содержится в:
Corey Hulen
2016-04-21 22:37:01 -07:00
родитель 5c755463ed
Коммит 2e5617c29b
268 изменённых файлов: 13214 добавлений и 9686 удалений

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import UserStore from 'stores/user_store.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {Modal} from 'react-bootstrap';
import LoadingScreen from './loading_screen.jsx';

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

@@ -3,27 +3,20 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
import TeamStore from 'stores/team_store.jsx';
import Constants from 'utils/constants.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
import {FormattedMessage} from 'react-intl';
import {Link} from 'react-router';
function getStateFromStores() {
return {currentTeam: TeamStore.getCurrent()};
}
import React from 'react';
export default class AdminNavbarDropdown extends React.Component {
constructor(props) {
super(props);
this.blockToggle = false;
this.state = getStateFromStores();
}
componentDidMount() {
@@ -64,24 +57,27 @@ export default class AdminNavbarDropdown extends React.Component {
>
<li>
<Link
to={'/' + this.state.currentTeam.name + '/channels/town-square'}
to={'/select_team'}
>
<FormattedMessage
id='admin.nav.switch'
defaultMessage='Switch to {display_name}'
values={{
display_name: this.state.currentTeam.display_name
display_name: global.window.mm_config.SiteName
}}
/>
</Link>
</li>
<li>
<Link to={Utils.getTeamURLFromAddressBar() + '/logout'}>
<a
href='#'
onClick={GlobalActions.emitUserLoggedOutEvent}
>
<FormattedMessage
id='admin.nav.logout'
defaultMessage='Logout'
/>
</Link>
</a>
</li>
</ul>
</li>

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

@@ -4,6 +4,7 @@
import $ from 'jquery';
import AdminNavbarDropdown from './admin_navbar_dropdown.jsx';
import UserStore from 'stores/user_store.jsx';
import Client from 'utils/web_client.jsx';
import {FormattedMessage} from 'react-intl';
@@ -41,7 +42,7 @@ export default class SidebarHeader extends React.Component {
profilePicture = (
<img
className='user__picture'
src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at}
src={Client.getUsersRoute() + '/' + me.id + '/image?time=' + me.update_at}
/>
);
}

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

@@ -7,7 +7,7 @@ import * as Utils from '../../utils/utils.jsx';
import AdminStore from '../../stores/admin_store.jsx';
import UserStore from '../../stores/user_store.jsx';
import * as Client from '../../utils/client.jsx';
import * as Client from '../../utils/web_client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import {FormattedMessage, FormattedDate, FormattedTime} from 'react-intl';
@@ -153,7 +153,7 @@ export default class ComplianceReports extends React.Component {
var download = '';
if (report.status === 'finished') {
download = (
<a href={'/api/v1/admin/download_compliance_report/' + report.id}>
<a href={Client.getAdminRoute() + '/download_compliance_report/' + report.id}>
<FormattedMessage
id='admin.compliance_table.download'
defaultMessage='Download'

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

@@ -2,7 +2,7 @@
// See License.txt for license information.
import $ from 'jquery';
import * as Client from '../../utils/client.jsx';
import * as Client from '../../utils/web_client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import * as Utils from '../../utils/utils.jsx';

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import crypto from 'crypto';
import ConnectionSecurityDropdownSetting from './connection_security_dropdown_setting.jsx';

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import crypto from 'crypto';

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
@@ -61,6 +61,7 @@ class LdapSettings extends React.Component {
config.LdapSettings.BindPassword = this.refs.BindPassword.value.trim();
config.LdapSettings.FirstNameAttribute = this.refs.FirstNameAttribute.value.trim();
config.LdapSettings.LastNameAttribute = this.refs.LastNameAttribute.value.trim();
config.LdapSettings.NicknameAttribute = this.refs.NicknameAttribute.value.trim();
config.LdapSettings.EmailAttribute = this.refs.EmailAttribute.value.trim();
config.LdapSettings.UsernameAttribute = this.refs.UsernameAttribute.value.trim();
config.LdapSettings.IdAttribute = this.refs.IdAttribute.value.trim();
@@ -438,6 +439,35 @@ class LdapSettings extends React.Component {
</p>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='NicknameAttribute'
>
<FormattedMessage
id='admin.ldap.nicknameAttrTitle'
defaultMessage='Nickname Attribute:'
/>
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
id='NicknameAttribute'
ref='NicknameAttribute'
placeholder={Utils.localizeMessage('admin.ldap.nicknameAttrEx', 'Ex "nickname"')}
defaultValue={this.props.config.LdapSettings.NicknameAttribute}
onChange={this.handleChange}
disabled={!this.state.enable}
/>
<p className='help-text'>
<FormattedMessage
id='admin.ldap.nicknameAttrDesc'
defaultMessage='(Optional) The attribute in the LDAP server that will be used to populate the nickname of users in Mattermost.'
/>
</p>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';

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

@@ -4,7 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
@@ -54,10 +54,7 @@ class LicenseSettings extends React.Component {
$('#upload-button').button('loading');
const formData = new FormData();
formData.append('license', file, file.name);
Client.uploadLicenseFile(formData,
Client.uploadLicenseFile(file,
() => {
Utils.clearFileInput(element[0]);
$('#upload-button').button('reset');

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';

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

@@ -2,7 +2,7 @@
// See License.txt for license information.
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import {Modal} from 'react-bootstrap';
@@ -40,12 +40,9 @@ class ResetPasswordModal extends React.Component {
this.setState({serverError: null});
var data = {};
data.new_password = password;
data.name = this.props.team.name;
data.user_id = this.props.user.id;
Client.resetPassword(data,
Client.adminResetPassword(
this.props.user.id,
password,
() => {
this.props.onModalSubmit(ReactDOM.findDOMNode(this.refs.password).value);
},
@@ -159,4 +156,4 @@ ResetPasswordModal.propTypes = {
onModalDismissed: React.PropTypes.func
};
export default injectIntl(ResetPasswordModal);
export default injectIntl(ResetPasswordModal);

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import crypto from 'crypto';

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

@@ -2,7 +2,7 @@
// See License.txt for license information.
import $ from 'jquery';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
@@ -42,6 +42,7 @@ class TeamSettings extends React.Component {
this.handleImageSubmit = this.handleImageSubmit.bind(this);
this.uploading = false;
this.timestamp = 0;
this.state = {
saveNeeded: false,
@@ -53,7 +54,7 @@ class TeamSettings extends React.Component {
componentWillMount() {
if (global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.CustomBrand === 'true') {
$.get('/api/v1/admin/get_brand_image').done(() => this.setState({brandImageExists: true}));
$.get(Client.getAdminRoute() + '/get_brand_image').done(() => this.setState({brandImageExists: true}));
}
}
@@ -89,6 +90,7 @@ class TeamSettings extends React.Component {
if (element.prop('files').length > 0) {
this.setState({fileSelected: true, brandImage: element.prop('files')[0]});
}
$('#upload-button').button('reset');
}
handleSubmit(e) {
@@ -100,8 +102,8 @@ class TeamSettings extends React.Component {
config.TeamSettings.RestrictCreationToDomains = this.refs.RestrictCreationToDomains.value.trim();
config.TeamSettings.EnableTeamCreation = this.refs.EnableTeamCreation.checked;
config.TeamSettings.EnableUserCreation = this.refs.EnableUserCreation.checked;
config.TeamSettings.EnableOpenServer = this.refs.EnableOpenServer.checked;
config.TeamSettings.RestrictTeamNames = this.refs.RestrictTeamNames.checked;
config.TeamSettings.EnableTeamListing = this.refs.EnableTeamListing.checked;
if (this.refs.EnableCustomBrand) {
config.TeamSettings.EnableCustomBrand = this.refs.EnableCustomBrand.checked;
@@ -155,6 +157,7 @@ class TeamSettings extends React.Component {
Client.uploadBrandImage(this.state.brandImage,
() => {
$('#upload-button').button('complete');
this.timestamp = Utils.getTimestamp();
this.setState({brandImageExists: true, brandImage: null});
this.uploading = false;
},
@@ -193,7 +196,7 @@ class TeamSettings extends React.Component {
img = (
<img
className='brand-img'
src='/api/v1/admin/get_brand_image'
src={Client.getAdminRoute() + '/get_brand_image?t=' + this.timestamp}
/>
);
} else {
@@ -534,6 +537,53 @@ class TeamSettings extends React.Component {
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='EnableOpenServer'
>
<FormattedMessage
id='admin.team.openServerTitle'
defaultMessage='Enable Open Server: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
<input
type='radio'
name='EnableOpenServer'
value='true'
ref='EnableOpenServer'
defaultChecked={this.props.config.TeamSettings.EnableOpenServer}
onChange={this.handleChange}
/>
<FormattedMessage
id='admin.team.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
type='radio'
name='EnableOpenServer'
value='false'
defaultChecked={!this.props.config.TeamSettings.EnableOpenServer}
onChange={this.handleChange}
/>
<FormattedMessage
id='admin.team.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>
<FormattedMessage
id='admin.team.openServerDescription'
defaultMessage='When true, anyone can signup for a user account on this server without the need to be invited.'
/>
</p>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
@@ -610,53 +660,6 @@ class TeamSettings extends React.Component {
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='EnableTeamListing'
>
<FormattedMessage
id='admin.team.dirTitle'
defaultMessage='Enable Team Directory: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
<input
type='radio'
name='EnableTeamListing'
value='true'
ref='EnableTeamListing'
defaultChecked={this.props.config.TeamSettings.EnableTeamListing}
onChange={this.handleChange}
/>
<FormattedMessage
id='admin.team.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
type='radio'
name='EnableTeamListing'
value='false'
defaultChecked={!this.props.config.TeamSettings.EnableTeamListing}
onChange={this.handleChange}
/>
<FormattedMessage
id='admin.team.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>
<FormattedMessage
id='admin.team.dirDesc'
defaultMessage='When true, teams that are configured to show in team directory will show on main page inplace of creating a new team.'
/>
</p>
</div>
</div>
{brand}
<div className='form-group'>

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

@@ -1,7 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import LoadingScreen from '../loading_screen.jsx';
import UserItem from './user_item.jsx';
import ResetPasswordModal from './reset_password_modal.jsx';

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

@@ -1,13 +1,13 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import UserStore from 'stores/user_store.jsx';
import ConfirmModal from '../confirm_modal.jsx';
import TeamStore from 'stores/team_store.jsx';
import {FormattedMessage} from 'react-intl';
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
import React from 'react';
import {browserHistory} from 'react-router';
@@ -22,6 +22,7 @@ export default class UserItem extends React.Component {
this.handleMakeAdmin = this.handleMakeAdmin.bind(this);
this.handleMakeSystemAdmin = this.handleMakeSystemAdmin.bind(this);
this.handleResetPassword = this.handleResetPassword.bind(this);
this.handleResetMfa = this.handleResetMfa.bind(this);
this.handleDemote = this.handleDemote.bind(this);
this.handleDemoteSubmit = this.handleDemoteSubmit.bind(this);
this.handleDemoteCancel = this.handleDemoteCancel.bind(this);
@@ -40,12 +41,9 @@ export default class UserItem extends React.Component {
if (this.props.user.id === me.id) {
this.handleDemote(this.props.user, '');
} else {
const data = {
user_id: this.props.user.id,
new_roles: ''
};
Client.updateRoles(data,
Client.updateRoles(
this.props.user.id,
'',
() => {
this.props.refreshProfiles();
},
@@ -86,12 +84,9 @@ export default class UserItem extends React.Component {
if (this.props.user.id === me.id) {
this.handleDemote(this.props.user, 'admin');
} else {
const data = {
user_id: this.props.user.id,
new_roles: 'admin'
};
Client.updateRoles(data,
Client.updateRoles(
this.props.user.id,
'admin',
() => {
this.props.refreshProfiles();
},
@@ -104,12 +99,10 @@ export default class UserItem extends React.Component {
handleMakeSystemAdmin(e) {
e.preventDefault();
const data = {
user_id: this.props.user.id,
new_roles: 'system_admin'
};
Client.updateRoles(data,
Client.updateRoles(
this.props.user.id,
'system_admin',
() => {
this.props.refreshProfiles();
},
@@ -124,6 +117,19 @@ export default class UserItem extends React.Component {
this.props.doPasswordReset(this.props.user);
}
handleResetMfa(e) {
e.preventDefault();
Client.adminResetMfa(this.props.user.id,
() => {
this.props.refreshProfiles();
},
(err) => {
this.setState({serverError: err.message});
}
);
}
handleDemote(user, role) {
this.setState({
serverError: this.state.serverError,
@@ -143,12 +149,9 @@ export default class UserItem extends React.Component {
}
handleDemoteSubmit() {
const data = {
user_id: this.props.user.id,
new_roles: this.state.role
};
Client.updateRoles(data,
Client.updateRoles(
this.props.user.id,
this.state.role,
() => {
this.setState({
serverError: null,
@@ -211,10 +214,15 @@ export default class UserItem extends React.Component {
const email = user.email;
let showMakeMember = user.roles === 'admin' || user.roles === 'system_admin';
let showMakeAdmin = user.roles === '' || user.roles === 'system_admin';
//let showMakeAdmin = user.roles === '' || user.roles === 'system_admin';
let showMakeAdmin = false;
let showMakeSystemAdmin = user.roles === '' || user.roles === 'admin';
let showMakeActive = false;
let showMakeNotActive = user.roles !== 'system_admin';
let mfaEnabled = global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.MFA === 'true' && global.window.mm_config.EnableMultifactorAuthentication === 'true';
let showMfaReset = mfaEnabled && user.mfa_active;
if (user.delete_at > 0) {
currentRoles = (
@@ -319,6 +327,64 @@ export default class UserItem extends React.Component {
</li>
);
}
let mfaReset = null;
if (showMfaReset) {
mfaReset = (
<li role='presentation'>
<a
role='menuitem'
href='#'
onClick={this.handleResetMfa}
>
<FormattedMessage
id='admin.user_item.resetMfa'
defaultMessage='Remove MFA'
/>
</a>
</li>
);
}
let mfaActiveText;
if (mfaEnabled) {
if (user.mfa_active) {
mfaActiveText = (
<FormattedHTMLMessage
id='admin.user_item.mfaYes'
defaultMessage=', <strong>MFA</strong>: Yes'
/>
);
} else {
mfaActiveText = (
<FormattedHTMLMessage
id='admin.user_item.mfaNo'
defaultMessage=', <strong>MFA</strong>: No'
/>
);
}
}
let authServiceText;
if (user.auth_service) {
authServiceText = (
<FormattedHTMLMessage
id='admin.user_item.authServiceNotEmail'
defaultMessage=', <strong>Sign-in Method:</strong> {service}'
values={{
service: Utils.toTitleCase(user.auth_service)
}}
/>
);
} else {
authServiceText = (
<FormattedHTMLMessage
id='admin.user_item.authServiceEmail'
defaultMessage=', <strong>Sign-in Method:</strong> Email'
/>
);
}
const me = UserStore.getCurrentUser();
let makeDemoteModal = null;
if (this.props.user.id === me.id) {
@@ -368,13 +434,23 @@ export default class UserItem extends React.Component {
<div className='more-modal__row'>
<img
className='more-modal__image pull-left'
src={`/api/v1/users/${user.id}/image?time=${user.update_at}`}
src={`${Client.getUsersRoute()}/${user.id}/image?time=${user.update_at}`}
height='36'
width='36'
/>
<div className='more-modal__details'>
<div className='more-modal__name'>{Utils.getDisplayName(user)}</div>
<div className='more-modal__description'>{email}</div>
<div className='more-modal__description'>
<FormattedHTMLMessage
id='admin.user_item.emailTitle'
defaultMessage='<strong>Email:</strong> {email}'
values={{
email
}}
/>
{authServiceText}
{mfaActiveText}
</div>
</div>
<div className='more-modal__actions'>
<div className='dropdown member-drop'>
@@ -397,6 +473,7 @@ export default class UserItem extends React.Component {
{makeActive}
{makeNotActive}
{makeSystemAdmin}
{mfaReset}
<li role='presentation'>
<a
role='menuitem'

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

@@ -1,7 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';

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

@@ -89,6 +89,8 @@ export default class AddCommand extends React.Component {
/>
)
});
return;
}
if (!command.url) {
@@ -101,12 +103,14 @@ export default class AddCommand extends React.Component {
/>
)
});
return;
}
AsyncClient.addCommand(
command,
() => {
browserHistory.push('/settings/integrations/commands');
browserHistory.push('/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands');
},
(err) => {
this.setState({
@@ -251,7 +255,7 @@ export default class AddCommand extends React.Component {
return (
<div className='backstage-content row'>
<BackstageHeader>
<Link to={'/settings/integrations/commands'}>
<Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands'}>
<FormattedMessage
id='installed_command.header'
defaultMessage='Slash Commands'
@@ -482,7 +486,7 @@ export default class AddCommand extends React.Component {
<FormError errors={[this.state.serverError, this.state.clientError]}/>
<Link
className='btn btn-sm'
to={'/settings/integrations/commands'}
to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands'}
>
<FormattedMessage
id='add_command.cancel'

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

@@ -5,6 +5,7 @@ import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
import {browserHistory} from 'react-router';
import * as Utils from 'utils/utils.jsx';
import BackstageHeader from './backstage_header.jsx';
import ChannelSelect from 'components/channel_select.jsx';
@@ -69,7 +70,7 @@ export default class AddIncomingWebhook extends React.Component {
AsyncClient.addIncomingHook(
hook,
() => {
browserHistory.push('/settings/integrations/incoming_webhooks');
browserHistory.push('/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks');
},
(err) => {
this.setState({
@@ -102,7 +103,7 @@ export default class AddIncomingWebhook extends React.Component {
return (
<div className='backstage-content'>
<BackstageHeader>
<Link to={'/settings/integrations/incoming_webhooks'}>
<Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks'}>
<FormattedMessage
id='installed_incoming_webhooks.header'
defaultMessage='Incoming Webhooks'

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

@@ -5,6 +5,7 @@ import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
import {browserHistory} from 'react-router';
import * as Utils from 'utils/utils.jsx';
import BackstageHeader from './backstage_header.jsx';
import ChannelSelect from 'components/channel_select.jsx';
@@ -109,7 +110,7 @@ export default class AddOutgoingWebhook extends React.Component {
AsyncClient.addOutgoingHook(
hook,
() => {
browserHistory.push('/settings/integrations/outgoing_webhooks');
browserHistory.push('/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks');
},
(err) => {
this.setState({
@@ -154,7 +155,7 @@ export default class AddOutgoingWebhook extends React.Component {
return (
<div className='backstage-content'>
<BackstageHeader>
<Link to={'/settings/integrations/outgoing_webhooks'}>
<Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks'}>
<FormattedMessage
id='installed_outgoing_webhooks.header'
defaultMessage='Outgoing Webhooks'
@@ -273,7 +274,7 @@ export default class AddOutgoingWebhook extends React.Component {
<FormError errors={[this.state.serverError, this.state.clientError]}/>
<Link
className='btn btn-sm'
to={'/settings/integrations/outgoing_webhooks'}
to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks'}
>
<FormattedMessage
id='add_outgoing_webhook.cancel'

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

@@ -46,7 +46,7 @@ export default class BackstageNavbar extends React.Component {
<div className='backstage-navbar row'>
<Link
className='backstage-navbar__back'
to={`/${this.state.team.display_name}/channels/town-square`}
to={`/${this.state.team.name}/channels/town-square`}
>
<i className='fa fa-angle-left'/>
<span>

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

@@ -3,6 +3,7 @@
import React from 'react';
import * as Utils from 'utils/utils.jsx';
import BackstageCategory from './backstage_category.jsx';
import BackstageSection from './backstage_section.jsx';
import {FormattedMessage} from 'react-intl';
@@ -14,7 +15,7 @@ export default class BackstageSidebar extends React.Component {
<ul>
<BackstageCategory
name='integrations'
parentLink={'/settings'}
parentLink={'/' + Utils.getTeamNameFromUrl() + '/settings'}
icon='fa-link'
title={
<FormattedMessage

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

@@ -5,6 +5,7 @@ import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
import IntegrationStore from 'stores/integration_store.jsx';
import * as Utils from 'utils/utils.jsx';
import {FormattedMessage} from 'react-intl';
import InstalledCommand from './installed_command.jsx';
@@ -84,7 +85,7 @@ export default class InstalledCommands extends React.Component {
defaultMessage='Add Slash Command'
/>
}
addLink='/settings/integrations/commands/add'
addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands/add'}
>
{commands}
</InstalledIntegrations>

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

@@ -90,6 +90,17 @@ export default class InstalledIncomingWebhook extends React.Component {
</span>
</div>
{description}
<div className='item-details__row'>
<span className='item-details__url'>
<FormattedMessage
id='installed_integrations.url'
defaultMessage='URL: {url}'
values={{
url: Utils.getWindowLocationOrigin() + '/hooks/' + incomingWebhook.id
}}
/>
</span>
</div>
<div className='tem-details__row'>
<span className='item-details__creation'>
<FormattedMessage

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

@@ -5,6 +5,7 @@ import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
import IntegrationStore from 'stores/integration_store.jsx';
import * as Utils from 'utils/utils.jsx';
import {FormattedMessage} from 'react-intl';
import InstalledIncomingWebhook from './installed_incoming_webhook.jsx';
@@ -76,7 +77,7 @@ export default class InstalledIncomingWebhooks extends React.Component {
defaultMessage='Add Incoming Webhook'
/>
}
addLink='/settings/integrations/incoming_webhooks/add'
addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks/add'}
>
{incomingWebhooks}
</InstalledIntegrations>

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

@@ -5,6 +5,7 @@ import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
import IntegrationStore from 'stores/integration_store.jsx';
import * as Utils from 'utils/utils.jsx';
import {FormattedMessage} from 'react-intl';
import InstalledOutgoingWebhook from './installed_outgoing_webhook.jsx';
@@ -82,7 +83,7 @@ export default class InstalledOutgoingWebhooks extends React.Component {
defaultMessage='Add Outgoing Webhook'
/>
}
addLink='/settings/integrations/outgoing_webhooks/add'
addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks/add'}
>
{outgoingWebhooks}
</InstalledIntegrations>

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

@@ -5,6 +5,7 @@ import React from 'react';
import {FormattedMessage} from 'react-intl';
import IntegrationOption from './integration_option.jsx';
import * as Utils from 'utils/utils.jsx';
import WebhookIcon from 'images/webhook_icon.jpg';
@@ -29,7 +30,7 @@ export default class Integrations extends React.Component {
defaultMessage='Incoming webhooks allow external integrations to send messages'
/>
}
link={'/settings/integrations/incoming_webhooks'}
link={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks'}
/>
);
}
@@ -51,7 +52,7 @@ export default class Integrations extends React.Component {
defaultMessage='Outgoing webhooks allow external integrations to receive and respond to messages'
/>
}
link={'/settings/integrations/outgoing_webhooks'}
link={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks'}
/>
);
}
@@ -73,7 +74,7 @@ export default class Integrations extends React.Component {
defaultMessage='Slash commands send events to an external integration'
/>
}
link={'/settings/integrations/commands'}
link={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands'}
/>
);
}

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

@@ -25,7 +25,7 @@ import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as Utils from 'utils/utils.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import {FormattedMessage} from 'react-intl';

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

@@ -3,33 +3,26 @@
import * as Utils from 'utils/utils.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
import {FormattedMessage} from 'react-intl';
import {Modal} from 'react-bootstrap';
const holders = defineMessages({
notFound: {
id: 'channel_info.notFound',
defaultMessage: 'No Channel Found'
}
});
import React from 'react';
class ChannelInfoModal extends React.Component {
export default class ChannelInfoModal extends React.Component {
render() {
const {formatMessage} = this.props.intl;
let channel = this.props.channel;
if (!channel) {
const notFound = Utils.localizeMessage('channel_info.notFound', 'No Channel Found');
channel = {
display_name: formatMessage(holders.notFound),
name: formatMessage(holders.notFound),
purpose: formatMessage(holders.notFound),
id: formatMessage(holders.notFound)
display_name: notFound,
name: notFound,
purpose: notFound,
id: notFound
};
}
const channelURL = Utils.getShortenedTeamURL() + channel.name;
const channelURL = Utils.getTeamURLFromAddressBar() + '/channels/' + channel.name;
return (
<Modal
@@ -97,10 +90,7 @@ class ChannelInfoModal extends React.Component {
}
ChannelInfoModal.propTypes = {
intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
onHide: React.PropTypes.func.isRequired,
channel: React.PropTypes.object.isRequired
};
export default injectIntl(ChannelInfoModal);
};

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

@@ -4,7 +4,7 @@
import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import {FormattedMessage} from 'react-intl';
import SpinnerButton from 'components/spinner_button.jsx';
@@ -37,13 +37,9 @@ export default class ChannelInviteButton extends React.Component {
addingUser: true
});
const data = {
user_id: this.props.user.id
};
Client.addChannelMember(
this.props.channel.id,
data,
this.props.user.id,
() => {
this.setState({
addingUser: false

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

@@ -9,7 +9,7 @@ import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import {FormattedMessage} from 'react-intl';
@@ -97,12 +97,9 @@ export default class ChannelMembersModal extends React.Component {
handleRemove(user) {
const userId = user.id;
const data = {};
data.user_id = userId;
Client.removeChannelMember(
ChannelStore.getCurrentId(),
data,
userId,
() => {
const memberList = this.state.memberList.slice();
for (let i = 0; i < memberList.length; i++) {

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

@@ -6,7 +6,7 @@ import {Modal} from 'react-bootstrap';
import SettingItemMin from './setting_item_min.jsx';
import SettingItemMax from './setting_item_max.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import {FormattedMessage} from 'react-intl';
@@ -60,7 +60,7 @@ export default class ChannelNotificationsModal extends React.Component {
data.desktop = notifyLevel;
//TODO: This should be moved to event_helpers
Client.updateNotifyProps(data,
Client.updateChannelNotifyProps(data,
() => {
// YUCK
var member = ChannelStore.getMember(channelId);
@@ -252,7 +252,7 @@ export default class ChannelNotificationsModal extends React.Component {
};
//TODO: This should be fixed, moved to event_helpers
Client.updateNotifyProps(data,
Client.updateChannelNotifyProps(data,
() => {
// Yuck...
var member = ChannelStore.getMember(channelId);

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

@@ -54,7 +54,7 @@ export default class ChannelSelect extends React.Component {
];
this.state.channels.forEach((channel) => {
if (channel.type !== Constants.DM_CHANNEL) {
if (channel.type === Constants.OPEN_CHANNEL) {
options.push(
<option
key={channel.id}

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

@@ -1,8 +1,6 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import TeamStore from 'stores/team_store.jsx';
import React from 'react';
import {FormattedMessage} from 'react-intl';
import {Link} from 'react-router';
@@ -13,39 +11,14 @@ export default class Claim extends React.Component {
constructor(props) {
super(props);
this.onTeamChange = this.onTeamChange.bind(this);
this.updateStateFromStores = this.updateStateFromStores.bind(this);
this.state = {};
}
componentWillMount() {
this.setState({
email: this.props.location.query.email,
newType: this.props.location.query.new_type,
oldType: this.props.location.query.old_type,
teamName: this.props.params.team,
teamDisplayName: ''
oldType: this.props.location.query.old_type
});
this.updateStateFromStores();
}
componentDidMount() {
TeamStore.addChangeListener(this.onTeamChange);
}
componentWillUnmount() {
TeamStore.removeChangeListener(this.onTeamChange);
}
updateStateFromStores() {
const team = TeamStore.getByName(this.state.teamName);
let displayName = '';
if (team) {
displayName = team.display_name;
}
this.setState({
teamDisplayName: displayName
});
}
onTeamChange() {
this.updateStateFromStores();
}
render() {
return (
@@ -66,8 +39,6 @@ export default class Claim extends React.Component {
/>
<div id='claim'>
{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
@@ -83,7 +54,6 @@ export default class Claim extends React.Component {
Claim.defaultProps = {
};
Claim.propTypes = {
params: React.PropTypes.object.isRequired,
location: React.PropTypes.object.isRequired,
children: React.PropTypes.node
};

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

@@ -2,12 +2,11 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import React from 'react';
import ReactDOM from 'react-dom';
import {FormattedMessage} from 'react-intl';
import {browserHistory} from 'react-router';
export default class EmailToLDAP extends React.Component {
constructor(props) {
@@ -45,17 +44,14 @@ export default class EmailToLDAP extends React.Component {
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,
Client.emailToLdap(
this.props.email,
password,
ldapId,
ldapPassword,
(data) => {
if (data.follow_link) {
browserHistory.push(data.follow_link);
window.location.href = data.follow_link;
}
},
(error) => {
@@ -74,6 +70,20 @@ export default class EmailToLDAP extends React.Component {
formClass += ' has-error';
}
let loginPlaceholder;
if (global.window.mm_config.LdapLoginFieldName) {
loginPlaceholder = global.window.mm_config.LdapLoginFieldName;
} else {
loginPlaceholder = Utils.localizeMessage('claim.email_to_ldap.ldapId', 'LDAP ID');
}
let passwordPlaceholder;
if (global.window.mm_config.LdapPasswordFieldName) {
passwordPlaceholder = global.window.mm_config.LdapPasswordFieldName;
} else {
passwordPlaceholder = Utils.localizeMessage('claim.email_to_ldap.ldapPwd', 'LDAP Password');
}
return (
<div>
<h3>
@@ -98,9 +108,8 @@ export default class EmailToLDAP extends React.Component {
<p>
<FormattedMessage
id='claim.email_to_ldap.enterPwd'
defaultMessage='Enter the password for your {team} {site} email account'
defaultMessage='Enter the password for your {site} email account'
values={{
team: this.props.teamDisplayName,
site: global.window.mm_config.SiteName
}}
/>
@@ -125,10 +134,6 @@ export default class EmailToLDAP extends React.Component {
<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}>
@@ -138,7 +143,7 @@ export default class EmailToLDAP extends React.Component {
name='ldapId'
ref='ldapid'
autoComplete='off'
placeholder={Utils.localizeMessage('claim.email_to_ldap.ldapId', 'LDAP ID')}
placeholder={loginPlaceholder}
spellCheck='false'
/>
</div>
@@ -149,7 +154,7 @@ export default class EmailToLDAP extends React.Component {
name='ldapPassword'
ref='ldappassword'
autoComplete='off'
placeholder={Utils.localizeMessage('claim.email_to_ldap.ldapPwd', 'LDAP Password')}
placeholder={passwordPlaceholder}
spellCheck='false'
/>
</div>
@@ -172,7 +177,5 @@ export default class EmailToLDAP extends React.Component {
EmailToLDAP.defaultProps = {
};
EmailToLDAP.propTypes = {
email: React.PropTypes.string,
teamName: React.PropTypes.string,
teamDisplayName: React.PropTypes.string
email: React.PropTypes.string
};

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

@@ -2,12 +2,11 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import React from 'react';
import ReactDOM from 'react-dom';
import {FormattedMessage} from 'react-intl';
import {browserHistory} from 'react-router';
export default class EmailToOAuth extends React.Component {
constructor(props) {
@@ -31,16 +30,13 @@ export default class EmailToOAuth extends React.Component {
state.error = null;
this.setState(state);
var postData = {};
postData.password = password;
postData.email = this.props.email;
postData.team_name = this.props.teamName;
postData.service = this.props.newType;
Client.emailToOAuth(postData,
Client.emailToOAuth(
this.props.email,
password,
this.props.newType,
(data) => {
if (data.follow_link) {
browserHistory.push(data.follow_link);
window.location.href = data.follow_link;
}
},
(error) => {
@@ -94,9 +90,8 @@ export default class EmailToOAuth extends React.Component {
<p>
<FormattedMessage
id='claim.email_to_oauth.enterPwd'
defaultMessage='Enter the password for your {team} {site} account'
defaultMessage='Enter the password for your {site} account'
values={{
team: this.props.teamDisplayName,
site: global.window.mm_config.SiteName
}}
/>
@@ -134,7 +129,5 @@ EmailToOAuth.defaultProps = {
};
EmailToOAuth.propTypes = {
newType: React.PropTypes.string,
email: React.PropTypes.string,
teamName: React.PropTypes.string,
teamDisplayName: React.PropTypes.string
email: React.PropTypes.string
};

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

@@ -2,12 +2,11 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import React from 'react';
import ReactDOM from 'react-dom';
import {FormattedMessage} from 'react-intl';
import {browserHistory} from 'react-router';
export default class LDAPToEmail extends React.Component {
constructor(props) {
@@ -45,16 +44,13 @@ export default class LDAPToEmail extends React.Component {
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,
Client.ldapToEmail(
this.props.email,
password,
ldapPassword,
(data) => {
if (data.follow_link) {
browserHistory.push(data.follow_link);
window.location.href = data.follow_link;
}
},
(error) => {
@@ -73,6 +69,13 @@ export default class LDAPToEmail extends React.Component {
formClass += ' has-error';
}
let passwordPlaceholder;
if (global.window.mm_config.LdapPasswordFieldName) {
passwordPlaceholder = global.window.mm_config.LdapPasswordFieldName;
} else {
passwordPlaceholder = Utils.localizeMessage('claim.ldap_to_email.ldapPwd', 'LDAP Password');
}
return (
<div>
<h3>
@@ -100,9 +103,9 @@ export default class LDAPToEmail extends React.Component {
<p>
<FormattedMessage
id='claim.ldap_to_email.enterLdapPwd'
defaultMessage='Enter your LDAP password for your {team} {site} email account'
defaultMessage='Enter your {ldapPassword} for your {site} email account'
values={{
team: this.props.teamDisplayName,
ldapPassword: passwordPlaceholder,
site: global.window.mm_config.SiteName
}}
/>
@@ -113,7 +116,7 @@ export default class LDAPToEmail extends React.Component {
className='form-control'
name='ldapPassword'
ref='ldappassword'
placeholder={Utils.localizeMessage('claim.ldap_to_email.ldapPwd', 'LDAP Password')}
placeholder={passwordPlaceholder}
spellCheck='false'
/>
</div>
@@ -162,7 +165,5 @@ export default class LDAPToEmail extends React.Component {
LDAPToEmail.defaultProps = {
};
LDAPToEmail.propTypes = {
email: React.PropTypes.string,
teamName: React.PropTypes.string,
teamDisplayName: React.PropTypes.string
email: React.PropTypes.string
};

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

@@ -2,7 +2,7 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import React from 'react';
import ReactDOM from 'react-dom';
@@ -38,12 +38,9 @@ export default class OAuthToEmail extends React.Component {
state.error = null;
this.setState(state);
var postData = {};
postData.password = password;
postData.email = this.props.email;
postData.team_name = this.props.teamName;
Client.oauthToEmail(postData,
Client.oauthToEmail(
this.props.email,
password,
(data) => {
if (data.follow_link) {
browserHistory.push(data.follow_link);
@@ -87,10 +84,9 @@ export default class OAuthToEmail extends React.Component {
</p>
<p>
<FormattedMessage
id='claim.oauth_to_email_newPwd'
defaultMessage='Enter a new password for your {team} {site} account'
id='claim.oauth_to_email.enterNewPwd'
defaultMessage='Enter a new password for your {site} account'
values={{
team: this.props.teamDisplayName,
site: global.window.mm_config.SiteName
}}
/>
@@ -137,8 +133,6 @@ export default class OAuthToEmail extends React.Component {
OAuthToEmail.defaultProps = {
};
OAuthToEmail.propTypes = {
teamName: React.PropTypes.string,
teamDisplayName: React.PropTypes.string,
currentType: React.PropTypes.string,
email: React.PropTypes.string
};

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

@@ -4,7 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx';
@@ -148,7 +148,6 @@ class CreateComment extends React.Component {
Client.createPost(
post,
ChannelStore.getCurrent(),
(data) => {
AsyncClient.getPosts(this.props.channelId);

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

@@ -11,7 +11,7 @@ import TutorialTip from './tutorial/tutorial_tip.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
@@ -172,7 +172,7 @@ class CreatePost extends React.Component {
GlobalActions.emitUserPostedEvent(post);
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
Client.createPost(post, channel,
Client.createPost(post,
(data) => {
AsyncClient.getPosts();

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

@@ -3,7 +3,8 @@
import ReactDOM from 'react-dom';
import * as utils from 'utils/utils.jsx';
import * as client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import {Link} from 'react-router';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
@@ -11,11 +12,11 @@ import logoImage from 'images/logo.png';
const holders = defineMessages({
required: {
id: 'team_signup_display_name.required',
id: 'create_team_display_name.required',
defaultMessage: 'This field is required'
},
charLength: {
id: 'team_signup_display_name.charLength',
id: 'create_team_display_name.charLength',
defaultMessage: 'Name must be 4 or more characters up to a maximum of 15'
}
});
@@ -26,16 +27,11 @@ class TeamSignupDisplayNamePage extends React.Component {
constructor(props) {
super(props);
this.submitBack = this.submitBack.bind(this);
this.submitNext = this.submitNext.bind(this);
this.state = {};
}
submitBack(e) {
e.preventDefault();
this.props.state.wizard = 'welcome';
this.props.updateParent(this.props.state);
}
submitNext(e) {
e.preventDefault();
@@ -54,12 +50,14 @@ class TeamSignupDisplayNamePage extends React.Component {
this.props.state.team.name = utils.cleanUpUrlable(displayName);
this.props.updateParent(this.props.state);
}
handleFocus(e) {
e.preventDefault();
e.currentTarget.select();
}
render() {
client.track('signup', 'signup_team_02_name');
Client.track('signup', 'signup_team_02_name');
var nameError = null;
var nameDivClass = 'form-group';
@@ -77,7 +75,7 @@ class TeamSignupDisplayNamePage extends React.Component {
/>
<h2>
<FormattedMessage
id='team_signup_display_name.teamName'
id='create_team_display_name.teamName'
defaultMessage='Team Name'
/>
</h2>
@@ -101,7 +99,7 @@ class TeamSignupDisplayNamePage extends React.Component {
</div>
<div>
<FormattedMessage
id='team_signup_display_name.nameHelp'
id='create_team_display_name.nameHelp'
defaultMessage='Name your team in any language. Your team name shows in menus and headings.'
/>
</div>
@@ -111,20 +109,17 @@ class TeamSignupDisplayNamePage extends React.Component {
onClick={this.submitNext}
>
<FormattedMessage
id='team_signup_display_name.next'
id='create_team_display_name.next'
defaultMessage='Next'
/><i className='glyphicon glyphicon-chevron-right'></i>
</button>
<div className='margin--extra'>
<a
href='#'
onClick={this.submitBack}
>
<Link to='/select_team'>
<FormattedMessage
id='team_signup_display_name.back'
id='create_team_display_name.back'
defaultMessage='Back to previous step'
/>
</a>
</Link>
</div>
</form>
</div>

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

@@ -4,8 +4,11 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
import Constants from 'utils/constants.jsx';
import {browserHistory} from 'react-router';
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
@@ -13,30 +16,30 @@ import logoImage from 'images/logo.png';
const holders = defineMessages({
required: {
id: 'team_signup_url.required',
id: 'create_team_url.required',
defaultMessage: 'This field is required'
},
regex: {
id: 'team_signup_url.regex',
id: 'create_team_url.regex',
defaultMessage: "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash."
},
charLength: {
id: 'team_signup_url.charLength',
id: 'create_team_url.charLength',
defaultMessage: 'Name must be 4 or more characters up to a maximum of 15'
},
taken: {
id: 'team_signup_url.taken',
id: 'create_team_url.taken',
defaultMessage: 'URL is taken or contains a reserved word'
},
unavailable: {
id: 'team_signup_url.unavailable',
id: 'create_team_url.unavailable',
defaultMessage: 'This URL is unavailable. Please try another.'
}
});
import React from 'react';
class TeamSignupUrlPage extends React.Component {
class TeamUrl extends React.Component {
constructor(props) {
super(props);
@@ -48,7 +51,7 @@ class TeamSignupUrlPage extends React.Component {
}
submitBack(e) {
e.preventDefault();
this.props.state.wizard = 'team_display_name';
this.props.state.wizard = 'display_name';
this.props.updateParent(this.props.state);
}
submitNext(e) {
@@ -81,25 +84,39 @@ class TeamSignupUrlPage extends React.Component {
}
}
Client.findTeamByName(name,
(data) => {
if (data) {
this.setState({nameError: formatMessage(holders.unavailable)});
} else {
if (global.window.mm_config.SendEmailNotifications === 'true') {
this.props.state.wizard = 'send_invites';
} else {
this.props.state.wizard = 'username';
}
this.props.state.team.type = 'O';
$('#finish-button').button('loading');
var teamSignup = JSON.parse(JSON.stringify(this.props.state));
teamSignup.team.type = 'O';
teamSignup.team.name = name;
this.props.state.team.name = name;
this.props.updateParent(this.props.state);
}
},
(err) => {
this.setState({nameError: err.message});
}
Client.findTeamByName(name,
(findTeam) => {
if (findTeam) {
this.setState({nameError: formatMessage(holders.unavailable)});
$('#finish-button').button('reset');
} else {
Client.createTeam(teamSignup.team,
(team) => {
Client.track('signup', 'signup_team_08_complete');
$('#sign-up-button').button('reset');
TeamStore.saveTeam(team);
TeamStore.appendTeamMember({team_id: team.id, user_id: UserStore.getCurrentId(), roles: 'admin'});
TeamStore.emitChange();
browserHistory.push('/' + team.name + '/channels/town-square');
},
(err) => {
this.setState({nameError: err.message});
$('#finish-button').button('reset');
}
);
$('#finish-button').button('reset');
}
},
(err) => {
this.setState({nameError: err.message});
$('#finish-button').button('reset');
}
);
}
handleFocus(e) {
@@ -130,7 +147,7 @@ class TeamSignupUrlPage extends React.Component {
/>
<h2>
<FormattedMessage
id='team_signup_url.teamUrl'
id='create_team_url.teamUrl'
defaultMessage='Team URL'
/>
</h2>
@@ -163,13 +180,13 @@ class TeamSignupUrlPage extends React.Component {
</div>
<p>
<FormattedMessage
id='team_signup_url.webAddress'
id='create_team_url.webAddress'
defaultMessage='Choose the web address of your new team:'
/>
</p>
<ul className='color--light'>
<FormattedHTMLMessage
id='team_signup_url.hint'
id='create_team_url.hint'
defaultMessage="<li>Short and memorable is best</li>
<li>Use lowercase letters, numbers and dashes</li>
<li>Must start with a letter and can't end in a dash</li>"
@@ -177,13 +194,14 @@ class TeamSignupUrlPage extends React.Component {
</ul>
<button
type='submit'
id='finish-button'
className='btn btn-primary margin--extra'
onClick={this.submitNext}
>
<FormattedMessage
id='team_signup_url.next'
defaultMessage='Next'
/><i className='glyphicon glyphicon-chevron-right'></i>
id='create_team_password.finish'
defaultMessage='Finish'
/>
</button>
<div className='margin--extra'>
<a
@@ -191,7 +209,7 @@ class TeamSignupUrlPage extends React.Component {
onClick={this.submitBack}
>
<FormattedMessage
id='team_signup_url.back'
id='create_team_url.back'
defaultMessage='Back to previous step'
/>
</a>
@@ -202,10 +220,10 @@ class TeamSignupUrlPage extends React.Component {
}
}
TeamSignupUrlPage.propTypes = {
TeamUrl.propTypes = {
intl: intlShape.isRequired,
state: React.PropTypes.object,
updateParent: React.PropTypes.func
};
export default injectIntl(TeamSignupUrlPage);
export default injectIntl(TeamUrl);

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

@@ -0,0 +1,72 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import ErrorBar from 'components/error_bar.jsx';
import {FormattedMessage} from 'react-intl';
import {browserHistory, Link} from 'react-router';
import React from 'react';
export default class CreateTeam extends React.Component {
constructor(props) {
super(props);
this.submit = this.submit.bind(this);
this.updateParent = this.updateParent.bind(this);
const state = {};
state.team = {};
state.wizard = 'display_name';
this.state = state;
}
submit() {
// todo fill in
}
componentDidMount() {
browserHistory.push('/create_team/display_name');
}
updateParent(state) {
this.setState(state);
browserHistory.push('/create_team/' + state.wizard);
}
render() {
return (
<div>
<ErrorBar/>
<div className='signup-header'>
<Link to='/select_team'>
<span className='fa fa-chevron-left'/>
<FormattedMessage
id='web.header.back'
/>
</Link>
</div>
<div className='col-sm-12'>
<div className='signup-team__container'>
<h1>{global.window.mm_config.SiteName}</h1>
<h4 className='color--light'>
<FormattedMessage
id='web.root.singup_info'
/>
</h4>
<div className='signup__content'>
{React.cloneElement(this.props.children, {
state: this.state,
updateParent: this.updateParent
})}
</div>
</div>
</div>
</div>
);
}
}
CreateTeam.propTypes = {
children: React.PropTypes.node
};

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

@@ -2,7 +2,7 @@
// See License.txt for license information.
import * as AsyncClient from 'utils/async_client.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import {Modal} from 'react-bootstrap';
import TeamStore from 'stores/team_store.jsx';
import Constants from 'utils/constants.jsx';

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import PostStore from 'stores/post_store.jsx';
import ModalStore from 'stores/modal_store.jsx';
import {Modal} from 'react-bootstrap';

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

@@ -2,7 +2,7 @@
// See License.txt for license information.
import {FormattedMessage} from 'react-intl';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import LoadingScreen from './loading_screen.jsx';
import {browserHistory, Link} from 'react-router';
@@ -25,14 +25,14 @@ export default class DoVerifyEmail extends React.Component {
const email = this.props.location.query.email;
Client.verifyEmail(
uid,
hid,
() => {
browserHistory.push('/' + teamName + '/login?extra=verified&email=' + email);
},
(err) => {
this.setState({verifyStatus: 'failure', serverError: err.message});
},
uid,
hid
}
);
}
render() {

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

@@ -3,7 +3,7 @@
import ReactDOM from 'react-dom';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import * as Utils from 'utils/utils.jsx';

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

@@ -4,7 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
@@ -49,12 +49,9 @@ export default class EditChannelPurposeModal extends React.Component {
return;
}
const data = {
channel_id: this.props.channel.id,
channel_purpose: ReactDOM.findDOMNode(this.refs.purpose).value.trim()
};
Client.updateChannelPurpose(data,
Client.updateChannelPurpose(
this.props.channel.id,
ReactDOM.findDOMNode(this.refs.purpose).value.trim(),
() => {
AsyncClient.getChannel(this.props.channel.id);

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
import Textbox from './textbox.jsx';
@@ -33,20 +33,25 @@ class EditPostModal extends React.Component {
this.handleEdit = this.handleEdit.bind(this);
this.handleEditInput = this.handleEditInput.bind(this);
this.handleEditKeyPress = this.handleEditKeyPress.bind(this);
this.handleUserInput = this.handleUserInput.bind(this);
this.handleEditPostEvent = this.handleEditPostEvent.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.onPreferenceChange = this.onPreferenceChange.bind(this);
this.state = {editText: '', title: '', post_id: '', channel_id: '', comments: 0, refocusId: ''};
this.state = {editText: '', originalText: '', title: '', post_id: '', channel_id: '', comments: 0, refocusId: ''};
}
handleEdit() {
var updatedPost = {};
updatedPost.message = this.state.editText.trim();
if (updatedPost.message === this.state.originalText.trim()) {
// no changes so just close the modal
$('#edit_post').modal('hide');
return;
}
if (updatedPost.message.length === 0) {
var tempState = this.state;
delete tempState.editText;
Reflect.deleteProperty(tempState, 'editText');
BrowserStore.setItem('edit_state_transfer', tempState);
$('#edit_post').modal('hide');
GlobalActions.showDeletePostModal(PostStore.getPost(this.state.channel_id, this.state.post_id), this.state.comments);
@@ -79,12 +84,10 @@ class EditPostModal extends React.Component {
this.handleEdit(e);
}
}
handleUserInput(e) {
this.setState({editText: e.target.value});
}
handleEditPostEvent(options) {
this.setState({
editText: options.message || '',
originalText: options.message || '',
title: options.title || '',
post_id: options.postId || '',
channel_id: options.channelId || '',
@@ -108,7 +111,7 @@ class EditPostModal extends React.Component {
var self = this;
$(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', () => {
self.setState({editText: '', title: '', channel_id: '', post_id: '', comments: 0, refocusId: '', error: ''});
self.setState({editText: '', originalText: '', title: '', channel_id: '', post_id: '', comments: 0, refocusId: '', error: ''});
});
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', (e) => {
@@ -116,7 +119,15 @@ class EditPostModal extends React.Component {
if (!button) {
return;
}
self.setState({editText: $(button).attr('data-message'), title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments'), refocusId: $(button).attr('data-refocusid')});
self.setState({
editText: $(button).attr('data-message'),
originalText: $(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', () => {
@@ -163,17 +174,17 @@ class EditPostModal extends React.Component {
aria-label='Close'
onClick={this.handleEditClose}
>
<span aria-hidden='true'>&times;</span>
<span aria-hidden='true'>{'×'}</span>
</button>
<h4 className='modal-title'>
<FormattedMessage
id='edit_post.edit'
defaultMessage='Edit {title}'
values={{
title: this.state.title
}}
/>
</h4>
<h4 className='modal-title'>
<FormattedMessage
id='edit_post.edit'
defaultMessage='Edit {title}'
values={{
title: this.state.title
}}
/>
</h4>
</div>
<div className='edit-modal-body modal-body'>
<Textbox

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

@@ -29,8 +29,8 @@ export default class ErrorBar extends React.Component {
}
componentWillMount() {
if (global.window.mm_config.SendEmailNotifications === 'false') {
ErrorStore.storeLastError({message: Utils.localizeMessage('error_bar.preview_mode', 'Preview Mode: Email notifications have not been configured')});
if (!ErrorStore.getIgnoreEmailPreview() && global.window.mm_config.SendEmailNotifications === 'false') {
ErrorStore.storeLastError({email_preview: true, message: Utils.localizeMessage('error_bar.preview_mode', 'Preview Mode: Email notifications have not been configured')});
this.onErrorChange();
}
}

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

@@ -4,7 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import {intlShape, injectIntl, defineMessages} from 'react-intl';
@@ -100,7 +100,7 @@ class FileAttachment extends React.Component {
getFileInfoFromName(name) {
var fileInfo = utils.splitFileLocation(name);
fileInfo.path = utils.getWindowLocationOrigin() + '/api/v1/files/get' + fileInfo.path;
fileInfo.path = utils.getWindowLocationOrigin() + Client.getFilesRoute() + '/get' + fileInfo.path;
return fileInfo;
}

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

@@ -4,7 +4,7 @@
import $ from 'jquery';
import 'jquery-dragster/jquery.dragster.js';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import * as Utils from 'utils/utils.jsx';
@@ -49,9 +49,9 @@ class FileUpload extends React.Component {
fileUploadSuccess(channelId, data) {
this.props.onFileUpload(data.filenames, data.client_ids, channelId);
var requests = this.state.requests;
const requests = JSON.parse(JSON.stringify(this.state.requests));
for (var j = 0; j < data.client_ids.length; j++) {
delete requests[data.client_ids[j]];
Reflect.deleteProperty(requests, data.client_ids[j]);
}
this.setState({requests});
}
@@ -64,13 +64,13 @@ class FileUpload extends React.Component {
// clear any existing errors
this.props.onUploadError(null);
var channelId = this.props.channelId || ChannelStore.getCurrentId();
const channelId = this.props.channelId || ChannelStore.getCurrentId();
var uploadsRemaining = Constants.MAX_UPLOAD_FILES - this.props.getFileCount(channelId);
var numUploads = 0;
const uploadsRemaining = Constants.MAX_UPLOAD_FILES - this.props.getFileCount(channelId);
let numUploads = 0;
// keep track of how many files have been too large
var tooLargeFiles = [];
const tooLargeFiles = [];
for (let i = 0; i < files.length && numUploads < uploadsRemaining; i++) {
if (files[i].size > Constants.MAX_FILE_SIZE) {
@@ -81,18 +81,15 @@ class FileUpload extends React.Component {
// generate a unique id that can be used by other components to refer back to this upload
const clientId = Utils.generateId();
// prepare data to be uploaded
var formData = new FormData();
formData.append('channel_id', channelId);
formData.append('files', files[i], files[i].name);
formData.append('client_ids', clientId);
var request = Client.uploadFile(formData,
const request = Client.uploadFile(files[i],
files[i].name,
channelId,
clientId,
this.fileUploadSuccess.bind(this, channelId),
this.fileUploadFail.bind(this, clientId)
);
var requests = this.state.requests;
const requests = this.state.requests;
requests[clientId] = request;
this.setState({requests});
@@ -231,8 +228,6 @@ class FileUpload extends React.Component {
// generate a unique id that can be used by other components to refer back to this file upload
var clientId = Utils.generateId();
var formData = new FormData();
formData.append('channel_id', channelId);
var d = new Date();
var hour;
if (d.getHours() < 10) {
@@ -247,16 +242,17 @@ class FileUpload extends React.Component {
min = String(d.getMinutes());
}
var name = formatMessage(holders.pasted) + d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate() + ' ' + hour + '-' + min + '.' + ext;
formData.append('files', file, name);
formData.append('client_ids', clientId);
const name = formatMessage(holders.pasted) + d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate() + ' ' + hour + '-' + min + '.' + ext;
var request = Client.uploadFile(formData,
const request = Client.uploadFile(file,
name,
channelId,
clientId,
self.fileUploadSuccess.bind(self, channelId),
self.fileUploadFail.bind(self, clientId)
);
var requests = self.state.requests;
const requests = self.state.requests;
requests[clientId] = request;
self.setState({requests});
@@ -280,13 +276,13 @@ class FileUpload extends React.Component {
}
cancelUpload(clientId) {
var requests = this.state.requests;
var request = requests[clientId];
const requests = JSON.parse(JSON.stringify(this.state.requests));
const request = requests[clientId];
if (request) {
request.abort();
delete requests[clientId];
Reflect.deleteProperty(requests, clientId);
this.setState({requests});
}
}

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

@@ -113,6 +113,7 @@ class FilteredUserList extends React.Component {
>
<UserList
users={users}
teamMembers={this.props.teamMembers}
actions={this.props.actions}
actionProps={this.props.actionProps}
/>
@@ -124,6 +125,7 @@ class FilteredUserList extends React.Component {
FilteredUserList.defaultProps = {
users: [],
teamMembers: [],
actions: [],
actionProps: {}
};
@@ -131,6 +133,7 @@ FilteredUserList.defaultProps = {
FilteredUserList.propTypes = {
intl: intlShape.isRequired,
users: React.PropTypes.arrayOf(React.PropTypes.object),
teamMembers: React.PropTypes.arrayOf(React.PropTypes.object),
actions: React.PropTypes.arrayOf(React.PropTypes.func),
actionProps: React.PropTypes.object,
style: React.PropTypes.object

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

@@ -9,12 +9,12 @@ import {Link} from 'react-router';
export default class NotLoggedIn extends React.Component {
componentDidMount() {
$('body').attr('class', 'sticky');
$('#root').attr('class', 'container-fluid');
$('body').addClass('sticky');
$('#root').addClass('container-fluid');
}
componentWillUnmount() {
$('body').attr('class', '');
$('#root').attr('class', '');
$('body').removeClass('sticky');
$('#root').removeClass('container-fluid');
}
render() {
return (

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

@@ -5,7 +5,7 @@ import ReactDOM from 'react-dom';
import * as utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
import ModalStore from 'stores/modal_store.jsx';
import UserStore from 'stores/user_store.jsx';

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

@@ -2,39 +2,17 @@
// See License.txt for license information.
import $ from 'jquery';
import LoadingScreen from 'components/loading_screen.jsx';
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';
const TutorialSteps = Constants.TutorialSteps;
const Preferences = Constants.Preferences;
import ErrorBar from 'components/error_bar.jsx';
import * as Websockets from 'action_creators/websocket_actions.jsx';
import LoadingScreen from 'components/loading_screen.jsx';
import Constants from 'utils/constants.jsx';
import {browserHistory} from 'react-router';
import SidebarRight from 'components/sidebar_right.jsx';
import SidebarRightMenu from 'components/sidebar_right_menu.jsx';
import Navbar from 'components/navbar.jsx';
// Modals
import GetPostLinkModal from 'components/get_post_link_modal.jsx';
import GetTeamInviteLinkModal from 'components/get_team_invite_link_modal.jsx';
import EditPostModal from 'components/edit_post_modal.jsx';
import DeletePostModal from 'components/delete_post_modal.jsx';
import MoreChannelsModal from 'components/more_channels.jsx';
import TeamSettingsModal from 'components/team_settings_modal.jsx';
import RemovedFromChannelModal from 'components/removed_from_channel_modal.jsx';
import RegisterAppModal from 'components/register_app_modal.jsx';
import ImportThemeModal from 'components/user_settings/import_theme_modal.jsx';
import InviteMemberModal from 'components/invite_member_modal.jsx';
import SelectTeamModal from 'components/admin_console/select_team_modal.jsx';
const CLIENT_STATUS_INTERVAL = 30000;
const BACKSPACE_CHAR = 8;
@@ -45,19 +23,22 @@ export default class LoggedIn extends React.Component {
super(params);
this.onUserChanged = this.onUserChanged.bind(this);
this.setupUser = this.setupUser.bind(this);
this.state = {
user: null,
profiles: null
user: UserStore.getCurrentUser()
};
}
isValidState() {
return this.state.user != null && this.state.profiles != null;
}
onUserChanged() {
// Grab the current user
const user = UserStore.getCurrentUser();
if (this.state.user) {
this.setupUser(this.state.user);
}
}
isValidState() {
return this.state.user != null;
}
setupUser(user) {
// Update segment indentify
if (global.window.mm_config.SegmentDeveloperKey != null && global.window.mm_config.SegmentDeveloperKey !== '') {
global.window.analytics.identify(user.id, {
@@ -65,7 +46,6 @@ export default class LoggedIn extends React.Component {
email: user.email,
createdAt: user.create_at,
username: user.username,
team_id: user.team_id,
id: user.id
});
}
@@ -78,25 +58,19 @@ export default class LoggedIn extends React.Component {
Utils.applyTheme(Constants.THEMES.default);
}
}
}
// Go to tutorial if we are first arrivign
const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
if (tutorialStep <= TutorialSteps.INTRO_SCREENS) {
browserHistory.push(Utils.getTeamURLFromAddressBar() + '/tutorial');
}
// Get profiles
const profiles = UserStore.getProfiles();
onUserChanged() {
// Grab the current user
const user = UserStore.getCurrentUser();
this.setupUser(user);
this.setState({
user,
profiles
user
});
}
componentWillMount() {
// Emit view action
GlobalActions.viewLoggedIn();
componentWillMount() {
// Listen for user
UserStore.addChangeListener(this.onUserChanged);
@@ -116,7 +90,7 @@ export default class LoggedIn extends React.Component {
}
console.log('detected logout from a different tab'); //eslint-disable-line no-console
browserHistory.push('/' + this.props.params.team);
browserHistory.push('/');
}
if (e.originalEvent.key === '__login__' && e.originalEvent.storageArea === localStorage && e.originalEvent.newValue) {
@@ -170,18 +144,6 @@ export default class LoggedIn extends React.Component {
$('body').addClass('ios');
}
// Set up tracking for whether the window is active
window.isActive = true;
$(window).on('focus', () => {
AsyncClient.updateLastViewedAt();
ChannelStore.resetCounts(ChannelStore.getCurrentId());
ChannelStore.emitChange();
window.isActive = true;
});
$(window).on('blur', () => {
window.isActive = false;
});
// if preferences have already been stored in local storage do not wait until preference store change is fired and handled in channel.jsx
const selectedFont = PreferenceStore.get(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'selected_font', Constants.DEFAULT_FONT);
Utils.applyFont(selectedFont);
@@ -193,13 +155,11 @@ export default class LoggedIn extends React.Component {
}
});
}
componentWillUnmount() {
$('#root').attr('class', '');
clearInterval(this.intervalId);
$(window).off('focus');
$(window).off('blur');
Websockets.close();
UserStore.removeChangeListener(this.onUserChanged);
@@ -211,75 +171,18 @@ export default class LoggedIn extends React.Component {
$(window).off('keydown.preventBackspace');
}
render() {
if (!this.isValidState()) {
return <LoadingScreen/>;
}
let content = [];
if (this.props.children) {
content = this.props.children;
} else {
content.push(
this.props.navbar
);
content.push(
this.props.sidebar
);
content.push(
<div
key='inner-wrap'
className='inner-wrap channel__wrap'
>
<div className='row header'>
<div id='navbar'>
<Navbar/>
</div>
</div>
<div className='row main'>
{React.cloneElement(this.props.center, {
user: this.state.user,
profiles: this.state.profiles
})}
</div>
</div>
);
}
return (
<div className='channel-view'>
<ErrorBar/>
<div className='container-fluid'>
<SidebarRight/>
<SidebarRightMenu/>
{content}
<GetPostLinkModal/>
<GetTeamInviteLinkModal/>
<InviteMemberModal/>
<ImportThemeModal/>
<TeamSettingsModal/>
<MoreChannelsModal/>
<EditPostModal/>
<DeletePostModal/>
<RemovedFromChannelModal/>
<RegisterAppModal/>
<SelectTeamModal/>
</div>
</div>
);
return React.cloneElement(this.props.children, {
user: this.state.user
});
}
}
LoggedIn.defaultProps = {
};
LoggedIn.propTypes = {
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
]),
navbar: React.PropTypes.element,
sidebar: React.PropTypes.element,
center: React.PropTypes.element,
params: React.PropTypes.object
children: React.PropTypes.object
};

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

@@ -5,12 +5,14 @@ import LoginEmail from './components/login_email.jsx';
import LoginUsername from './components/login_username.jsx';
import LoginLdap from './components/login_ldap.jsx';
import LoginMfa from './components/login_mfa.jsx';
import ErrorBar from 'components/error_bar.jsx';
import TeamStore from 'stores/team_store.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import UserStore from 'stores/user_store.jsx';
import Client from 'utils/web_client.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
import * as Client from 'utils/client.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
@@ -18,40 +20,24 @@ import {FormattedMessage} from 'react-intl';
import {browserHistory, Link} from 'react-router';
import React from 'react';
import logoImage from 'images/logo.png';
export default class Login extends React.Component {
constructor(props) {
super(props);
this.getStateFromStores = this.getStateFromStores.bind(this);
this.onTeamChange = this.onTeamChange.bind(this);
this.preSubmit = this.preSubmit.bind(this);
this.submit = this.submit.bind(this);
this.finishSignin = this.finishSignin.bind(this);
const state = this.getStateFromStores();
state.doneCheckLogin = false;
const state = {};
state.showMfa = false;
this.state = state;
}
componentDidMount() {
TeamStore.addChangeListener(this.onTeamChange);
Client.getMeLoggedIn((data) => {
if (data && data.logged_in !== 'false') {
browserHistory.push('/' + this.props.params.team + '/channels/town-square');
} else {
this.setState({doneCheckLogin: true}); //eslint-disable-line react/no-did-mount-set-state
}
});
}
componentWillUnmount() {
TeamStore.removeChangeListener(this.onTeamChange);
}
getStateFromStores() {
return {
currentTeam: TeamStore.getByName(this.props.params.team)
};
}
onTeamChange() {
this.setState(this.getStateFromStores());
if (UserStore.getCurrentUser()) {
browserHistory.push('/select_team');
}
}
preSubmit(method, loginId, password) {
if (global.window.mm_config.EnableMultifactorAuthentication !== 'true') {
@@ -59,7 +45,7 @@ export default class Login extends React.Component {
return;
}
Client.checkMfa(method, this.state.currentTeam.name, loginId,
Client.checkMfa(method, loginId,
(data) => {
if (data.mfa_required === 'true') {
this.setState({showMfa: true, method, loginId, password});
@@ -78,27 +64,41 @@ export default class Login extends React.Component {
}
);
}
finishSignin() {
GlobalActions.emitInitialLoad(
() => {
browserHistory.push('/select_team');
}
);
}
submit(method, loginId, password, token) {
this.setState({showMfa: false, serverEmailError: null, serverUsernameError: null, serverLdapError: null});
const team = this.state.currentTeam.name;
if (method === Constants.EMAIL_SERVICE) {
Client.loginByEmail(team, loginId, password, token,
Client.webLogin(
loginId,
null,
password,
token,
() => {
UserStore.setLastEmail(loginId);
browserHistory.push('/' + team + '/channels/town-square');
this.finishSignin();
},
(err) => {
if (err.id === 'api.user.login.not_verified.app_error') {
browserHistory.push('/should_verify_email?teamname=' + encodeURIComponent(team) + '&email=' + encodeURIComponent(loginId));
browserHistory.push('/should_verify_email?&email=' + encodeURIComponent(loginId));
return;
}
this.setState({serverEmailError: err.message});
}
);
} else if (method === Constants.USERNAME_SERVICE) {
Client.loginByUsername(team, loginId, password, token,
Client.webLogin(
null,
loginId,
password,
token,
() => {
UserStore.setLastUsername(loginId);
@@ -106,7 +106,7 @@ export default class Login extends React.Component {
if (redirect) {
browserHistory.push(decodeURIComponent(redirect));
} else {
browserHistory.push('/' + team + '/channels/town-square');
this.finishSignin();
}
},
(err) => {
@@ -120,13 +120,16 @@ export default class Login extends React.Component {
}
);
} else if (method === Constants.LDAP_SERVICE) {
Client.loginByLdap(team, loginId, password, token,
Client.loginByLdap(
loginId,
password,
token,
() => {
const redirect = Utils.getUrlParameter('redirect');
if (redirect) {
browserHistory.push(decodeURIComponent(redirect));
} else {
browserHistory.push('/' + team + '/channels/town-square');
this.finishSignin();
}
},
(err) => {
@@ -144,7 +147,7 @@ export default class Login extends React.Component {
return (
<div>
<img
src='/api/v1/admin/get_brand_image'
src={Client.getAdminRoute() + '/get_brand_image'}
/>
<p dangerouslySetInnerHTML={{__html: TextFormatting.formatText(text)}}/>
</div>
@@ -153,7 +156,7 @@ export default class Login extends React.Component {
return null;
}
createLoginOptions(currentTeam) {
createLoginOptions() {
const extraParam = Utils.getUrlParameter('extra');
let extraBox = '';
if (extraParam) {
@@ -187,10 +190,19 @@ export default class Login extends React.Component {
/>
</div>
);
} else if (extraParam === Constants.PASSWORD_CHANGE) {
extraBox = (
<div className='alert alert-success'>
<i className='fa fa-check'/>
<FormattedMessage
id='login.passwordChanged'
defaultMessage=' Password updated successfully'
/>
</div>
);
}
}
const teamName = currentTeam.name;
const ldapEnabled = global.window.mm_config.EnableLdap === 'true';
const gitlabSigninEnabled = global.window.mm_config.EnableSignUpWithGitLab === 'true';
const googleSigninEnabled = global.window.mm_config.EnableSignUpWithGoogle === 'true';
@@ -200,10 +212,10 @@ export default class Login extends React.Component {
const oauthLogins = [];
if (gitlabSigninEnabled) {
oauthLogins.push(
<Link
<a
className='btn btn-custom-login gitlab'
key='gitlab'
to={'/api/v1/oauth/gitlab/login?team=' + encodeURIComponent(teamName)}
href={Client.getOAuthRoute() + '/gitlab/login'}
>
<span className='icon'/>
<span>
@@ -212,7 +224,7 @@ export default class Login extends React.Component {
defaultMessage='with GitLab'
/>
</span>
</Link>
</a>
);
}
@@ -221,7 +233,7 @@ export default class Login extends React.Component {
<Link
className='btn btn-custom-login google'
key='google'
to={'/api/v1/oauth/google/login?team=' + encodeURIComponent(teamName)}
to={Client.getOAuthRoute() + '/google/login'}
>
<span className='icon'/>
<span>
@@ -238,7 +250,6 @@ export default class Login extends React.Component {
if (emailSigninEnabled) {
emailLogin = (
<LoginEmail
teamName={teamName}
serverError={this.state.serverEmailError}
submit={this.preSubmit}
/>
@@ -263,7 +274,6 @@ export default class Login extends React.Component {
if (usernameSigninEnabled) {
usernameLogin = (
<LoginUsername
teamName={teamName}
serverError={this.state.serverUsernameError}
submit={this.preSubmit}
/>
@@ -288,7 +298,6 @@ export default class Login extends React.Component {
if (ldapEnabled) {
ldapLogin = (
<LoginLdap
teamName={teamName}
serverError={this.state.serverLdapError}
submit={this.preSubmit}
/>
@@ -309,34 +318,31 @@ export default class Login extends React.Component {
}
}
let userSignUp;
if (currentTeam.allow_open_invite) {
userSignUp = (
<div>
<span>
const userSignUp = (
<div>
<span>
<FormattedMessage
id='login.noAccount'
defaultMessage="Don't have an account? "
/>
<Link
to={'/signup_user_complete'}
className='signup-team-login'
>
<FormattedMessage
id='login.noAccount'
defaultMessage="Don't have an account? "
id='login.create'
defaultMessage='Create one now'
/>
<Link
to={'/signup_user_complete/?id=' + currentTeam.invite_id}
className='signup-team-login'
>
<FormattedMessage
id='login.create'
defaultMessage='Create one now'
/>
</Link>
</span>
</div>
);
}
</Link>
</span>
</div>
);
let forgotPassword;
if (usernameSigninEnabled || emailSigninEnabled) {
forgotPassword = (
<div className='form-group'>
<Link to={'/' + teamName + '/reset_password'}>
<Link to={'/reset_password'}>
<FormattedMessage
id='login.forgot'
defaultMessage='I forgot my password'
@@ -346,23 +352,6 @@ export default class Login extends React.Component {
);
}
let teamSignUp;
if (global.window.mm_config.EnableTeamCreation === 'true' && !Utils.isMobileApp()) {
teamSignUp = (
<div className='margin--extra'>
<Link
to='/'
className='signup-team-login'
>
<FormattedMessage
id='login.createTeam'
defaultMessage='Create a new team'
/>
</Link>
</div>
);
}
return (
<div>
{extraBox}
@@ -372,16 +361,10 @@ export default class Login extends React.Component {
{ldapLogin}
{userSignUp}
{forgotPassword}
{teamSignUp}
</div>
);
}
render() {
const currentTeam = this.state.currentTeam;
if (currentTeam == null || !this.state.doneCheckLogin) {
return <div/>;
}
let content;
let customContent;
let customClass;
@@ -395,7 +378,7 @@ export default class Login extends React.Component {
/>
);
} else {
content = this.createLoginOptions(currentTeam);
content = this.createLoginOptions();
customContent = this.createCustomLogin();
if (customContent) {
customClass = 'branded';
@@ -404,36 +387,23 @@ export default class Login extends React.Component {
return (
<div>
<div className='signup-header'>
<Link to='/'>
<span className='fa fa-chevron-left'/>
<FormattedMessage
id='web.header.back'
/>
</Link>
</div>
<ErrorBar/>
<div className='col-sm-12'>
<div className={'signup-team__container ' + customClass}>
<div className='signup__markdown'>
{customContent}
</div>
<img
className='signup-team-logo'
src={logoImage}
/>
<h1>{global.window.mm_config.SiteName}</h1>
<h4 className='color--light'>
<FormattedMessage
id='web.root.singup_info'
/>
</h4>
<div className='signup__content'>
<h5 className='margin--less'>
<FormattedMessage
id='login.signTo'
defaultMessage='Sign in to:'
/>
</h5>
<h2 className='signup-team__name'>{currentTeam.display_name}</h2>
<h2 className='signup-team__subdomain'>
<FormattedMessage
id='login.on'
defaultMessage='on {siteName}'
values={{
siteName: global.window.mm_config.SiteName
}}
/>
</h2>
{content}
</div>
</div>

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

@@ -4,6 +4,8 @@
import FilteredUserList from './filtered_user_list.jsx';
import TeamMembersDropdown from './team_members_dropdown.jsx';
import UserStore from 'stores/user_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import React from 'react';
@@ -13,18 +15,23 @@ export default class MemberListTeam extends React.Component {
this.getUsers = this.getUsers.bind(this);
this.onChange = this.onChange.bind(this);
this.onTeamChange = this.onTeamChange.bind(this);
this.state = {
users: this.getUsers()
users: this.getUsers(),
teamMembers: TeamStore.getMembersForTeam()
};
}
componentDidMount() {
UserStore.addChangeListener(this.onChange);
TeamStore.addChangeListener(this.onTeamChange);
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
}
componentWillUnmount() {
UserStore.removeChangeListener(this.onChange);
TeamStore.removeChangeListener(this.onTeamChange);
}
getUsers() {
@@ -46,11 +53,18 @@ export default class MemberListTeam extends React.Component {
});
}
onTeamChange() {
this.setState({
teamMembers: TeamStore.getMembersForTeam()
});
}
render() {
return (
<FilteredUserList
style={this.props.style}
users={this.state.users}
teamMembers={this.state.teamMembers}
actions={[TeamMembersDropdown]}
/>
);

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

@@ -4,7 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
import * as client from 'utils/client.jsx';
import client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import LoadingScreen from './loading_screen.jsx';

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

@@ -18,7 +18,7 @@ import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';

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

@@ -6,6 +6,7 @@ import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
import TeamStore from 'stores/team_store.jsx';
import AboutBuildModal from './about_build_modal.jsx';
import TeamMembersModal from './team_members_modal.jsx';
import ToggleModalButton from './toggle_modal_button.jsx';
@@ -25,10 +26,13 @@ export default class NavbarDropdown extends React.Component {
this.handleAboutModal = this.handleAboutModal.bind(this);
this.aboutModalDismissed = this.aboutModalDismissed.bind(this);
this.onTeamChange = this.onTeamChange.bind(this);
this.state = {
showUserSettingsModal: false,
showAboutModal: false
showAboutModal: false,
teams: TeamStore.getAll(),
teamMembers: TeamStore.getTeamMembers()
};
}
handleAboutModal() {
@@ -37,6 +41,7 @@ export default class NavbarDropdown extends React.Component {
aboutModalDismissed() {
this.setState({showAboutModal: false});
}
componentDidMount() {
$(ReactDOM.findDOMNode(this.refs.dropdown)).on('hide.bs.dropdown', () => {
$('.sidebar--left .dropdown-menu').scrollTop(0);
@@ -45,10 +50,22 @@ export default class NavbarDropdown extends React.Component {
this.blockToggle = false;
}, 100);
});
TeamStore.addChangeListener(this.onTeamChange);
}
onTeamChange() {
this.setState({
teams: TeamStore.getAll(),
teamMembers: TeamStore.getTeamMembers()
});
}
componentWillUnmount() {
$(ReactDOM.findDOMNode(this.refs.dropdown)).off('hide.bs.dropdown');
TeamStore.removeChangeListener(this.onTeamChange);
}
render() {
var teamLink = '';
var inviteLink = '';
@@ -130,7 +147,7 @@ export default class NavbarDropdown extends React.Component {
if (isAdmin || window.EnableAdminOnlyIntegrations !== 'true') {
integrationsLink = (
<li>
<Link to={'/settings/integrations'}>
<Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations'}>
<FormattedMessage
id='navbar_dropdown.integrations'
defaultMessage='Integrations'
@@ -163,8 +180,7 @@ export default class NavbarDropdown extends React.Component {
<li key='newTeam_li'>
<Link
key='newTeam_a'
target='_blank'
to={Utils.getWindowLocationOrigin() + '/signup_team'}
to='/create_team'
>
<FormattedMessage
id='navbar_dropdown.create'
@@ -175,6 +191,34 @@ export default class NavbarDropdown extends React.Component {
);
}
if (this.state.teamMembers && this.state.teamMembers.length > 0) {
teams.push(
<li
key='teamDiv'
className='divider'
></li>
);
for (var index in this.state.teamMembers) {
if (this.state.teamMembers.hasOwnProperty(index)) {
var teamMember = this.state.teamMembers[index];
var team = this.state.teams[teamMember.team_id];
if (team.name !== this.props.teamName) {
teams.push(
<li key={'team_' + team.name}>
<Link
to={'/' + team.name + '/channels/town-square'}
>
{team.display_name}
</Link>
</li>
);
}
}
}
}
let helpLink = null;
if (global.window.mm_config.HelpLink) {
helpLink = (
@@ -245,12 +289,15 @@ export default class NavbarDropdown extends React.Component {
{inviteLink}
{teamLink}
<li>
<Link to={'/' + this.props.teamName + '/logout'}>
<a
href='#'
onClick={GlobalActions.emitUserLoggedOutEvent}
>
<FormattedMessage
id='navbar_dropdown.logout'
defaultMessage='Logout'
/>
</Link>
</a>
</li>
{adminDivider}
{teamSettings}

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

@@ -1,22 +1,156 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as GlobalActions from 'action_creators/global_actions.jsx';
import React from 'react';
import $ from 'jquery';
import {browserHistory} from 'react-router';
import * as Utils from 'utils/utils.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
import Constants from 'utils/constants.jsx';
const TutorialSteps = Constants.TutorialSteps;
const Preferences = Constants.Preferences;
import ErrorBar from 'components/error_bar.jsx';
import SidebarRight from 'components/sidebar_right.jsx';
import SidebarRightMenu from 'components/sidebar_right_menu.jsx';
import Navbar from 'components/navbar.jsx';
// Modals
import GetPostLinkModal from 'components/get_post_link_modal.jsx';
import GetTeamInviteLinkModal from 'components/get_team_invite_link_modal.jsx';
import EditPostModal from 'components/edit_post_modal.jsx';
import DeletePostModal from 'components/delete_post_modal.jsx';
import MoreChannelsModal from 'components/more_channels.jsx';
import TeamSettingsModal from 'components/team_settings_modal.jsx';
import RemovedFromChannelModal from 'components/removed_from_channel_modal.jsx';
import RegisterAppModal from 'components/register_app_modal.jsx';
import ImportThemeModal from 'components/user_settings/import_theme_modal.jsx';
import InviteMemberModal from 'components/invite_member_modal.jsx';
import SelectTeamModal from 'components/admin_console/select_team_modal.jsx';
export default class NeedsTeam extends React.Component {
componentWillMount() {
GlobalActions.loadTeamRequiredPage();
constructor(params) {
super(params);
this.onChanged = this.onChanged.bind(this);
this.state = {
profiles: UserStore.getProfiles(),
team: TeamStore.getCurrent()
};
}
onChanged() {
this.setState({
profiles: UserStore.getProfiles(),
team: TeamStore.getCurrent()
});
}
componentWillMount() {
UserStore.addChangeListener(this.onChanged);
TeamStore.addChangeListener(this.onChanged);
// Emit view action
GlobalActions.viewLoggedIn();
// Go to tutorial if we are first arrivign
const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
if (tutorialStep <= TutorialSteps.INTRO_SCREENS) {
browserHistory.push(Utils.getTeamURLFromAddressBar() + '/tutorial');
}
// Set up tracking for whether the window is active
window.isActive = true;
$(window).on('focus', () => {
AsyncClient.updateLastViewedAt();
ChannelStore.resetCounts(ChannelStore.getCurrentId());
ChannelStore.emitChange();
window.isActive = true;
});
$(window).on('blur', () => {
window.isActive = false;
});
}
componentWillUnmount() {
UserStore.removeChangeListener(this.onChanged);
TeamStore.addChangeListener(this.onChanged);
$(window).off('focus');
$(window).off('blur');
}
render() {
return this.props.children;
let content = [];
if (this.props.children) {
content = this.props.children;
} else {
content.push(
this.props.navbar
);
content.push(
this.props.sidebar
);
content.push(
<div
key='inner-wrap'
className='inner-wrap channel__wrap'
>
<div className='row header'>
<div id='navbar'>
<Navbar/>
</div>
</div>
<div className='row main'>
{React.cloneElement(this.props.center, {
user: this.props.user,
profiles: this.state.profiles,
team: this.state.team
})}
</div>
</div>
);
}
return (
<div className='channel-view'>
<ErrorBar/>
<div className='container-fluid'>
<SidebarRight/>
<SidebarRightMenu/>
{content}
<GetPostLinkModal/>
<GetTeamInviteLinkModal/>
<InviteMemberModal/>
<ImportThemeModal/>
<TeamSettingsModal/>
<MoreChannelsModal/>
<EditPostModal/>
<DeletePostModal/>
<RemovedFromChannelModal/>
<RegisterAppModal/>
<SelectTeamModal/>
</div>
</div>
);
}
}
NeedsTeam.defaultProps = {
};
NeedsTeam.propTypes = {
children: React.PropTypes.object
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
]),
navbar: React.PropTypes.element,
sidebar: React.PropTypes.element,
center: React.PropTypes.element,
params: React.PropTypes.object,
user: React.PropTypes.object
};

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

@@ -2,7 +2,7 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import UserStore from 'stores/user_store.jsx';
import NewChannelModal from './new_channel_modal.jsx';
@@ -106,11 +106,7 @@ class NewChannelFlow extends React.Component {
(data) => {
Client.getChannel(
data.id,
(data2, textStatus, xhr) => {
if (xhr.status === 304 || !data2) {
return;
}
(data2) => {
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_CHANNEL,
channel: data2.channel,

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

@@ -32,17 +32,22 @@ export default class NotifyCounts extends React.Component {
this.onListenerChange = this.onListenerChange.bind(this);
this.state = getCountsStateFromStores();
this.mounted = false;
}
componentDidMount() {
this.mounted = true;
ChannelStore.addChangeListener(this.onListenerChange);
}
componentWillUnmount() {
this.mounted = false;
ChannelStore.removeChangeListener(this.onListenerChange);
}
onListenerChange() {
var newState = getCountsStateFromStores();
if (!utils.areObjectsEqual(newState, this.state)) {
this.setState(newState);
if (this.mounted) {
var newState = getCountsStateFromStores();
if (!utils.areObjectsEqual(newState, this.state)) {
this.setState(newState);
}
}
}
render() {

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

@@ -2,7 +2,7 @@
// See License.txt for license information.
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
@@ -40,16 +40,12 @@ class PasswordResetForm extends React.Component {
error: null
});
const data = {};
data.new_password = password;
data.hash = this.props.location.query.h;
data.data = this.props.location.query.d;
data.name = this.props.params.team;
Client.resetPassword(data,
Client.resetPassword(
this.props.location.query.code,
password,
() => {
this.setState({error: null});
browserHistory.push('/' + this.props.params.team + '/login');
browserHistory.push('/login?extra=' + Constants.PASSWORD_CHANGE);
},
(err) => {
this.setState({error: err.message});

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

@@ -4,7 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
import * as client from 'utils/client.jsx';
import client from 'utils/web_client.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
@@ -43,10 +43,8 @@ class PasswordResetSendLink extends React.Component {
error: ''
});
var data = {};
data.email = email;
data.name = this.props.params.team;
client.sendPasswordReset(data,
client.sendPasswordReset(
email,
() => {
this.setState({
error: null,

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

@@ -7,6 +7,7 @@ import UserStore from 'stores/user_store.jsx';
import {Popover, Overlay} from 'react-bootstrap';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
import Client from 'utils/web_client.jsx';
import {FormattedMessage} from 'react-intl';
import {browserHistory} from 'react-router';
@@ -97,7 +98,7 @@ export default class PopoverListMembers extends React.Component {
className='more-modal__image'
width='26px'
height='26px'
src={`/api/v1/users/${m.id}/image?time=${m.update_at}`}
src={`${Client.getUsersRoute()}/${m.id}/image?time=${m.update_at}`}
/>
<div className='more-modal__details'>
<div

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

@@ -10,7 +10,7 @@ import ChannelStore from 'stores/channel_store.jsx';
import Constants from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
@@ -48,7 +48,7 @@ export default class Post extends React.Component {
e.preventDefault();
var post = this.props.post;
Client.createPost(post, post.channel_id,
Client.createPost(post,
(data) => {
AsyncClient.getPosts();

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

@@ -1,7 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import ModalStore from 'stores/modal_store.jsx';
import {Modal} from 'react-bootstrap';

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

@@ -3,7 +3,7 @@
import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import Constants from 'utils/constants.jsx';

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

@@ -9,7 +9,7 @@ import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
import FileAttachmentList from './file_attachment_list.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
var ActionTypes = Constants.ActionTypes;
import * as TextFormatting from 'utils/text_formatting.jsx';
@@ -43,7 +43,7 @@ class RhsComment extends React.Component {
e.preventDefault();
var post = this.props.post;
Client.createPost(post, post.channel_id,
Client.createPost(post,
(data) => {
AsyncClient.getPosts(post.channel_id);
@@ -261,7 +261,7 @@ class RhsComment extends React.Component {
<div className='post__content'>
<div className='post__img'>
<img
src={'/api/v1/users/' + post.user_id + '/image?time=' + timestamp}
src={Client.getUsersRoute() + '/' + post.user_id + '/image?time=' + timestamp}
height='36'
width='36'
/>

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

@@ -3,6 +3,7 @@
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import Constants from 'utils/constants.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
import {FormattedMessage} from 'react-intl';
@@ -21,16 +22,7 @@ export default class RhsHeaderPost extends React.Component {
}
handleClose(e) {
e.preventDefault();
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_SEARCH,
results: null
});
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_POST_SELECTED,
postId: null
});
GlobalActions.emitCloseRightHandSide();
}
handleBack(e) {
e.preventDefault();

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

@@ -1,10 +1,10 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import $ from 'jquery';
//import $ from 'jquery';
//import Client from 'utils/web_client.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
import * as Client from 'utils/client.jsx';
import BrowserStore from 'stores/browser_store.jsx';
import LocalizationStore from 'stores/localization_store.jsx';
import {IntlProvider} from 'react-intl';
@@ -14,6 +14,7 @@ import React from 'react';
import FastClick from 'fastclick';
import {browserHistory} from 'react-router';
import UserStore from 'stores/user_store.jsx';
export default class Root extends React.Component {
constructor(props) {
@@ -29,15 +30,16 @@ export default class Root extends React.Component {
localizationChanged() {
this.setState({locale: LocalizationStore.getLocale(), translations: LocalizationStore.getTranslations()});
}
redirectIfNecessary(props) {
if (props.location.pathname === '/') {
Client.getMeLoggedIn((data) => {
if (!data || data.logged_in === 'false') {
browserHistory.push('/signup_team');
} else {
browserHistory.push('/' + data.team_name + '/channels/town-square');
}
});
if (UserStore.getNoAccounts()) {
browserHistory.push('/signup_user_complete');
} else if (UserStore.getCurrentUser()) {
browserHistory.push('/select_team');
} else {
browserHistory.push('/login');
}
}
}
componentWillReceiveProps(newProps) {
@@ -47,28 +49,6 @@ export default class Root extends React.Component {
// Setup localization listener
LocalizationStore.addChangeListener(this.localizationChanged);
// Browser store check version
BrowserStore.checkVersion();
window.onerror = (msg, url, line, column, stack) => {
var l = {};
l.level = 'ERROR';
l.message = 'msg: ' + msg + ' row: ' + line + ' col: ' + column + ' stack: ' + stack + ' url: ' + url;
$.ajax({
url: '/api/v1/admin/log_client',
dataType: 'json',
contentType: 'application/json',
type: 'POST',
data: JSON.stringify(l)
});
if (window.mm_config.EnableDeveloper === 'true') {
window.ErrorStore.storeLastError({message: 'DEVELOPER MODE: A javascript error has occured. Please use the javascript console to capture and report the error (row: ' + line + ' col: ' + column + ').'});
window.ErrorStore.emitChange();
}
};
// Ya....
/*eslint-disable */
if (window.mm_config.SegmentDeveloperKey != null && window.mm_config.SegmentDeveloperKey !== "") {
@@ -76,11 +56,7 @@ export default class Root extends React.Component {
analytics.load(window.mm_config.SegmentDeveloperKey);
analytics.page();
}}();
} else {
global.window.analytics = {};
global.window.analytics.page = function(){};
global.window.analytics.track = function(){};
}
}
/*eslint-enable */
// Fastclick

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

@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as client from 'utils/client.jsx';
import client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import SearchStore from 'stores/search_store.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';

257
webapp/components/select_team/select_team.jsx Обычный файл
Просмотреть файл

@@ -0,0 +1,257 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import UserStore from 'stores/user_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import * as Utils from 'utils/utils.jsx';
import ErrorBar from 'components/error_bar.jsx';
import LoadingScreen from 'components/loading_screen.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
import {Link} from 'react-router';
import {FormattedMessage} from 'react-intl';
//import {browserHistory, Link} from 'react-router';
import React from 'react';
import logoImage from 'images/logo.png';
export default class Login extends React.Component {
constructor(props) {
super(props);
this.onTeamChange = this.onTeamChange.bind(this);
const state = this.getStateFromStores(false);
this.state = state;
}
componentDidMount() {
TeamStore.addChangeListener(this.onTeamChange);
AsyncClient.getAllTeamListings();
}
componentWillUnmount() {
TeamStore.removeChangeListener(this.onTeamChange);
}
onTeamChange() {
this.setState(this.getStateFromStores(true));
}
getStateFromStores(loaded) {
return {
teams: TeamStore.getAll(),
teamMembers: TeamStore.getTeamMembers(),
teamListings: TeamStore.getTeamListings(),
loaded
};
}
createCustomLogin() {
if (global.window.mm_license.IsLicensed === 'true' &&
global.window.mm_license.CustomBrand === 'true' &&
global.window.mm_config.EnableCustomBrand === 'true') {
const text = global.window.mm_config.CustomBrandText || '';
return (
<div>
<img
src={Client.getAdminRoute() + '/get_brand_image'}
/>
<p dangerouslySetInnerHTML={{__html: TextFormatting.formatText(text)}}/>
</div>
);
}
return null;
}
render() {
var content;
let customClass;
const customContent = this.createCustomLogin();
if (customContent) {
customClass = 'branded';
}
var teamContents = [];
var isAlreadyMember = new Map();
for (var index in this.state.teamMembers) {
if (this.state.teamMembers.hasOwnProperty(index)) {
var teamMember = this.state.teamMembers[index];
var team = this.state.teams[teamMember.team_id];
isAlreadyMember[teamMember.team_id] = true;
teamContents.push(
<div
key={'team_' + team.name}
className='signup-team-dir'
>
<Link
to={'/' + team.name + '/channels/town-square'}
>
<span className='signup-team-dir__name'>{team.display_name}</span>
<span
className='glyphicon glyphicon-menu-right right signup-team-dir__arrow'
aria-hidden='true'
/>
</Link>
</div>
);
}
}
if (!teamContents || teamContents.length === 0) {
teamContents = (
<div className='signup-team-dir-err'>
<div>
<FormattedMessage
id='signup_team.no_teams'
defaultMessage='You do not appear to be a member of any team. Please ask your administrator for an invite, join an open team if one exists or possibly create a new team.'
/>
</div>
</div>
);
}
content = (
<div className='signup__content'>
<h4>
<FormattedMessage
id='signup_team.choose'
defaultMessage='Teams you are a member of:'
/>
</h4>
<div className='signup-team-all'>
{teamContents}
</div>
</div>
);
var openTeamContents = [];
for (var id in this.state.teamListings) {
if (this.state.teamListings.hasOwnProperty(id) && !isAlreadyMember[id]) {
var openTeam = this.state.teamListings[id];
openTeamContents.push(
<div
key={'team_' + openTeam.name}
className='signup-team-dir'
>
<Link
to={`/signup_user_complete/?id=${openTeam.invite_id}`}
>
<span className='signup-team-dir__name'>{openTeam.display_name}</span>
<span
className='glyphicon glyphicon-menu-right right signup-team-dir__arrow'
aria-hidden='true'
/>
</Link>
</div>
);
}
}
var openContent;
if (openTeamContents.length > 0) {
openContent = (
<div className='signup__content'>
<h4>
<FormattedMessage
id='signup_team.join_open'
defaultMessage='Open teams you can join: '
/>
</h4>
<div className='signup-team-all'>
{openTeamContents}
</div>
</div>
);
}
if (!this.state.loaded) {
openContent = <LoadingScreen/>;
}
var isSystemAdmin = Utils.isSystemAdmin(UserStore.getCurrentUser().roles);
let teamSignUp;
if (isSystemAdmin || (global.window.mm_config.EnableTeamCreation === 'true' && !Utils.isMobileApp())) {
teamSignUp = (
<div className='margin--extra'>
<Link
to='/create_team'
className='signup-team-login'
>
<FormattedMessage
id='login.createTeam'
defaultMessage='Create a new team'
/>
</Link>
</div>
);
}
let adminConsoleLink;
if (isSystemAdmin) {
adminConsoleLink = (
<div className='margin--extra'>
<Link
to='/admin_console'
className='signup-team-login'
>
<FormattedMessage
id='navbar_dropdown.console'
defaultMessage='System Console'
/>
</Link>
</div>
);
}
return (
<div>
<ErrorBar/>
<div className='signup-header'>
<a
href='#'
onClick={GlobalActions.emitUserLoggedOutEvent}
>
<span className='fa fa-chevron-left'/>
<FormattedMessage
id='navbar_dropdown.logout'
/>
</a>
</div>
<div className='col-sm-12'>
<div className={'signup-team__container ' + customClass}>
<div className='signup__markdown'>
{customContent}
</div>
<img
className='signup-team-logo'
src={logoImage}
/>
<h1>{global.window.mm_config.SiteName}</h1>
<h4 className='color--light'>
<FormattedMessage
id='web.root.singup_info'
/>
</h4>
{content}
{openContent}
{teamSignUp}
{adminConsoleLink}
</div>
</div>
</div>
);
}
}

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

@@ -2,7 +2,7 @@
// See License.txt for license information.
import {FormattedMessage} from 'react-intl';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import React from 'react';
import {Link} from 'react-router';
@@ -18,19 +18,19 @@ export default class ShouldVerifyEmail extends React.Component {
};
}
handleResend() {
const teamName = this.props.location.query.teamname;
const email = this.props.location.query.email;
this.setState({resendStatus: 'sending'});
Client.resendVerification(() => {
this.setState({resendStatus: 'success'});
},
() => {
this.setState({resendStatus: 'failure'});
},
teamName,
email);
Client.resendVerification(
email,
() => {
this.setState({resendStatus: 'success'});
},
() => {
this.setState({resendStatus: 'failure'});
}
);
}
render() {
let resendConfirm = '';

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

@@ -7,6 +7,7 @@ import NavbarDropdown from './navbar_dropdown.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
import * as Utils from 'utils/utils.jsx';
import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
const Preferences = Constants.Preferences;
@@ -61,7 +62,7 @@ export default class SidebarHeader extends React.Component {
profilePicture = (
<img
className='user__picture'
src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at}
src={Client.getUsersRoute() + '/' + me.id + '/image?time=' + me.update_at}
/>
);
}

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

@@ -228,13 +228,16 @@ export default class SidebarRightMenu extends React.Component {
{manageLink}
{consoleLink}
<li>
<Link to={Utils.getTeamURLFromAddressBar() + '/logout'}>
<a
href='#'
onClick={GlobalActions.emitUserLoggedOutEvent}
>
<i className='fa fa-sign-out'></i>
<FormattedMessage
id='sidebar_right_menu.logout'
defaultMessage='Logout'
/>
</Link>
</a>
</li>
<li className='divider'></li>
{helpLink}

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

@@ -1,235 +0,0 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import ChoosePage from './team_signup_choose_auth.jsx';
import EmailSignUpPage from './team_signup_with_email.jsx';
import SSOSignupPage from './team_signup_with_sso.jsx';
import LdapSignUpPage from './team_signup_with_ldap.jsx';
import Constants from 'utils/constants.jsx';
import TeamStore from 'stores/team_store.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {FormattedMessage} from 'react-intl';
import React from 'react';
import {Link} from 'react-router';
import logoImage from 'images/logo.png';
export default class TeamSignUp extends React.Component {
constructor(props) {
super(props);
this.updatePage = this.updatePage.bind(this);
this.onTeamUpdate = this.onTeamUpdate.bind(this);
var count = 0;
if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
count = count + 1;
}
if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
count = count + 1;
}
if (global.window.mm_config.EnableLdap === 'true') {
count = count + 1;
}
if (count > 1) {
this.state = {page: 'choose'};
} else if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
this.state = {page: 'email'};
} else if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
this.state = {page: 'gitlab'};
} else if (global.window.mm_config.EnableLdap === 'true') {
this.state = {page: 'ldap'};
} else {
this.state = {page: 'none'};
}
}
updatePage(page) {
this.setState({page});
}
componentWillMount() {
if (global.window.mm_config.EnableTeamListing === 'true') {
AsyncClient.getAllTeams();
this.onTeamUpdate();
}
}
componentDidMount() {
TeamStore.addChangeListener(this.onTeamUpdate);
}
componentWillUnmount() {
TeamStore.removeChangeListener(this.onTeamUpdate);
}
onTeamUpdate() {
this.setState({
teams: TeamStore.getAll()
});
}
render() {
let teamListing = null;
if (global.window.mm_config.EnableTeamListing === 'true') {
if (this.state.teams == null) {
teamListing = (<div/>);
} else if (this.state.teams.length === 0) {
if (global.window.mm_config.EnableTeamCreation !== 'true') {
teamListing = (
<div>
<FormattedMessage
id='signup_team.noTeams'
defaultMessage='There are no teams included in the Team Directory and team creation has been disabled.'
/>
</div>
);
}
} else {
teamListing = (
<div>
<h4>
<FormattedMessage
id='signup_team.choose'
defaultMessage='Choose a Team'
/>
</h4>
<div className='signup-team-all'>
{
Object.values(this.state.teams).map((team) => {
if (team.allow_team_listing) {
return (
<div
key={'team_' + team.name}
className='signup-team-dir'
>
<Link
to={'/' + team.name}
>
<span className='signup-team-dir__name'>{team.display_name}</span>
<span
className='glyphicon glyphicon-menu-right right signup-team-dir__arrow'
aria-hidden='true'
/>
</Link>
</div>
);
}
return null;
})
}
</div>
<h4>
<FormattedMessage
id='signup_team.createTeam'
defaultMessage='Or Create a Team'
/>
</h4>
</div>
);
}
}
let signupMethod = null;
let goBack = (
<div className='signup-header'>
<a
href='#'
onClick={
(e) => {
e.preventDefault();
this.updatePage('choose');
}
}
>
<span className='fa fa-chevron-left'/>
<FormattedMessage
id='web.header.back'
/>
</a>
</div>
);
if (global.window.mm_config.EnableTeamCreation !== 'true') {
if (teamListing == null) {
signupMethod = (
<FormattedMessage
id='signup_team.disabled'
defaultMessage='Team creation has been disabled. Please contact an administrator for access.'
/>
);
}
} else if (this.state.page === 'choose') {
signupMethod = (
<ChoosePage
updatePage={this.updatePage}
/>
);
goBack = null;
} else if (this.state.page === 'email') {
signupMethod = (
<div>
<EmailSignUpPage/>
</div>
);
} else if (this.state.page === 'ldap') {
return (
<div>
{teamListing}
<LdapSignUpPage/>
</div>
);
} else if (this.state.page === 'gitlab') {
signupMethod = (
<SSOSignupPage service={Constants.GITLAB_SERVICE}/>
);
} else if (this.state.page === 'google') {
signupMethod = (
<SSOSignupPage service={Constants.GOOGLE_SERVICE}/>
);
} else if (this.state.page === 'none') {
signupMethod = (
<FormattedMessage
id='signup_team.none'
defaultMessage='No team creation method has been enabled. Please contact an administrator for access.'
/>
);
goBack = null;
}
return (
<div>
{goBack}
<div className='col-sm-12'>
<div className='signup-team__container'>
<img
className='signup-team-logo'
src={logoImage}
/>
<h1>{global.window.mm_config.SiteName}</h1>
<h4 className='color--light'>
<FormattedMessage
id='web.root.singup_info'
/>
</h4>
<div id='signup-team'>
{teamListing}
{signupMethod}
</div>
</div>
</div>
</div>
);
}
}
TeamSignUp.propTypes = {
};

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

@@ -74,6 +74,7 @@ export default class SignupTeamComplete extends React.Component {
SignupTeamComplete.defaultProps = {
};
SignupTeamComplete.propTypes = {
location: React.PropTypes.object,
children: React.PropTypes.node

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

@@ -1,89 +0,0 @@
// 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 {intlShape, injectIntl, defineMessages} from 'react-intl';
const holders = defineMessages({
validEmail: {
id: 'team_signup_email.validEmail',
defaultMessage: 'Please enter a valid email address'
},
different: {
id: 'team_signup_email.different',
defaultMessage: 'Please use a different email than the one used at signup'
},
address: {
id: 'team_signup_email.address',
defaultMessage: 'Email Address'
}
});
import React from 'react';
class TeamSignupEmailItem extends React.Component {
constructor(props) {
super(props);
this.getValue = this.getValue.bind(this);
this.validate = this.validate.bind(this);
this.state = {};
}
getValue() {
return ReactDOM.findDOMNode(this.refs.email).value.trim();
}
validate(teamEmail) {
const {formatMessage} = this.props.intl;
const email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
if (!email) {
return true;
}
if (!Utils.isEmail(email)) {
this.setState({emailError: formatMessage(holders.validEmail)});
return false;
} else if (email === teamEmail) {
this.setState({emailError: formatMessage(holders.different)});
return false;
}
this.setState({emailError: ''});
return true;
}
render() {
let emailError = null;
let emailDivClass = 'form-group';
if (this.state.emailError) {
emailError = <label className='control-label'>{this.state.emailError}</label>;
emailDivClass += ' has-error';
}
return (
<div className={emailDivClass}>
<input
autoFocus={this.props.focus}
type='email'
ref='email'
className='form-control'
placeholder={this.props.intl.formatMessage(holders.address)}
defaultValue={this.props.email}
maxLength='128'
spellCheck='false'
/>
{emailError}
</div>
);
}
}
TeamSignupEmailItem.propTypes = {
intl: intlShape.isRequired,
focus: React.PropTypes.bool,
email: React.PropTypes.string
};
export default injectIntl(TeamSignupEmailItem, {withRef: true});

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

@@ -1,17 +0,0 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {FormattedMessage} from 'react-intl';
import React from 'react';
export default class FinishedPage extends React.Component {
render() {
return (
<FormattedMessage
id='signup_team_complete.completed'
defaultMessage="You've already completed the signup process for this invitation or this invitation has expired."
/>
);
}
}

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

@@ -1,225 +0,0 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import BrowserStore from 'stores/browser_store.jsx';
import UserStore from 'stores/user_store.jsx';
import Constants from 'utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
import {browserHistory} from 'react-router';
import logoImage from 'images/logo.png';
const holders = defineMessages({
passwordError: {
id: 'team_signup_password.passwordError',
defaultMessage: 'Please enter at least {chars} characters'
},
creating: {
id: 'team_signup_password.creating',
defaultMessage: 'Creating team...'
}
});
import React from 'react';
class TeamSignupPasswordPage extends React.Component {
constructor(props) {
super(props);
this.submitBack = this.submitBack.bind(this);
this.submitNext = this.submitNext.bind(this);
this.state = {};
}
submitBack(e) {
e.preventDefault();
this.props.state.wizard = 'username';
this.props.updateParent(this.props.state);
}
submitNext(e) {
e.preventDefault();
var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
this.setState({passwordError: this.props.intl.formatMessage(holders.passwordError, {chars: Constants.MIN_PASSWORD_LENGTH})});
return;
}
this.setState({passwordError: null, serverError: null});
$('#finish-button').button('loading');
var teamSignup = JSON.parse(JSON.stringify(this.props.state));
teamSignup.user.password = password;
teamSignup.user.allow_marketing = true;
delete teamSignup.wizard;
Client.createTeamFromSignup(teamSignup,
() => {
Client.track('signup', 'signup_team_08_complete');
var props = this.props;
Client.loginByEmail(
teamSignup.team.name,
teamSignup.team.email,
teamSignup.user.password,
'', // No MFA Token
() => {
UserStore.setLastEmail(teamSignup.team.email);
if (this.props.hash > 0) {
BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: 'finished'}));
}
$('#sign-up-button').button('reset');
props.state.wizard = 'finished';
props.updateParent(props.state, true);
browserHistory.push('/' + teamSignup.team.name + '/channels/town-square');
},
(err) => {
if (err.id === 'api.user.login.not_verified.app_error') {
browserHistory.push('/should_verify_email?email=' + encodeURIComponent(teamSignup.team.email) + '&teamname=' + encodeURIComponent(teamSignup.team.name));
} else {
this.setState({serverError: err.message});
$('#finish-button').button('reset');
}
}
);
},
(err) => {
this.setState({serverError: err.message});
$('#finish-button').button('reset');
}
);
}
render() {
Client.track('signup', 'signup_team_07_password');
var passwordError = null;
var passwordDivStyle = 'form-group';
if (this.state.passwordError) {
passwordError = <div className='form-group has-error'><label className='control-label'>{this.state.passwordError}</label></div>;
passwordDivStyle = ' has-error';
}
var serverError = null;
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
}
return (
<div>
<form>
<img
className='signup-team-logo'
src={logoImage}
/>
<h2 className='margin--less'>
<FormattedMessage
id='team_signup_password.yourPassword'
defaultMessage='Your password'
/>
</h2>
<h5 className='color--light'>
<FormattedMessage
id='team_signup_password.selectPassword'
defaultMessage="Select a password that you'll use to login with your email address:"
/>
</h5>
<div className='inner__content margin--extra'>
<h5><strong>
<FormattedMessage
id='team_signup_password.email'
defaultMessage='Email'
/>
</strong></h5>
<div className='block--gray form-group'>{this.props.state.team.email}</div>
<div className={passwordDivStyle}>
<div className='row'>
<div className='col-sm-11'>
<h5><strong>
<FormattedMessage
id='team_signup_password.choosePwd'
defaultMessage='Choose your password'
/>
</strong></h5>
<input
autoFocus={true}
type='password'
ref='password'
className='form-control'
placeholder=''
maxLength='128'
spellCheck='false'
/>
<span className='color--light help-block'>
<FormattedMessage
id='team_signup_password.hint'
defaultMessage='Passwords must contain {min} to {max} characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.'
values={{
min: Constants.MIN_PASSWORD_LENGTH,
max: Constants.MAX_PASSWORD_LENGTH
}}
/>
</span>
</div>
</div>
{passwordError}
{serverError}
</div>
</div>
<div className='form-group'>
<button
type='submit'
className='btn btn-primary margin--extra'
id='finish-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.creating)}
onClick={this.submitNext}
>
<FormattedMessage
id='team_signup_password.finish'
defaultMessage='Finish'
/>
</button>
</div>
<p>
<FormattedHTMLMessage
id='team_signup_password.agreement'
defaultMessage="By proceeding to create your account and use {siteName}, you agree to our <a href='/static/help/terms.html'>Terms of Service</a> and <a href='/static/help/privacy.html'>Privacy Policy</a>. If you do not agree, you cannot use {siteName}."
values={{
siteName: global.window.mm_config.SiteName
}}
/>
</p>
<div className='margin--extra'>
<a
href='#'
onClick={this.submitBack}
>
<FormattedMessage
id='team_signup_password.back'
defaultMessage='Back to previous step'
/>
</a>
</div>
</form>
</div>
);
}
}
TeamSignupPasswordPage.defaultProps = {
state: {},
hash: ''
};
TeamSignupPasswordPage.propTypes = {
intl: intlShape.isRequired,
state: React.PropTypes.object,
hash: React.PropTypes.string,
updateParent: React.PropTypes.func
};
export default injectIntl(TeamSignupPasswordPage);

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

@@ -1,215 +0,0 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import $ from 'jquery';
import EmailItem from './team_signup_email_item.jsx';
import * as Client from 'utils/client.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
import logoImage from 'images/logo.png';
import React from 'react';
export default class TeamSignupSendInvitesPage extends React.Component {
constructor(props) {
super(props);
this.submitBack = this.submitBack.bind(this);
this.submitNext = this.submitNext.bind(this);
this.submitAddInvite = this.submitAddInvite.bind(this);
this.submitSkip = this.submitSkip.bind(this);
this.keySubmit = this.keySubmit.bind(this);
this.state = {
emailEnabled: global.window.mm_config.SendEmailNotifications === 'true'
};
}
submitBack(e) {
e.preventDefault();
this.props.state.wizard = 'team_url';
this.props.updateParent(this.props.state);
}
submitNext(e) {
e.preventDefault();
var valid = true;
if (this.state.emailEnabled) {
var emails = [];
for (var i = 0; i < this.props.state.invites.length; i++) {
if (this.refs['email_' + i].getWrappedInstance().validate(this.props.state.team.email)) {
emails.push(this.refs['email_' + i].getWrappedInstance().getValue());
} else {
valid = false;
}
}
if (valid) {
this.props.state.invites = emails;
}
}
if (valid) {
this.props.state.wizard = 'username';
this.props.updateParent(this.props.state);
}
}
submitAddInvite(e) {
e.preventDefault();
this.props.state.wizard = 'send_invites';
if (!this.props.state.invites) {
this.props.state.invites = [];
}
this.props.state.invites.push('');
this.props.updateParent(this.props.state);
}
submitSkip(e) {
e.preventDefault();
this.props.state.wizard = 'username';
this.props.updateParent(this.props.state);
}
keySubmit(e) {
if (e && e.keyCode === 13) {
this.submitNext(e);
}
}
componentDidMount() {
if (!this.state.emailEnabled) {
// Must use keypress not keyup due to event chain of pressing enter
$('body').keypress(this.keySubmit);
}
}
componentWillUnmount() {
if (!this.state.emailEnabled) {
$('body').off('keypress', this.keySubmit);
}
}
render() {
Client.track('signup', 'signup_team_05_send_invites');
var content = null;
var bottomContent = null;
if (this.state.emailEnabled) {
var emails = [];
for (var i = 0; i < this.props.state.invites.length; i++) {
if (i === 0) {
emails.push(
<EmailItem
focus={true}
key={i}
ref={'email_' + i}
email={this.props.state.invites[i]}
/>
);
} else {
emails.push(
<EmailItem
focus={false}
key={i}
ref={'email_' + i}
email={this.props.state.invites[i]}
/>
);
}
}
content = (
<div>
{emails}
<div className='form-group text-right'>
<a
href='#'
onClick={this.submitAddInvite}
>
<FormattedMessage
id='team_signup_send_invites.addInvitation'
defaultMessage='Add Invitation'
/>
</a>
</div>
</div>
);
bottomContent = (
<p className='color--light'>
<FormattedHTMLMessage
id='team_signup_send_invites.prefer'
defaultMessage='if you prefer, you can invite team members later<br /> and '
/>
<a
href='#'
onClick={this.submitSkip}
>
<FormattedMessage
id='team_signup_send_invites.skip'
defaultMessage='skip this step '
/>
</a>
<FormattedMessage
id='team_signup_send_invites.forNow'
defaultMessage='for now.'
/>
</p>
);
} else {
content = (
<div className='form-group color--light'>
<FormattedMessage
id='team_signup_send_invites.disabled'
defaultMessage='Email is currently disabled for your team, and emails cannot be sent. Contact your system administrator to enable email and email invitations.'
/>
</div>
);
}
return (
<div>
<form>
<img
className='signup-team-logo'
src={logoImage}
/>
<h2>
<FormattedMessage
id='team_signup_send_invites.title'
defaultMessage='Invite Team Members'
/>
</h2>
{content}
<div className='form-group'>
<button
type='submit'
className='btn-primary btn'
onClick={this.submitNext}
>
<FormattedMessage
id='team_signup_send_invites.next'
defaultMessage='Next'
/><i className='glyphicon glyphicon-chevron-right'/>
</button>
</div>
</form>
{bottomContent}
<div className='margin--extra'>
<a
href='#'
onClick={this.submitBack}
>
<FormattedMessage
id='team_signup_send_invites.back'
defaultMessage='Back to previous step'
/>
</a>
</div>
</div>
);
}
}
TeamSignupSendInvitesPage.propTypes = {
state: React.PropTypes.object.isRequired,
updateParent: React.PropTypes.func.isRequired
};

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

@@ -1,169 +0,0 @@
// 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 Constants from 'utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
import logoImage from 'images/logo.png';
const holders = defineMessages({
reserved: {
id: 'team_signup_username.reserved',
defaultMessage: 'This username is reserved, please choose a new one.'
},
invalid: {
id: 'team_signup_username.invalid',
defaultMessage: 'Username must begin with a letter, and contain between {min} to {max} characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''
}
});
import React from 'react';
class TeamSignupUsernamePage extends React.Component {
constructor(props) {
super(props);
this.submitBack = this.submitBack.bind(this);
this.submitNext = this.submitNext.bind(this);
this.state = {};
}
submitBack(e) {
e.preventDefault();
if (global.window.mm_config.SendEmailNotifications === 'true') {
this.props.state.wizard = 'send_invites';
} else {
this.props.state.wizard = 'team_url';
}
this.props.updateParent(this.props.state);
}
submitNext(e) {
e.preventDefault();
const {formatMessage} = this.props.intl;
var name = ReactDOM.findDOMNode(this.refs.name).value.trim().toLowerCase();
var usernameError = Utils.isValidUsername(name);
if (usernameError === 'Cannot use a reserved word as a username.') { //this should be change to some kind of ID
this.setState({nameError: formatMessage(holders.reserved)});
return;
} else if (usernameError) {
this.setState({nameError: formatMessage(holders.invalid, {min: Constants.MIN_USERNAME_LENGTH, max: Constants.MAX_USERNAME_LENGTH})});
return;
}
this.props.state.wizard = 'password';
this.props.state.user.username = name;
this.props.updateParent(this.props.state);
}
render() {
Client.track('signup', 'signup_team_06_username');
var nameError = null;
var nameHelpText = (
<span className='color--light help-block'>
<FormattedMessage
id='team_signup_username.hint'
defaultMessage="Usernames must begin with a letter and contain between {min} to {max} characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'"
values={{
min: Constants.MIN_USERNAME_LENGTH,
max: Constants.MAX_USERNAME_LENGTH
}}
/>
</span>
);
var nameDivClass = 'form-group';
if (this.state.nameError) {
nameError = <label className='control-label'>{this.state.nameError}</label>;
nameHelpText = '';
nameDivClass += ' has-error';
}
return (
<div>
<form>
<img
className='signup-team-logo'
src={logoImage}
/>
<h2 className='margin--less'>
<FormattedMessage
id='team_signup_username.username'
defaultMessage='Your username'
/>
</h2>
<h5 className='color--light'>
<FormattedMessage
id='team_signup_username.memorable'
defaultMessage='Select a memorable username that makes it easy for teammates to identify you:'
/>
</h5>
<div className='inner__content margin--extra'>
<div className={nameDivClass}>
<div className='row'>
<div className='col-sm-11'>
<h5><strong>
<FormattedMessage
id='team_signup_username.chooseUsername'
defaultMessage='Choose your username'
/>
</strong></h5>
<input
autoFocus={true}
type='text'
ref='name'
className='form-control'
placeholder=''
defaultValue={this.props.state.user.username}
maxLength={Constants.MAX_USERNAME_LENGTH}
spellCheck='false'
/>
{nameHelpText}
</div>
</div>
{nameError}
</div>
</div>
<button
type='submit'
className='btn btn-primary margin--extra'
onClick={this.submitNext}
>
<FormattedMessage
id='team_signup_username.next'
defaultMessage='Next'
/>
<i className='glyphicon glyphicon-chevron-right'></i>
</button>
<div className='margin--extra'>
<a
href='#'
onClick={this.submitBack}
>
<FormattedMessage
id='team_signup_username.back'
defaultMessage='Back to previous step'
/>
</a>
</div>
</form>
</div>
);
}
}
TeamSignupUsernamePage.defaultProps = {
state: null
};
TeamSignupUsernamePage.propTypes = {
intl: intlShape.isRequired,
state: React.PropTypes.object,
updateParent: React.PropTypes.func
};
export default injectIntl(TeamSignupUsernamePage);

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

@@ -1,243 +0,0 @@
// 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 BrowserStore from 'stores/browser_store.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
import {browserHistory} from 'react-router';
import logoImage from 'images/logo.png';
const holders = defineMessages({
storageError: {
id: 'team_signup_welcome.storageError',
defaultMessage: 'This service requires local storage to be enabled. Please enable it or exit private browsing.'
},
validEmailError: {
id: 'team_signup_welcome.validEmailError',
defaultMessage: 'Please enter a valid email address'
},
address: {
id: 'team_signup_welcome.address',
defaultMessage: 'Email Address'
}
});
import React from 'react';
class TeamSignupWelcomePage extends React.Component {
constructor(props) {
super(props);
this.submitNext = this.submitNext.bind(this);
this.handleDiffEmail = this.handleDiffEmail.bind(this);
this.handleDiffSubmit = this.handleDiffSubmit.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
this.state = {useDiff: false};
document.addEventListener('keyup', this.handleKeyPress, false);
}
submitNext(e) {
if (!BrowserStore.isLocalStorageSupported()) {
this.setState({storageError: this.props.intl.formatMessage(holders.storageError)});
return;
}
e.preventDefault();
this.props.state.wizard = 'team_display_name';
this.props.updateParent(this.props.state);
}
handleDiffEmail(e) {
e.preventDefault();
this.setState({useDiff: true});
}
handleDiffSubmit(e) {
e.preventDefault();
const {formatMessage} = this.props.intl;
var state = {useDiff: true, serverError: ''};
var email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
if (!email || !Utils.isEmail(email)) {
state.emailError = formatMessage(holders.validEmailError);
this.setState(state);
return;
} else if (!BrowserStore.isLocalStorageSupported()) {
state.emailError = formatMessage(holders.storageError);
this.setState(state);
return;
}
state.emailError = '';
Client.signupTeam(email,
function success(data) {
if (data.follow_link) {
browserHistory.push(data.follow_link);
} else {
this.props.state.wizard = 'finished';
this.props.updateParent(this.props.state);
browserHistory.push('/signup_team_confirm/?email=' + encodeURIComponent(email));
}
}.bind(this),
function error(err) {
let errorMsg = err.message;
if (err.detailed_error.indexOf('Invalid RCPT TO address provided') >= 0) {
errorMsg = formatMessage(holders.validEmailError);
}
this.setState({emailError: '', serverError: errorMsg});
}.bind(this)
);
}
handleKeyPress(event) {
if (event.keyCode === 13) {
if (this.state.useDiff) {
this.handleDiffSubmit(event);
} else {
this.submitNext(event);
}
}
}
componentWillUnmount() {
document.removeEventListener('keyup', this.handleKeyPress, false);
}
render() {
Client.track('signup', 'signup_team_01_welcome');
var storageError = null;
if (this.state.storageError) {
storageError = <label className='control-label'>{this.state.storageError}</label>;
}
var emailError = null;
var emailDivClass = 'form-group';
if (this.state.emailError) {
emailError = <label className='control-label'>{this.state.emailError}</label>;
emailDivClass += ' has-error';
}
var serverError = null;
if (this.state.serverError) {
serverError = (
<div className='form-group has-error'>
<label className='control-label'>{this.state.serverError}</label>
</div>
);
}
var differentEmailLinkClass = '';
var emailDivContainerClass = 'hidden';
if (this.state.useDiff) {
differentEmailLinkClass = 'hidden';
emailDivContainerClass = '';
}
return (
<div>
<img
className='signup-team-logo'
src={logoImage}
/>
<h3 className='sub-heading'>
<FormattedMessage
id='team_signup_welcome.welcome'
defaultMessage='Welcome to:'
/>
</h3>
<h1 className='margin--top-none'>{global.window.mm_config.SiteName}</h1>
<p className='margin--less'>
<FormattedMessage
id='team_signup_welcome.lets'
defaultMessage="Let's set up your new team"
/>
</p>
<div>
<FormattedMessage
id='team_signup_welcome.confirm'
defaultMessage='Please confirm your email address:'
/>
<br/>
<div className='inner__content'>
<div className='block--gray'>{this.props.state.team.email}</div>
</div>
</div>
<p className='margin--extra color--light'>
<FormattedHTMLMessage
id='team_signup_welcome.admin'
defaultMessage='Your account will administer the new team site. <br />
You can add other administrators later.'
/>
</p>
<div className='form-group'>
<button
className='btn-primary btn form-group'
type='submit'
onClick={this.submitNext}
>
<i className='glyphicon glyphicon-ok'></i>
<FormattedMessage
id='team_signup_welcome.yes'
defaultMessage='Yes, this address is correct'
/>
</button>
{storageError}
</div>
<hr/>
<div className={emailDivContainerClass}>
<div className={emailDivClass}>
<div className='row'>
<div className='col-sm-9'>
<input
type='email'
ref='email'
className='form-control'
placeholder={this.props.intl.formatMessage(holders.address)}
maxLength='128'
spellCheck='false'
/>
</div>
</div>
{emailError}
</div>
{serverError}
<button
className='btn btn-md btn-primary'
type='button'
onClick={this.handleDiffSubmit}
>
<FormattedMessage
id='team_signup_welcome.instead'
defaultMessage='Use this instead'
/>
</button>
</div>
<a
href='#'
onClick={this.handleDiffEmail}
className={differentEmailLinkClass}
>
<FormattedMessage
id='team_signup_welcome.different'
defaultMessage='Use a different email'
/>
</a>
</div>
);
}
}
TeamSignupWelcomePage.defaultProps = {
state: {}
};
TeamSignupWelcomePage.propTypes = {
intl: intlShape.isRequired,
updateParent: React.PropTypes.func.isRequired,
state: React.PropTypes.object
};
export default injectIntl(TeamSignupWelcomePage);

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

@@ -1,49 +0,0 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
import {Link} from 'react-router';
import React from 'react';
export default class SignupTeamConfirm extends React.Component {
render() {
return (
<div>
<div className='signup-header'>
<Link to='/'>
<span className='fa fa-chevron-left'/>
<FormattedMessage
id='web.header.back'
/>
</Link>
</div>
<div className='col-sm-12'>
<div classNameName='signup-team__container'>
<h3>
<FormattedMessage
id='signup_team_confirm.title'
defaultMessage='Sign up Complete'
/>
</h3>
<p>
<FormattedHTMLMessage
id='signup_team_confirm.checkEmail'
defaultMessage='Please check your email: <strong>{email}</strong><br />Your email contains a link to set up your team'
values={{
email: this.props.location.query.email
}}
/>
</p>
</div>
</div>
</div>
);
}
}
SignupTeamConfirm.defaultProps = {
};
SignupTeamConfirm.propTypes = {
location: React.PropTypes.object
};

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

@@ -3,12 +3,13 @@
import LoadingScreen from 'components/loading_screen.jsx';
import LoginLdap from 'components/login/components/login_ldap.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
import BrowserStore from 'stores/browser_store.jsx';
import UserStore from 'stores/user_store.jsx';
import * as Utils from 'utils/utils.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
@@ -24,7 +25,6 @@ class SignupUserComplete extends React.Component {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.inviteInfoRecieved = this.inviteInfoRecieved.bind(this);
this.handleLdapSignup = this.handleLdapSignup.bind(this);
this.state = {
@@ -34,7 +34,10 @@ class SignupUserComplete extends React.Component {
email: '',
teamDisplayName: '',
teamName: '',
teamId: ''
teamId: '',
openServer: false,
loading: true,
inviteId: ''
};
}
componentWillMount() {
@@ -46,19 +49,91 @@ class SignupUserComplete extends React.Component {
let teamDisplayName = '';
let teamName = '';
let teamId = '';
let openServer = false;
let loading = true;
// If we have a hash in the url then we are attempting to access a private team
if (hash) {
const parsedData = JSON.parse(data);
usedBefore = BrowserStore.getGlobalItem(hash);
email = parsedData.email;
teamDisplayName = parsedData.display_name;
teamName = parsedData.name;
teamId = parsedData.id;
if ((inviteId && inviteId.length > 0) || (hash && hash.length > 0)) {
// if we are already logged in then attempt to just join the team
if (UserStore.getCurrentUser()) {
loading = true;
Client.addUserToTeamFromInvite(
data,
hash,
inviteId,
() => {
GlobalActions.emitInitialLoad(
() => {
browserHistory.push('/select_team');
}
);
},
(err) => {
this.setState({
noOpenServerError: true,
serverError: err.message,
loading: false
});
}
);
} else if (hash) {
// If we have a hash in the url then we are attempting to access a private team
const parsedData = JSON.parse(data);
usedBefore = BrowserStore.getGlobalItem(hash);
email = parsedData.email;
teamDisplayName = parsedData.display_name;
teamName = parsedData.name;
teamId = parsedData.id;
loading = false;
} else {
loading = true;
Client.getInviteInfo(
inviteId,
(inviteData) => {
if (!inviteData) {
return;
}
this.setState({
serverError: null,
teamDisplayName: inviteData.display_name,
teamName: inviteData.name,
teamId: inviteData.id,
loading: false
});
},
() => {
this.setState({
noOpenServerError: true,
loading: false,
serverError:
<FormattedMessage
id='signup_user_completed.invalid_invite'
defaultMessage='The invite link was invalid. Please speak with your Administrator to receive an invitation.'
/>
});
}
);
data = '';
hash = '';
}
} else if (global.window.mm_config.EnableOpenServer === 'true' || UserStore.getNoAccounts()) {
// If this is the first account then let them create an account anyway.
// The server will verify it's the first account before allowing creation.
// Of if the server is open then we don't care.
openServer = true;
loading = false;
} else {
Client.getInviteInfo(this.inviteInfoRecieved, null, inviteId);
data = '';
hash = '';
loading = false;
this.setState({
noOpenServerError: true,
serverError:
<FormattedMessage
id='signup_user_completed.no_open_server'
defaultMessage='This server does not allow open signups. Please speak with your Administrator to receive an invitation.'
/>,
loading: false
});
}
this.setState({
@@ -68,29 +143,25 @@ class SignupUserComplete extends React.Component {
email,
teamDisplayName,
teamName,
teamId
});
}
inviteInfoRecieved(data) {
if (!data) {
return;
}
this.setState({
teamDisplayName: data.display_name,
teamName: data.name,
teamId: data.id
teamId,
openServer,
inviteId,
loading
});
}
handleLdapSignup(method, loginId, password, token) {
Client.loginByLdap(this.state.teamName, loginId, password, token,
Client.loginByLdap(loginId, password, token,
() => {
const redirect = Utils.getUrlParameter('redirect');
if (redirect) {
browserHistory.push(decodeURIComponent(redirect));
} else {
browserHistory.push('/' + this.state.teamName + '/channels/town-square');
GlobalActions.emitInitialLoad(
() => {
browserHistory.push('/select_team');
}
);
}
},
(err) => {
@@ -187,28 +258,34 @@ class SignupUserComplete extends React.Component {
});
const user = {
team_id: this.state.teamId,
email: providedEmail,
username: providedUsername,
password: providedPassword,
allow_marketing: true
};
Client.createUser(user, this.state.data, this.state.hash,
Client.createUserWithInvite(user,
this.state.data,
this.state.hash,
this.state.inviteId,
() => {
Client.track('signup', 'signup_user_02_complete');
Client.loginByEmail(
this.state.teamName,
Client.login(
user.email,
null,
user.password,
'', // No MFA Token
'',
() => {
UserStore.setLastEmail(user.email);
if (this.state.hash > 0) {
BrowserStore.setGlobalItem(this.state.hash, JSON.stringify({usedBefore: true}));
}
browserHistory.push('/' + this.state.teamName + '/channels/town-square');
GlobalActions.emitInitialLoad(
() => {
browserHistory.push('/select_team');
}
);
},
(err) => {
if (err.id === 'api.user.login.not_verified.app_error') {
@@ -239,9 +316,7 @@ class SignupUserComplete extends React.Component {
);
}
// If we haven't got a team id yet we are waiting for
// the client so just show the standard loading screen
if (this.state.teamId === '') {
if (this.state.loading) {
return (<LoadingScreen/>);
}
@@ -349,7 +424,7 @@ class SignupUserComplete extends React.Component {
<a
className='btn btn-custom-login gitlab'
key='gitlab'
href={'/api/v1/oauth/gitlab/signup' + window.location.search + '&team=' + encodeURIComponent(this.state.teamName)}
href={Client.getOAuthRoute() + '/gitlab/signup' + window.location.search}
>
<span className='icon'/>
<span>
@@ -367,7 +442,7 @@ class SignupUserComplete extends React.Component {
<a
className='btn btn-custom-login google'
key='google'
href={'/api/v1/oauth/google/signup' + window.location.search + '&team=' + encodeURIComponent(this.state.teamName)}
href={Client.getOAuthRoute() + '/google/signup' + window.location.search + '&team=' + encodeURIComponent(this.state.teamName)}
>
<span className='icon'/>
<span>
@@ -497,12 +572,33 @@ class SignupUserComplete extends React.Component {
);
}
let terms = (
<p>
<FormattedHTMLMessage
id='create_team.agreement'
defaultMessage="By proceeding to create your account and use {siteName}, you agree to our <a href='/static/help/terms.html'>Terms of Service</a> and <a href='/static/help/privacy.html'>Privacy Policy</a>. If you do not agree, you cannot use {siteName}."
values={{
siteName: global.window.mm_config.SiteName
}}
/>
</p>
);
if (this.state.noOpenServerError) {
signupMessage = null;
ldapSignup = null;
emailSignup = null;
terms = null;
}
return (
<div>
<div className='signup-header'>
<Link to='/'>
<span classNameNameName='fa fa-chevron-left'/>
<FormattedMessage id='web.header.back'/>
<span className='fa fa-chevron-left'/>
<FormattedMessage
id='web.header.back'
/>
</Link>
</div>
<div className='col-sm-12'>
@@ -511,22 +607,12 @@ class SignupUserComplete extends React.Component {
className='signup-team-logo'
src={logoImage}
/>
<h5 className='margin--less'>
<h1>{global.window.mm_config.SiteName}</h1>
<h4 className='color--light'>
<FormattedMessage
id='signup_user_completed.welcome'
defaultMessage='Welcome to:'
id='web.root.singup_info'
/>
</h5>
<h2 className='signup-team__name'>{this.state.teamName}</h2>
<h2 className='signup-team__subdomain'>
<FormattedMessage
id='signup_user_completed.onSite'
defaultMessage='on {siteName}'
values={{
siteName: global.window.mm_config.SiteName
}}
/>
</h2>
</h4>
<h4 className='color--light'>
<FormattedMessage
id='signup_user_completed.lets'
@@ -537,6 +623,7 @@ class SignupUserComplete extends React.Component {
{ldapSignup}
{emailSignup}
{serverError}
{terms}
</div>
</div>
</div>

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

@@ -4,6 +4,7 @@
import SuggestionStore from 'stores/suggestion_store.jsx';
import UserStore from 'stores/user_store.jsx';
import * as Utils from 'utils/utils.jsx';
import Client from 'utils/web_client.jsx';
import {FormattedMessage} from 'react-intl';
@@ -42,7 +43,7 @@ class AtMentionSuggestion extends React.Component {
icon = (
<img
className='mention__image'
src={'/api/v1/users/' + item.id + '/image?time=' + item.update_at}
src={Client.getUsersRoute() + '/' + item.id + '/image?time=' + item.update_at}
/>
);
}

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

@@ -3,6 +3,7 @@
import SuggestionStore from 'stores/suggestion_store.jsx';
import UserStore from 'stores/user_store.jsx';
import Client from 'utils/web_client.jsx';
import React from 'react';
@@ -22,7 +23,7 @@ class SearchUserSuggestion extends React.Component {
>
<img
className='profile-img rounded'
src={'/api/v1/users/' + item.id + '/image?time=' + item.update_at}
src={Client.getUsersRoute() + '/' + item.id + '/image?time=' + item.update_at}
/>
<i className='fa fa fa-plus-square'></i>{item.username}
</div>

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

@@ -1,7 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import {FormattedMessage} from 'react-intl';

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

@@ -5,7 +5,7 @@ import $ from 'jquery';
import SettingItemMin from './setting_item_min.jsx';
import SettingItemMax from './setting_item_max.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import TeamStore from 'stores/team_store.jsx';
@@ -42,7 +42,7 @@ const holders = defineMessages({
},
openInviteTitle: {
id: 'general_tab.openInviteTitle',
defaultMessage: 'Allow anyone to sign-up from login page'
defaultMessage: 'Allow anyone to join this team'
},
codeTitle: {
id: 'general_tab.codeTitle',
@@ -68,7 +68,6 @@ class GeneralTab extends React.Component {
this.handleNameSubmit = this.handleNameSubmit.bind(this);
this.handleInviteIdSubmit = this.handleInviteIdSubmit.bind(this);
this.handleOpenInviteSubmit = this.handleOpenInviteSubmit.bind(this);
this.handleTeamListingSubmit = this.handleTeamListingSubmit.bind(this);
this.handleClose = this.handleClose.bind(this);
this.onUpdateNameSection = this.onUpdateNameSection.bind(this);
this.updateName = this.updateName.bind(this);
@@ -76,8 +75,6 @@ class GeneralTab extends React.Component {
this.updateInviteId = this.updateInviteId.bind(this);
this.onUpdateOpenInviteSection = this.onUpdateOpenInviteSection.bind(this);
this.handleOpenInviteRadio = this.handleOpenInviteRadio.bind(this);
this.onUpdateTeamListingSection = this.onUpdateTeamListingSection.bind(this);
this.handleTeamListingRadio = this.handleTeamListingRadio.bind(this);
this.handleGenerateInviteId = this.handleGenerateInviteId.bind(this);
this.state = this.setupInitialState(props);
@@ -96,12 +93,19 @@ class GeneralTab extends React.Component {
name: team.display_name,
invite_id: team.invite_id,
allow_open_invite: team.allow_open_invite,
allow_team_listing: team.allow_team_listing,
serverError: '',
clientError: ''
};
}
componentWillReceiveProps(nextProps) {
this.setState({
name: nextProps.team.display_name,
invite_id: nextProps.team.invite_id,
allow_open_invite: nextProps.team.allow_open_invite
});
}
handleGenerateInviteId(e) {
e.preventDefault();
@@ -117,14 +121,6 @@ class GeneralTab extends React.Component {
this.setState({allow_open_invite: openInvite});
}
handleTeamListingRadio(listing) {
if (global.window.mm_config.EnableTeamListing !== 'true' && listing) {
this.setState({clientError: this.props.intl.formatMessage(holders.dirDisabled)});
} else {
this.setState({allow_team_listing: listing});
}
}
handleOpenInviteSubmit(e) {
e.preventDefault();
@@ -145,26 +141,6 @@ class GeneralTab extends React.Component {
);
}
handleTeamListingSubmit(e) {
e.preventDefault();
var state = {serverError: '', clientError: ''};
var data = this.props.team;
data.allow_team_listing = this.state.allow_team_listing;
Client.updateTeam(data,
(team) => {
TeamStore.saveTeam(team);
TeamStore.emitChange();
this.updateSection('');
},
(err) => {
state.serverError = err.message;
this.setState(state);
}
);
}
handleNameSubmit(e) {
e.preventDefault();
@@ -239,12 +215,6 @@ class GeneralTab extends React.Component {
);
}
componentWillReceiveProps(newProps) {
if (newProps.team && newProps.teamDisplayName) {
this.setState({name: newProps.teamDisplayName});
}
}
handleClose() {
this.updateSection('');
}
@@ -284,15 +254,6 @@ class GeneralTab extends React.Component {
}
}
onUpdateTeamListingSection(e) {
e.preventDefault();
if (this.props.activeSection === 'team_listing') {
this.updateSection('');
} else {
this.updateSection('team_listing');
}
}
updateName(e) {
e.preventDefault();
this.setState({name: e.target.value});
@@ -313,105 +274,8 @@ class GeneralTab extends React.Component {
serverError = this.state.serverError;
}
const enableTeamListing = global.window.mm_config.EnableTeamListing === 'true';
const {formatMessage} = this.props.intl;
let teamListingSection;
if (this.props.activeSection === 'team_listing') {
const inputs = [];
let submitHandle = null;
if (enableTeamListing) {
submitHandle = this.handleTeamListingSubmit;
inputs.push(
<div key='userTeamListingOptions'>
<div className='radio'>
<label>
<input
name='userTeamListingOptions'
type='radio'
defaultChecked={this.state.allow_team_listing}
onChange={this.handleTeamListingRadio.bind(this, true)}
/>
<FormattedMessage
id='general_tab.yes'
defaultMessage='Yes'
/>
</label>
<br/>
</div>
<div className='radio'>
<label>
<input
ref='teamListingRadioNo'
name='userTeamListingOptions'
type='radio'
defaultChecked={!this.state.allow_team_listing}
onChange={this.handleTeamListingRadio.bind(this, false)}
/>
<FormattedMessage
id='general_tab.no'
defaultMessage='No'
/>
</label>
<br/>
</div>
<div>
<br/>
<FormattedMessage
id='general_tab.includeDirDesc'
defaultMessage='Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.'
/>
</div>
</div>
);
} else {
inputs.push(
<div key='userTeamListingOptions'>
<div>
<br/>
<FormattedMessage
id='general_tab.dirContact'
defaultMessage='Contact your system administrator to turn on the team directory on the system home page.'
/>
</div>
</div>
);
}
teamListingSection = (
<SettingItemMax
title={formatMessage(holders.includeDirTitle)}
inputs={inputs}
submit={submitHandle}
server_error={serverError}
client_error={clientError}
updateSection={this.onUpdateTeamListingSection}
/>
);
} else {
let describe = '';
if (enableTeamListing) {
if (this.state.allow_team_listing === true) {
describe = formatMessage(holders.yes);
} else {
describe = formatMessage(holders.no);
}
} else {
describe = formatMessage(holders.dirOff);
}
teamListingSection = (
<SettingItemMin
title={formatMessage(holders.includeDirTitle)}
describe={describe}
updateSection={this.onUpdateTeamListingSection}
/>
);
}
let openInviteSection;
if (this.props.activeSection === 'open_invite') {
const inputs = [
@@ -450,7 +314,7 @@ class GeneralTab extends React.Component {
<br/>
<FormattedMessage
id='general_tab.openInviteDesc'
defaultMessage='When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.'
defaultMessage='When allowed, a link to this team will be including on the landing page allowing anyone with an account to join this team.'
/>
</div>
</div>
@@ -639,8 +503,6 @@ class GeneralTab extends React.Component {
<div className='divider-light'/>
{openInviteSection}
<div className='divider-light'/>
{teamListingSection}
<div className='divider-light'/>
{inviteSection}
<div className='divider-dark'/>
</div>

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

@@ -3,7 +3,7 @@
import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import * as Client from 'utils/client.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
import ConfirmModal from './confirm_modal.jsx';
@@ -38,11 +38,9 @@ export default class TeamMembersDropdown extends React.Component {
if (this.props.user.id === me.id) {
this.handleDemote(this.props.user, '');
} else {
const data = {
user_id: this.props.user.id,
new_roles: ''
};
Client.updateRoles(data,
Client.updateRoles(
this.props.user.id,
'',
() => {
AsyncClient.getProfiles();
},
@@ -79,12 +77,9 @@ export default class TeamMembersDropdown extends React.Component {
if (this.props.user.id === me.id) {
this.handleDemote(this.props.user, 'admin');
} else {
const data = {
user_id: this.props.user.id,
new_roles: 'admin'
};
Client.updateRoles(data,
Client.updateRoles(
this.props.user.id,
'admin',
() => {
AsyncClient.getProfiles();
},
@@ -94,12 +89,13 @@ export default class TeamMembersDropdown extends React.Component {
);
}
}
handleDemote(user, role) {
handleDemote(user, role, newRole) {
this.setState({
serverError: this.state.serverError,
showDemoteModal: true,
user,
role
role,
newRole
});
}
handleDemoteCancel() {
@@ -107,16 +103,14 @@ export default class TeamMembersDropdown extends React.Component {
serverError: null,
showDemoteModal: false,
user: null,
role: null
role: null,
newRole: null
});
}
handleDemoteSubmit() {
const data = {
user_id: this.props.user.id,
new_roles: this.state.role
};
Client.updateRoles(data,
Client.updateRoles(
this.props.user.id,
this.state.newRole,
() => {
const teamUrl = TeamStore.getCurrentTeamUrl();
if (teamUrl) {
@@ -140,6 +134,7 @@ export default class TeamMembersDropdown extends React.Component {
);
}
const teamMember = this.props.teamMember;
const user = this.props.user;
let currentRoles = (
<FormattedMessage
@@ -168,8 +163,10 @@ export default class TeamMembersDropdown extends React.Component {
}
}
let showMakeMember = user.roles === 'admin' || user.roles === 'system_admin';
let showMakeAdmin = user.roles === '' || user.roles === 'system_admin';
let showMakeMember = teamMember.roles === 'admin' || user.roles === 'system_admin';
//let showMakeAdmin = teamMember.roles === '' && user.roles !== 'system_admin';
let showMakeAdmin = false;
let showMakeActive = false;
let showMakeNotActive = user.roles !== 'system_admin';
@@ -331,5 +328,6 @@ export default class TeamMembersDropdown extends React.Component {
}
TeamMembersDropdown.propTypes = {
user: React.PropTypes.object.isRequired
user: React.PropTypes.object.isRequired,
teamMember: React.PropTypes.object.isRequired
};

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

@@ -25,6 +25,7 @@ export default class TeamSettings extends React.Component {
}
onChange() {
var team = TeamStore.getCurrent();
if (!Utils.areObjectsEqual(this.state.team, team)) {
this.setState({team});
}

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

@@ -1,132 +0,0 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {FormattedMessage} from 'react-intl';
import React from 'react';
export default class ChooseAuthPage extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
var buttons = [];
if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
buttons.push(
<a
className='btn btn-custom-login gitlab btn-full'
key='gitlab'
href='#'
onClick={
function clickGit(e) {
e.preventDefault();
this.props.updatePage('gitlab');
}.bind(this)
}
>
<span className='icon'/>
<span>
<FormattedMessage
id='choose_auth_page.gitlabCreate'
defaultMessage='Create new team with GitLab Account'
/>
</span>
</a>
);
}
if (global.window.mm_config.EnableSignUpWithGoogle === 'true') {
buttons.push(
<a
className='btn btn-custom-login google btn-full'
key='google'
href='#'
onClick={
(e) => {
e.preventDefault();
this.props.updatePage('google');
}
}
>
<span className='icon'/>
<span>
<FormattedMessage
id='choose_auth_page.googleCreate'
defaultMessage='Create new team with Google Apps Account'
/>
</span>
</a>
);
}
if (global.window.mm_config.EnableLdap === 'true') {
buttons.push(
<a
className='btn btn-custom-login ldap btn-full'
key='ldap'
href='#'
onClick={
(e) => {
e.preventDefault();
this.props.updatePage('ldap');
}
}
>
<span className='icon'/>
<span>
<FormattedMessage
id='choose_auth_page.ldapCreate'
defaultMessage='Create new team with LDAP Account'
/>
</span>
</a>
);
}
if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
buttons.push(
<a
className='btn btn-custom-login email btn-full'
key='email'
href='#'
onClick={
function clickEmail(e) {
e.preventDefault();
this.props.updatePage('email');
}.bind(this)
}
>
<span className='fa fa-envelope'/>
<span>
<FormattedMessage
id='choose_auth_page.emailCreate'
defaultMessage='Create new team with email address'
/>
</span>
</a>
);
}
if (buttons.length === 0) {
buttons = (
<span>
<FormattedMessage
id='choose_auth_page.noSignup'
defaultMessage='No sign-up methods configured, please contact your system administrator.'
/>
</span>
);
}
return (
<div>
{buttons}
</div>
);
}
}
ChooseAuthPage.propTypes = {
updatePage: React.PropTypes.func
};

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

@@ -1,116 +0,0 @@
// 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 {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
import {browserHistory} from 'react-router';
const holders = defineMessages({
emailError: {
id: 'email_signup.emailError',
defaultMessage: 'Please enter a valid email address'
},
address: {
id: 'email_signup.address',
defaultMessage: 'Email Address'
}
});
import React from 'react';
class EmailSignUpPage extends React.Component {
constructor() {
super();
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {};
}
handleSubmit(e) {
e.preventDefault();
const team = {};
const state = {serverError: null};
let isValid = true;
team.email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
if (!team.email || !Utils.isEmail(team.email)) {
state.emailError = this.props.intl.formatMessage(holders.emailError);
isValid = false;
} else {
state.emailError = null;
}
if (!isValid) {
this.setState(state);
return;
}
Client.signupTeam(team.email,
(data) => {
if (data.follow_link) {
browserHistory.push(data.follow_link);
} else {
browserHistory.push(`/signup_team_confirm/?email=${encodeURIComponent(team.email)}`);
}
},
(err) => {
state.serverError = err.message;
this.setState(state);
}
);
}
render() {
let serverError = null;
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
}
let emailError = null;
if (this.state.emailError) {
emailError = <div className='form-group has-error'><label className='control-label'>{this.state.emailError}</label></div>;
}
return (
<form
role='form'
onSubmit={this.handleSubmit}
>
<div className='form-group'>
<input
autoFocus={true}
type='email'
ref='email'
className='form-control'
placeholder={this.props.intl.formatMessage(holders.address)}
maxLength='128'
spellCheck='false'
/>
{emailError}
</div>
<div className='form-group'>
<button
className='btn btn-md btn-primary'
type='submit'
>
<FormattedMessage
id='email_signup.createTeam'
defaultMessage='Create Team'
/>
</button>
{serverError}
</div>
</form>
);
}
}
EmailSignUpPage.defaultProps = {
};
EmailSignUpPage.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(EmailSignUpPage);

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше