signup_email.jsx 18.3 KB
Newer Older
1
2
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
3

4
5
6
import PropTypes from 'prop-types';
import React from 'react';
import {FormattedHTMLMessage, FormattedMessage} from 'react-intl';
7
import {Link} from 'react-router-dom';
8

9
import {trackEvent} from 'actions/diagnostics_actions.jsx';
10
import * as GlobalActions from 'actions/global_actions.jsx';
11
import {getInviteInfo} from 'actions/team_actions.jsx';
12
13
import {createUserWithInvite, loadMe, loginById} from 'actions/user_actions.jsx';
import BrowserStore from 'stores/browser_store.jsx';
14
15

import {browserHistory} from 'utils/browser_history';
16
import Constants from 'utils/constants.jsx';
17
import * as Utils from 'utils/utils.jsx';
18

19
import logoImage from 'images/logo.png';
20

21
22
import BackButton from 'components/common/back_button.jsx';
import LoadingScreen from 'components/loading_screen.jsx';
23
import SiteNameAndDescription from 'components/common/site_name_and_description';
24

25
26
27
export default class SignupEmail extends React.Component {
    static get propTypes() {
        return {
28
29
30
31
32
            location: PropTypes.object,
            enableSignUpWithEmail: PropTypes.bool.isRequired,
            siteName: PropTypes.string,
            termsOfServiceLink: PropTypes.string,
            privacyPolicyLink: PropTypes.string,
33
            customDescriptionText: PropTypes.string,
34
            passwordConfig: PropTypes.object,
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
        };
    }

    constructor(props) {
        super(props);

        this.handleSubmit = this.handleSubmit.bind(this);

        this.getInviteInfo = this.getInviteInfo.bind(this);
        this.renderEmailSignup = this.renderEmailSignup.bind(this);
        this.isUserValid = this.isUserValid.bind(this);

        this.state = this.getInviteInfo();
    }

50
51
52
53
    componentDidMount() {
        trackEvent('signup', 'signup_user_01_welcome');
    }

54
    getInviteInfo() {
55
        let data = (new URLSearchParams(this.props.location.search)).get('d');
56
        let token = (new URLSearchParams(this.props.location.search)).get('t');
57
        const inviteId = (new URLSearchParams(this.props.location.search)).get('id');
58
59
60
61
        let email = '';
        let teamDisplayName = '';
        let teamName = '';
        let teamId = '';
62
63
64
        let loading = false;
        const serverError = '';
        const noOpenServerError = false;
65

66
        if (token && token.length > 0) {
67
68
69
70
71
72
73
            const parsedData = JSON.parse(data);
            email = parsedData.email;
            teamDisplayName = parsedData.display_name;
            teamName = parsedData.name;
            teamId = parsedData.id;
        } else if (inviteId && inviteId.length > 0) {
            loading = true;
74
            getInviteInfo(
75
76
77
                inviteId,
                (inviteData) => {
                    if (!inviteData) {
78
                        this.setState({loading: false});
79
80
81
                        return;
                    }

82
83
84
85
86
                    this.setState({
                        loading: false,
                        serverError: '',
                        teamDisplayName: inviteData.display_name,
                        teamName: inviteData.name,
87
                        teamId: inviteData.id,
88
                    });
89
90
                },
                () => {
91
92
93
94
95
96
97
98
                    this.setState({
                        loading: false,
                        noOpenServerError: true,
                        serverError: (
                            <FormattedMessage
                                id='signup_user_completed.invalid_invite'
                                defaultMessage='The invite link was invalid.  Please speak with your Administrator to receive an invitation.'
                            />
99
                        ),
100
                    });
101
102
103
104
                }
            );

            data = null;
105
            token = null;
106
107
108
109
        }

        return {
            data,
110
            token,
111
112
113
114
115
116
117
            email,
            teamDisplayName,
            teamName,
            teamId,
            inviteId,
            loading,
            serverError,
118
            noOpenServerError,
119
120
121
122
        };
    }

    handleSignupSuccess(user, data) {
123
        trackEvent('signup', 'signup_user_02_complete');
124
        loginById(
125
126
127
            data.id,
            user.password,
            '',
128
            () => {
129
130
                if (this.state.token > 0) {
                    BrowserStore.setGlobalItem(this.state.token, JSON.stringify({usedBefore: true}));
131
132
                }

133
                loadMe().then(
134
                    () => {
135
136
137
                        const redirectTo = (new URLSearchParams(this.props.location.search)).get('redirect_to');
                        if (redirectTo) {
                            browserHistory.push(redirectTo);
138
139
140
141
142
143
                        } else {
                            GlobalActions.redirectUserToDefaultTeam();
                        }
                    }
                );
            },
144
145
146
147
            (err) => {
                if (err.id === 'api.user.login.not_verified.app_error') {
                    browserHistory.push('/should_verify_email?email=' + encodeURIComponent(user.email) + '&teamname=' + encodeURIComponent(this.state.teamName));
                } else {
148
149
                    this.setState({
                        serverError: err.message,
150
                        isSubmitting: false,
151
                    });
152
153
154
155
156
157
158
159
160
161
162
163
                }
            }
        );
    }

    isUserValid() {
        const providedEmail = this.refs.email.value.trim();
        if (!providedEmail) {
            this.setState({
                nameError: '',
                emailError: (<FormattedMessage id='signup_user_completed.required'/>),
                passwordError: '',
164
                serverError: '',
165
166
167
168
169
170
171
172
173
            });
            return false;
        }

        if (!Utils.isEmail(providedEmail)) {
            this.setState({
                nameError: '',
                emailError: (<FormattedMessage id='signup_user_completed.validEmail'/>),
                passwordError: '',
174
                serverError: '',
175
176
177
178
179
180
181
182
183
184
            });
            return false;
        }

        const providedUsername = this.refs.name.value.trim().toLowerCase();
        if (!providedUsername) {
            this.setState({
                nameError: (<FormattedMessage id='signup_user_completed.required'/>),
                emailError: '',
                passwordError: '',
185
                serverError: '',
186
187
188
189
190
191
192
193
194
195
            });
            return false;
        }

        const usernameError = Utils.isValidUsername(providedUsername);
        if (usernameError === 'Cannot use a reserved word as a username.') {
            this.setState({
                nameError: (<FormattedMessage id='signup_user_completed.reserved'/>),
                emailError: '',
                passwordError: '',
196
                serverError: '',
197
198
199
200
201
202
203
204
205
            });
            return false;
        } else if (usernameError) {
            this.setState({
                nameError: (
                    <FormattedMessage
                        id='signup_user_completed.usernameLength'
                        values={{
                            min: Constants.MIN_USERNAME_LENGTH,
206
                            max: Constants.MAX_USERNAME_LENGTH,
207
208
209
210
211
                        }}
                    />
                ),
                emailError: '',
                passwordError: '',
212
                serverError: '',
213
214
215
216
217
            });
            return false;
        }

        const providedPassword = this.refs.password.value;
218
219
        const {valid, error} = Utils.isValidPassword(providedPassword, this.props.passwordConfig);
        if (!valid && error) {
220
221
222
            this.setState({
                nameError: '',
                emailError: '',
223
                passwordError: error,
224
                serverError: '',
225
226
227
228
229
230
231
232
233
234
            });
            return false;
        }

        return true;
    }

    handleSubmit(e) {
        e.preventDefault();

235
236
237
238
239
        // bail out if a submission is already in progress
        if (this.state.isSubmitting) {
            return;
        }

240
241
242
243
244
        if (this.isUserValid()) {
            this.setState({
                nameError: '',
                emailError: '',
                passwordError: '',
245
                serverError: '',
246
                isSubmitting: true,
247
248
249
250
251
252
            });

            const user = {
                email: this.refs.email.value.trim(),
                username: this.refs.name.value.trim().toLowerCase(),
                password: this.refs.password.value,
253
                allow_marketing: true,
254
255
            };

256
            createUserWithInvite(user,
257
                this.state.token,
258
259
260
                this.state.inviteId,
                this.handleSignupSuccess.bind(this, user),
                (err) => {
261
262
                    this.setState({
                        serverError: err.message,
263
                        isSubmitting: false,
264
                    });
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
                }
            );
        }
    }

    renderEmailSignup() {
        let emailError = null;
        let emailHelpText = (
            <span className='help-block'>
                <FormattedMessage
                    id='signup_user_completed.emailHelp'
                    defaultMessage='Valid email required for sign-up'
                />
            </span>
        );
        let emailDivStyle = 'form-group';
        if (this.state.emailError) {
            emailError = (<label className='control-label'>{this.state.emailError}</label>);
            emailHelpText = '';
            emailDivStyle += ' has-error';
        }

        let nameError = null;
        let nameHelpText = (
            <span className='help-block'>
                <FormattedMessage
                    id='signup_user_completed.userHelp'
                    defaultMessage="Username must begin with a letter, and contain between {min} to {max} lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'"
                    values={{
                        min: Constants.MIN_USERNAME_LENGTH,
295
                        max: Constants.MAX_USERNAME_LENGTH,
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
                    }}
                />
            </span>
        );
        let nameDivStyle = 'form-group';
        if (this.state.nameError) {
            nameError = <label className='control-label'>{this.state.nameError}</label>;
            nameHelpText = '';
            nameDivStyle += ' has-error';
        }

        let passwordError = null;
        let passwordDivStyle = 'form-group';
        if (this.state.passwordError) {
            passwordError = <label className='control-label'>{this.state.passwordError}</label>;
            passwordDivStyle += ' has-error';
        }

        let yourEmailIs = null;
        if (this.state.email) {
            yourEmailIs = (
                <FormattedHTMLMessage
                    id='signup_user_completed.emailIs'
                    defaultMessage="Your email address is <strong>{email}</strong>. You'll use this address to sign in to {siteName}."
                    values={{
                        email: this.state.email,
322
                        siteName: this.props.siteName,
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
                    }}
                />
            );
        }

        let emailContainerStyle = 'margin--extra';
        if (this.state.email) {
            emailContainerStyle = 'hidden';
        }

        return (
            <form>
                <div className='inner__content'>
                    <div className={emailContainerStyle}>
                        <h5><strong>
                            <FormattedMessage
                                id='signup_user_completed.whatis'
                                defaultMessage="What's your email address?"
                            />
                        </strong></h5>
                        <div className={emailDivStyle}>
                            <input
345
                                id='email'
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
                                type='email'
                                ref='email'
                                className='form-control'
                                defaultValue={this.state.email}
                                placeholder=''
                                maxLength='128'
                                autoFocus={true}
                                spellCheck='false'
                                autoCapitalize='off'
                            />
                            {emailError}
                            {emailHelpText}
                        </div>
                    </div>
                    {yourEmailIs}
                    <div className='margin--extra'>
                        <h5><strong>
                            <FormattedMessage
                                id='signup_user_completed.chooseUser'
                                defaultMessage='Choose your username'
                            />
                        </strong></h5>
                        <div className={nameDivStyle}>
                            <input
370
                                id='name'
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
                                type='text'
                                ref='name'
                                className='form-control'
                                placeholder=''
                                maxLength={Constants.MAX_USERNAME_LENGTH}
                                spellCheck='false'
                                autoCapitalize='off'
                            />
                            {nameError}
                            {nameHelpText}
                        </div>
                    </div>
                    <div className='margin--extra'>
                        <h5><strong>
                            <FormattedMessage
                                id='signup_user_completed.choosePwd'
                                defaultMessage='Choose your password'
                            />
                        </strong></h5>
                        <div className={passwordDivStyle}>
                            <input
392
                                id='password'
393
394
395
396
397
398
399
400
401
402
403
404
                                type='password'
                                ref='password'
                                className='form-control'
                                placeholder=''
                                maxLength='128'
                                spellCheck='false'
                            />
                            {passwordError}
                        </div>
                    </div>
                    <p className='margin--extra'>
                        <button
405
                            id='createAccountButton'
406
407
408
                            type='submit'
                            onClick={this.handleSubmit}
                            className='btn-primary btn'
409
                            disabled={this.state.isSubmitting}
410
411
412
413
414
415
416
417
418
419
420
421
422
                        >
                            <FormattedMessage
                                id='signup_user_completed.create'
                                defaultMessage='Create Account'
                            />
                        </button>
                    </p>
                </div>
            </form>
        );
    }

    render() {
423
424
425
426
427
428
429
430
431
        const {
            customDescriptionText,
            enableSignUpWithEmail,
            location,
            privacyPolicyLink,
            siteName,
            termsOfServiceLink,
        } = this.props;

432
433
434
435
436
437
438
439
440
441
442
443
444
445
        let serverError = null;
        if (this.state.serverError) {
            serverError = (
                <div className={'form-group has-error'}>
                    <label className='control-label'>{this.state.serverError}</label>
                </div>
            );
        }

        if (this.state.loading) {
            return (<LoadingScreen/>);
        }

        let emailSignup;
446
        if (enableSignUpWithEmail) {
447
448
449
450
451
452
453
454
455
456
457
            emailSignup = this.renderEmailSignup();
        } else {
            return null;
        }

        let terms = null;
        if (!this.state.noOpenServerError && emailSignup) {
            terms = (
                <p>
                    <FormattedHTMLMessage
                        id='create_team.agreement'
458
                        defaultMessage="By proceeding to create your account and use {siteName}, you agree to our <a href='{TermsOfServiceLink}'>Terms of Service</a> and <a href='{PrivacyPolicyLink}'>Privacy Policy</a>. If you do not agree, you cannot use {siteName}."
459
                        values={{
460
461
462
                            siteName,
                            TermsOfServiceLink: termsOfServiceLink,
                            PrivacyPolicyLink: privacyPolicyLink,
463
464
465
466
467
468
469
470
471
472
473
474
                        }}
                    />
                </p>
            );
        }

        if (this.state.noOpenServerError) {
            emailSignup = null;
        }

        return (
            <div>
475
                <BackButton/>
476
477
478
479
480
481
                <div className='col-sm-12'>
                    <div className='signup-team__container padding--less'>
                        <img
                            className='signup-team-logo'
                            src={logoImage}
                        />
482
483
484
485
                        <SiteNameAndDescription
                            customDescriptionText={customDescriptionText}
                            siteName={siteName}
                        />
486
487
488
489
490
491
492
493
494
495
496
497
498
                        <h4 className='color--light'>
                            <FormattedMessage
                                id='signup_user_completed.lets'
                                defaultMessage="Let's create your account"
                            />
                        </h4>
                        <span className='color--light'>
                            <FormattedMessage
                                id='signup_user_completed.haveAccount'
                                defaultMessage='Already have an account?'
                            />
                            {' '}
                            <Link
499
                                to={'/login' + location.search}
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
                            >
                                <FormattedMessage
                                    id='signup_user_completed.signIn'
                                    defaultMessage='Click here to sign in.'
                                />
                            </Link>
                        </span>
                        {emailSignup}
                        {serverError}
                        {terms}
                    </div>
                </div>
            </div>
        );
    }
}