diff options
Diffstat (limited to 'web/react')
-rw-r--r-- | web/react/components/authorize.jsx | 42 | ||||
-rw-r--r-- | web/react/components/docs.jsx | 2 | ||||
-rw-r--r-- | web/react/components/find_team.jsx | 65 | ||||
-rw-r--r-- | web/react/components/login.jsx | 76 | ||||
-rw-r--r-- | web/react/components/login_email.jsx | 49 | ||||
-rw-r--r-- | web/react/components/login_ldap.jsx | 48 | ||||
-rw-r--r-- | web/react/components/password_reset_form.jsx | 64 | ||||
-rw-r--r-- | web/react/components/password_reset_send_link.jsx | 63 | ||||
-rw-r--r-- | web/react/components/post_info.jsx | 39 | ||||
-rw-r--r-- | web/react/components/signup_team.jsx | 43 | ||||
-rw-r--r-- | web/react/components/team_signup_choose_auth.jsx | 42 | ||||
-rw-r--r-- | web/react/components/team_signup_with_email.jsx | 34 | ||||
-rw-r--r-- | web/react/components/team_signup_with_sso.jsx | 52 | ||||
-rw-r--r-- | web/react/pages/find_team.jsx | 59 | ||||
-rw-r--r-- | web/react/pages/signup_team.jsx | 2 | ||||
-rw-r--r-- | web/react/stores/socket_store.jsx | 3 | ||||
-rw-r--r-- | web/react/utils/constants.jsx | 1 | ||||
-rw-r--r-- | web/react/utils/utils.jsx | 2 |
18 files changed, 579 insertions, 107 deletions
diff --git a/web/react/components/authorize.jsx b/web/react/components/authorize.jsx index 32e39fbff..90cbe3289 100644 --- a/web/react/components/authorize.jsx +++ b/web/react/components/authorize.jsx @@ -3,6 +3,8 @@ import * as Client from '../utils/client.jsx'; +import {FormattedMessage} from 'mm-intl'; + export default class Authorize extends React.Component { constructor(props) { super(props); @@ -35,25 +37,55 @@ export default class Authorize extends React.Component { return ( <div className='authorize-box'> <div className='authorize-inner'> - <h3>{'An application would like to connect to your '}{this.props.teamName}{' account'}</h3> - <label>{'The app '}{this.props.appName}{' would like the ability to access and modify your basic information.'}</label> + <h3> + <FormattedMessage + id='authorize.title' + defaultMessage='An application would like to connect to your {teamName} account' + values={{ + teamName: this.props.teamName + }} + /> + </h3> + <label> + <FormattedMessage + id='authorize.app' + defaultMessage='The app {appName} would like the ability to access and modify your basic information.' + values={{ + appName: this.props.appName + }} + /> + </label> <br/> <br/> - <label>{'Allow '}{this.props.appName}{' access?'}</label> + <label> + <FormattedMessage + id='authorize.access' + defaultMessage='Allow {appName} access?' + values={{ + appName: this.props.appName + }} + /> + </label> <br/> <button type='submit' className='btn authorize-btn' onClick={this.handleDeny} > - {'Deny'} + <FormattedMessage + id='authorize.deny' + defaultMessage='Deny' + /> </button> <button type='submit' className='btn btn-primary authorize-btn' onClick={this.handleAllow} > - {'Allow'} + <FormattedMessage + id='authorize.allow' + defaultMessage='Allow' + /> </button> </div> </div> diff --git a/web/react/components/docs.jsx b/web/react/components/docs.jsx index 188ca340b..6d3a109c2 100644 --- a/web/react/components/docs.jsx +++ b/web/react/components/docs.jsx @@ -13,7 +13,7 @@ export default class Docs extends React.Component { const errorState = {text: '## 404'}; if (props.site) { - $.get('/static/help/' + props.site + '.md').then((response) => { + $.get(`/static/help/${props.site}_${global.window.mm_locale}.md`).then((response) => { this.setState({text: response}); }, () => { this.setState(errorState); diff --git a/web/react/components/find_team.jsx b/web/react/components/find_team.jsx index 94ca48dbf..3ff9787ad 100644 --- a/web/react/components/find_team.jsx +++ b/web/react/components/find_team.jsx @@ -4,7 +4,20 @@ import * as utils from '../utils/utils.jsx'; import * as client from '../utils/client.jsx'; -export default class FindTeam extends React.Component { +import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl'; + +var holders = defineMessages({ + submitError: { + id: 'find_team.submitError', + defaultMessage: 'Please enter a valid email address' + }, + placeholder: { + id: 'find_team.placeholder', + defaultMessage: 'you@domain.com' + } +}); + +class FindTeam extends React.Component { constructor(props) { super(props); this.state = {}; @@ -19,7 +32,7 @@ export default class FindTeam extends React.Component { var email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase(); if (!email || !utils.isEmail(email)) { - state.email_error = 'Please enter a valid email address'; + state.email_error = this.props.intl.formatMessage(holders.submitError); this.setState(state); return; } @@ -50,25 +63,50 @@ export default class FindTeam extends React.Component { if (this.state.sent) { return ( <div> - <h4>{'Find Your teams'}</h4> - <p>{'An email was sent with links to any teams to which you are a member.'}</p> + <h4> + <FormattedMessage + id='find_team.findTitle' + defaultMessage='Find Your Team' + /> + </h4> + <p> + <FormattedMessage + id='find_team.findDescription' + defaultMessage='An email was sent with links to any teams to which you are a member.' + /> + </p> </div> ); } return ( <div> - <h4>Find Your Team</h4> + <h4> + <FormattedMessage + id='find_team.findTitle' + defaultMessage='Find Your Team' + /> + </h4> <form onSubmit={this.handleSubmit}> - <p>{'Get an email with links to any teams to which you are a member.'}</p> + <p> + <FormattedMessage + id='find_team.getLinks' + defaultMessage='Get an email with links to any teams to which you are a member.' + /> + </p> <div className='form-group'> - <label className='control-label'>Email</label> + <label className='control-label'> + <FormattedMessage + id='find_team.email' + defaultMessage='Email' + /> + </label> <div className={emailErrorClass}> <input type='text' ref='email' className='form-control' - placeholder='you@domain.com' + placeholder={this.props.intl.formatMessage(holders.placeholder)} maxLength='128' spellCheck='false' /> @@ -79,10 +117,19 @@ export default class FindTeam extends React.Component { className='btn btn-md btn-primary' type='submit' > - Send + <FormattedMessage + id='find_team.send' + defaultMessage='Send' + /> </button> </form> </div> ); } } + +FindTeam.propTypes = { + intl: intlShape.isRequired +}; + +export default injectIntl(FindTeam);
\ No newline at end of file diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx index 3c1d66334..c4f530af0 100644 --- a/web/react/components/login.jsx +++ b/web/react/components/login.jsx @@ -7,7 +7,7 @@ import LoginLdap from './login_ldap.jsx'; import * as Utils from '../utils/utils.jsx'; import Constants from '../utils/constants.jsx'; -var FormattedMessage = ReactIntl.FormattedMessage; +import {FormattedMessage} from 'mm-intl'; export default class Login extends React.Component { constructor(props) { @@ -24,10 +24,16 @@ export default class Login extends React.Component { loginMessage.push( <a className='btn btn-custom-login gitlab' + key='gitlab' href={'/' + teamName + '/login/gitlab'} > <span className='icon' /> - <span>{'with GitLab'}</span> + <span> + <FormattedMessage + id='login.gitlab' + defaultMessage='with GitLab' + /> + </span> </a> ); } @@ -36,10 +42,16 @@ export default class Login extends React.Component { loginMessage.push( <a className='btn btn-custom-login google' + key='google' href={'/' + teamName + '/login/google'} > <span className='icon' /> - <span>{'with Google Apps'}</span> + <span> + <FormattedMessage + id='login.google' + defaultMessage='with Google Apps' + /> + </span> </a> ); } @@ -49,9 +61,19 @@ export default class Login extends React.Component { if (extraParam) { let msg; if (extraParam === Constants.SIGNIN_CHANGE) { - msg = ' Sign-in method changed successfully'; + msg = ( + <FormattedMessage + id='login.changed' + defaultMessage=' Sign-in method changed successfully' + /> + ); } else if (extraParam === Constants.SIGNIN_VERIFIED) { - msg = ' Email Verified'; + msg = ( + <FormattedMessage + id='login.verified' + defaultMessage=' Email Verified' + /> + ); } if (msg != null) { @@ -78,7 +100,12 @@ export default class Login extends React.Component { <div> {loginMessage} <div className='or__container'> - <span>{'or'}</span> + <span> + <FormattedMessage + id='login.or' + defaultMessage='or' + /> + </span> </div> </div> ); @@ -90,7 +117,7 @@ export default class Login extends React.Component { <div className='form-group'> <a href={'/' + teamName + '/reset_password'}> <FormattedMessage - id='login.forgot_password' + id='login.forgot' defaultMessage='I forgot my password' /> </a> @@ -102,12 +129,19 @@ export default class Login extends React.Component { if (this.props.inviteId) { userSignUp = ( <div> - <span>{`Don't have an account? `} + <span> + <FormattedMessage + id='login.noAccount' + defaultMessage="Don't have an account? " + /> <a href={'/signup_user_complete/?id=' + this.props.inviteId} className='signup-team-login' > - {'Create one now'} + <FormattedMessage + id='login.create' + defaultMessage='Create one now' + /> </a> </span> </div> @@ -122,7 +156,10 @@ export default class Login extends React.Component { href='/' className='signup-team-login' > - {'Create a new team'} + <FormattedMessage + id='login.createTeam' + defaultMessage='Create a new team' + /> </a> </div> ); @@ -144,7 +181,7 @@ export default class Login extends React.Component { <span> <a href='/find_team'> <FormattedMessage - id='login.find_teams' + id='login.find' defaultMessage='Find your other teams' /> </a></span> @@ -154,9 +191,22 @@ export default class Login extends React.Component { return ( <div className='signup-team__container'> - <h5 className='margin--less'>{'Sign in to:'}</h5> + <h5 className='margin--less'> + <FormattedMessage + id='login.signTo' + defaultMessage='Sign in to:' + /> + </h5> <h2 className='signup-team__name'>{teamDisplayName}</h2> - <h2 className='signup-team__subdomain'>{'on '}{global.window.mm_config.SiteName}</h2> + <h2 className='signup-team__subdomain'> + <FormattedMessage + id='login.on' + defaultMessage='on {siteName}' + values={{ + siteName: global.window.mm_config.SiteName + }} + /> + </h2> {extraBox} {loginMessage} {emailSignup} diff --git a/web/react/components/login_email.jsx b/web/react/components/login_email.jsx index cfe34d1c7..cf1e1bc40 100644 --- a/web/react/components/login_email.jsx +++ b/web/react/components/login_email.jsx @@ -5,7 +5,32 @@ import * as Utils from '../utils/utils.jsx'; import * as Client from '../utils/client.jsx'; import UserStore from '../stores/user_store.jsx'; -export default class LoginEmail extends React.Component { +import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl'; + +var holders = defineMessages({ + badTeam: { + id: 'login_email.badTeam', + defaultMessage: 'Bad team name' + }, + emailReq: { + id: 'login_email.emailReq', + defaultMessage: 'An email is required' + }, + pwdReq: { + id: 'login_email.pwdReq', + defaultMessage: 'A password is required' + }, + email: { + id: 'login_email.email', + defaultMessage: 'Email' + }, + pwd: { + id: 'login_email.pwd', + defaultMessage: 'Password' + } +}); + +class LoginEmail extends React.Component { constructor(props) { super(props); @@ -17,25 +42,26 @@ export default class LoginEmail extends React.Component { } handleSubmit(e) { e.preventDefault(); + const {formatMessage} = this.props.intl; var state = {}; const name = this.props.teamName; if (!name) { - state.serverError = 'Bad team name'; + state.serverError = formatMessage(holders.badTeam); this.setState(state); return; } const email = this.refs.email.value.trim(); if (!email) { - state.serverError = 'An email is required'; + state.serverError = formatMessage(holders.emailReq); this.setState(state); return; } const password = this.refs.password.value.trim(); if (!password) { - state.serverError = 'A password is required'; + state.serverError = formatMessage(holders.pwdReq); this.setState(state); return; } @@ -55,7 +81,7 @@ export default class LoginEmail extends React.Component { } }, (err) => { - if (err.message === 'Login failed because email address has not been verified') { + if (err.id === 'api.user.login.not_verified.app_error') { window.location.href = '/verify_email?teamname=' + encodeURIComponent(name) + '&email=' + encodeURIComponent(email); return; } @@ -87,6 +113,7 @@ export default class LoginEmail extends React.Component { priorEmail = decodeURIComponent(emailParam); } + const {formatMessage} = this.props.intl; return ( <form onSubmit={this.handleSubmit}> <div className='signup__email-container'> @@ -101,7 +128,7 @@ export default class LoginEmail extends React.Component { name='email' defaultValue={priorEmail} ref='email' - placeholder='Email' + placeholder={formatMessage(holders.email)} spellCheck='false' /> </div> @@ -112,7 +139,7 @@ export default class LoginEmail extends React.Component { className='form-control' name='password' ref='password' - placeholder='Password' + placeholder={formatMessage(holders.pwd)} spellCheck='false' /> </div> @@ -121,7 +148,10 @@ export default class LoginEmail extends React.Component { type='submit' className='btn btn-primary' > - {'Sign in'} + <FormattedMessage + id='login_email.signin' + defaultMessage='Sign in' + /> </button> </div> </div> @@ -133,5 +163,8 @@ LoginEmail.defaultProps = { }; LoginEmail.propTypes = { + intl: intlShape.isRequired, teamName: React.PropTypes.string.isRequired }; + +export default injectIntl(LoginEmail);
\ No newline at end of file diff --git a/web/react/components/login_ldap.jsx b/web/react/components/login_ldap.jsx index 1e0e32f4f..d67f15fa5 100644 --- a/web/react/components/login_ldap.jsx +++ b/web/react/components/login_ldap.jsx @@ -4,7 +4,32 @@ import * as Utils from '../utils/utils.jsx'; import * as Client from '../utils/client.jsx'; -export default class LoginLdap extends React.Component { +import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl'; + +const holders = defineMessages({ + badTeam: { + id: 'login_ldap.badTeam', + defaultMessage: 'Bad team name' + }, + idReq: { + id: 'login_ldap.idlReq', + defaultMessage: 'An LDAP ID is required' + }, + pwdReq: { + id: 'login_ldap.pwdReq', + defaultMessage: 'An LDAP password is required' + }, + username: { + id: 'login_ldap.username', + defaultMessage: 'LDAP Username' + }, + pwd: { + id: 'login_ldap.pwd', + defaultMessage: 'LDAP Password' + } +}); + +class LoginLdap extends React.Component { constructor(props) { super(props); @@ -16,25 +41,26 @@ export default class LoginLdap extends React.Component { } handleSubmit(e) { e.preventDefault(); + const {formatMessage} = this.props.intl; var state = {}; const teamName = this.props.teamName; if (!teamName) { - state.serverError = 'Bad team name'; + state.serverError = formatMessage(holders.badTeam); this.setState(state); return; } const id = this.refs.id.value.trim(); if (!id) { - state.serverError = 'An LDAP ID is required'; + state.serverError = formatMessage(holders.idReq); this.setState(state); return; } const password = this.refs.password.value.trim(); if (!password) { - state.serverError = 'An LDAP password is required'; + state.serverError = formatMessage(holders.pwdReq); this.setState(state); return; } @@ -64,7 +90,7 @@ export default class LoginLdap extends React.Component { serverError = <label className='control-label'>{this.state.serverError}</label>; errorClass = ' has-error'; } - + const {formatMessage} = this.props.intl; return ( <form onSubmit={this.handleSubmit}> <div className='signup__email-container'> @@ -76,7 +102,7 @@ export default class LoginLdap extends React.Component { autoFocus={true} className='form-control' ref='id' - placeholder='LDAP Username' + placeholder={formatMessage(holders.username)} spellCheck='false' /> </div> @@ -85,7 +111,7 @@ export default class LoginLdap extends React.Component { type='password' className='form-control' ref='password' - placeholder='LDAP Password' + placeholder={formatMessage(holders.pwd)} spellCheck='false' /> </div> @@ -94,7 +120,10 @@ export default class LoginLdap extends React.Component { type='submit' className='btn btn-primary' > - {'Sign in'} + <FormattedMessage + id='login_ldap.signin' + defaultMessage='Sign in' + /> </button> </div> </div> @@ -106,5 +135,8 @@ LoginLdap.defaultProps = { }; LoginLdap.propTypes = { + intl: intlShape.isRequired, teamName: React.PropTypes.string.isRequired }; + +export default injectIntl(LoginLdap);
\ No newline at end of file diff --git a/web/react/components/password_reset_form.jsx b/web/react/components/password_reset_form.jsx index 8063db05a..380dbe973 100644 --- a/web/react/components/password_reset_form.jsx +++ b/web/react/components/password_reset_form.jsx @@ -4,7 +4,24 @@ import * as Client from '../utils/client.jsx'; import Constants from '../utils/constants.jsx'; -export default class PasswordResetForm extends React.Component { +import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl'; + +const holders = defineMessages({ + error: { + id: 'password_form.error', + defaultMessage: 'Please enter at least {chars} characters.' + }, + update: { + id: 'password_form.update', + defaultMessage: 'Your password has been updated successfully.' + }, + pwd: { + id: 'password_form.pwd', + defaultMessage: 'Password' + } +}); + +class PasswordResetForm extends React.Component { constructor(props) { super(props); @@ -14,11 +31,13 @@ export default class PasswordResetForm extends React.Component { } handlePasswordReset(e) { e.preventDefault(); + + const {formatMessage} = this.props.intl; var state = {}; var password = ReactDOM.findDOMNode(this.refs.password).value.trim(); if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) { - state.error = 'Please enter at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters.'; + state.error = formatMessage(holders.error, {chars: Constants.MIN_PASSWORD_LENGTH}); this.setState(state); return; } @@ -34,7 +53,7 @@ export default class PasswordResetForm extends React.Component { Client.resetPassword(data, function resetSuccess() { - this.setState({error: null, updateText: 'Your password has been updated successfully.'}); + this.setState({error: null, updateText: formatMessage(holders.update)}); }.bind(this), function resetFailure(err) { this.setState({error: err.message, updateText: null}); @@ -44,7 +63,15 @@ export default class PasswordResetForm extends React.Component { render() { var updateText = null; if (this.state.updateText) { - updateText = <div className='form-group'><br/><label className='control-label reset-form'>{this.state.updateText} Click <a href={'/' + this.props.teamName + '/login'}>here</a> to log in.</label></div>; + updateText = (<div className='form-group'><br/><label className='control-label reset-form'>{this.state.updateText} + <FormattedHTMLMessage + id='password_form.click' + defaultMessage='Click <a href={url}>here</a> to log in.' + values={{ + url: '/' + this.props.teamName + '/login' + }} + /> + </label></div>); } var error = null; @@ -57,19 +84,34 @@ export default class PasswordResetForm extends React.Component { formClass += ' has-error'; } + const {formatMessage} = this.props.intl; return ( <div className='col-sm-12'> <div className='signup-team__container'> - <h3>{'Password Reset'}</h3> + <h3> + <FormattedMessage + id='password_form.title' + defaultMessage='Password Reset' + /> + </h3> <form onSubmit={this.handlePasswordReset}> - <p>{'Enter a new password for your ' + this.props.teamDisplayName + ' ' + global.window.mm_config.SiteName + ' account.'}</p> + <p> + <FormattedMessage + id='password_form.enter' + defaultMessage='Enter a new password for your {teamDisplayName} {siteName} account.' + values={{ + teamDisplayName: this.props.teamDisplayName, + siteName: global.window.mm_config.SiteName + }} + /> + </p> <div className={formClass}> <input type='password' className='form-control' name='password' ref='password' - placeholder='Password' + placeholder={formatMessage(holders.pwd)} spellCheck='false' /> </div> @@ -78,7 +120,10 @@ export default class PasswordResetForm extends React.Component { type='submit' className='btn btn-primary' > - {'Change my password'} + <FormattedMessage + id='password_form.change' + defaultMessage='Change my password' + /> </button> {updateText} </form> @@ -95,8 +140,11 @@ PasswordResetForm.defaultProps = { data: '' }; PasswordResetForm.propTypes = { + intl: intlShape.isRequired, teamName: React.PropTypes.string, teamDisplayName: React.PropTypes.string, hash: React.PropTypes.string, data: React.PropTypes.string }; + +export default injectIntl(PasswordResetForm);
\ No newline at end of file diff --git a/web/react/components/password_reset_send_link.jsx b/web/react/components/password_reset_send_link.jsx index 051b8b02c..8cc8a050d 100644 --- a/web/react/components/password_reset_send_link.jsx +++ b/web/react/components/password_reset_send_link.jsx @@ -4,7 +4,28 @@ import * as Utils from '../utils/utils.jsx'; import * as client from '../utils/client.jsx'; -export default class PasswordResetSendLink extends React.Component { +import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl'; + +const holders = defineMessages({ + error: { + id: 'password_send.error', + defaultMessage: 'Please enter a valid email address.' + }, + link: { + id: 'password_send.link', + defaultMessage: '<p>A password reset link has been sent to <b>{email}</b> for your <b>{teamDisplayName}</b> team on {hostname}.</p>' + }, + checkInbox: { + id: 'password_send.checkInbox', + defaultMessage: 'Please check your inbox.' + }, + email: { + id: 'password_send.email', + defaultMessage: 'Email' + } +}); + +class PasswordResetSendLink extends React.Component { constructor(props) { super(props); @@ -15,10 +36,11 @@ export default class PasswordResetSendLink extends React.Component { handleSendLink(e) { e.preventDefault(); var state = {}; + const {formatMessage} = this.props.intl; var email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase(); if (!email || !Utils.isEmail(email)) { - state.error = 'Please enter a valid email address.'; + state.error = formatMessage(holders.error); this.setState(state); return; } @@ -32,7 +54,7 @@ export default class PasswordResetSendLink extends React.Component { client.sendPasswordReset(data, function passwordResetSent() { - this.setState({error: null, updateText: <p>A password reset link has been sent to <b>{email}</b> for your <b>{this.props.teamDisplayName}</b> team on {window.location.hostname}.</p>, moreUpdateText: 'Please check your inbox.'}); + this.setState({error: null, updateText: formatMessage(holders.link, {email: email, teamDisplayName: this.props.teamDisplayName, hostname: window.location.hostname}), moreUpdateText: formatMessage(holders.checkInbox)}); $(ReactDOM.findDOMNode(this.refs.reset_form)).hide(); }.bind(this), function passwordResetFailedToSend(err) { @@ -43,7 +65,12 @@ export default class PasswordResetSendLink extends React.Component { render() { var updateText = null; if (this.state.updateText) { - updateText = <div className='reset-form alert alert-success'>{this.state.updateText}{this.state.moreUpdateText}</div>; + updateText = ( + <div className='reset-form alert alert-success' + dangerouslySetInnerHTML={{__html: this.state.updateText + this.state.moreUpdateText}} + > + </div> + ); } var error = null; @@ -56,23 +83,37 @@ export default class PasswordResetSendLink extends React.Component { formClass += ' has-error'; } + const {formatMessage} = this.props.intl; return ( <div className='col-sm-12'> <div className='signup-team__container'> - <h3>Password Reset</h3> + <h3> + <FormattedMessage + id='password_send.title' + defaultMessage='Password Reset' + /> + </h3> {updateText} <form onSubmit={this.handleSendLink} ref='reset_form' > - <p>{'To reset your password, enter the email address you used to sign up for ' + this.props.teamDisplayName + '.'}</p> + <p> + <FormattedMessage + id='password_send.description' + defaultMessage='To reset your password, enter the email address you used to sign up for {teamName}.' + values={{ + teamName: this.props.teamDisplayName + }} + /> + </p> <div className={formClass}> <input type='email' className='form-control' name='email' ref='email' - placeholder='Email' + placeholder={formatMessage(holders.email)} spellCheck='false' /> </div> @@ -81,7 +122,10 @@ export default class PasswordResetSendLink extends React.Component { type='submit' className='btn btn-primary' > - Reset my password + <FormattedMessage + id='password_send.reset' + defaultMessage='Reset my password' + /> </button> </form> </div> @@ -95,6 +139,9 @@ PasswordResetSendLink.defaultProps = { teamDisplayName: '' }; PasswordResetSendLink.propTypes = { + intl: intlShape.isRequired, teamName: React.PropTypes.string, teamDisplayName: React.PropTypes.string }; + +export default injectIntl(PasswordResetSendLink);
\ No newline at end of file diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx index 26bd6adde..73b47024c 100644 --- a/web/react/components/post_info.jsx +++ b/web/react/components/post_info.jsx @@ -22,6 +22,26 @@ export default class PostInfo extends React.Component { this.handlePermalinkCopy = this.handlePermalinkCopy.bind(this); } + createReplyLink() { + if (this.props.allowReply === 'true') { + var hideReply = ''; + + if (this.props.commentCount >= 1) { + hideReply = ' post__reply--hide'; + } + + return ( + <div className={'post__reply' + hideReply}> + <a + onClick={this.props.handleCommentClick} + href='#' + > + <span dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}/> + </a> + </div> + ); + } + } createDropdown() { var post = this.props.post; var isOwner = UserStore.getCurrentId() === post.user_id; @@ -42,23 +62,6 @@ export default class PostInfo extends React.Component { dataComments = this.props.commentCount; } - if (this.props.allowReply === 'true') { - dropdownContents.push( - <li - key='replyLink' - role='presentation' - > - <a - className='link__reply theme' - href='#' - onClick={this.props.handleCommentClick} - > - {'Reply'} - </a> - </li> - ); - } - dropdownContents.push( <li key='copyLink' @@ -181,6 +184,7 @@ export default class PostInfo extends React.Component { } var dropdown = this.createDropdown(); + var replyLink = this.createReplyLink(); const permalink = TeamStore.getCurrentTeamUrl() + '/pl/' + post.id; const copyButtonText = this.state.copiedLink ? (<div>{'Copy '}<i className='fa fa-check'/></div>) : 'Copy'; @@ -223,6 +227,7 @@ export default class PostInfo extends React.Component { /> </li> <li className='col col__reply'> + {replyLink} <div className='dropdown' ref='dotMenu' diff --git a/web/react/components/signup_team.jsx b/web/react/components/signup_team.jsx index a554427d5..098e9f65a 100644 --- a/web/react/components/signup_team.jsx +++ b/web/react/components/signup_team.jsx @@ -6,6 +6,8 @@ import EmailSignUpPage from './team_signup_with_email.jsx'; import SSOSignupPage from './team_signup_with_sso.jsx'; import Constants from '../utils/constants.jsx'; +import {FormattedMessage} from 'mm-intl'; + export default class TeamSignUp extends React.Component { constructor(props) { super(props); @@ -43,12 +45,24 @@ export default class TeamSignUp extends React.Component { if (global.window.mm_config.EnableTeamListing === 'true') { if (this.props.teams.length === 0) { if (global.window.mm_config.EnableTeamCreation !== 'true') { - teamListing = (<div>{'There are no teams include in the Team Directory and team creation has been disabled.'}</div>); + teamListing = ( + <div> + <FormattedMessage + id='signup_team.noTeams' + defaultMessage='There are no teams include in the Team Directory and team creation has been disabled.' + /> + </div> + ); } } else { teamListing = ( <div> - <h4>{'Choose a Team'}</h4> + <h4> + <FormattedMessage + id='signup_team.choose' + defaultMessage='Choose a Team' + /> + </h4> <div className='signup-team-all'> { this.props.teams.map((team) => { @@ -71,7 +85,12 @@ export default class TeamSignUp extends React.Component { }) } </div> - <h4>{'Or Create a Team'}</h4> + <h4> + <FormattedMessage + id='signup_team.createTeam' + defaultMessage='Or Create a Team' + /> + </h4> </div> ); } @@ -79,7 +98,14 @@ export default class TeamSignUp extends React.Component { if (global.window.mm_config.EnableTeamCreation !== 'true') { if (teamListing == null) { - return (<div>{'Team creation has been disabled. Please contact an administrator for access.'}</div>); + return ( + <div> + <FormattedMessage + id='signup_team.disabled' + defaultMessage='Team creation has been disabled. Please contact an administrator for access.' + /> + </div> + ); } return ( @@ -122,7 +148,14 @@ export default class TeamSignUp extends React.Component { </div> ); } else if (this.state.page === 'none') { - return (<div>{'No team creation method has been enabled. Please contact an administrator for access.'}</div>); + return ( + <div> + <FormattedMessage + id='signup_team.none' + defaultMessage='No team creation method has been enabled. Please contact an administrator for access.' + /> + </div> + ); } } } diff --git a/web/react/components/team_signup_choose_auth.jsx b/web/react/components/team_signup_choose_auth.jsx index 19b9750b3..2dc67e92e 100644 --- a/web/react/components/team_signup_choose_auth.jsx +++ b/web/react/components/team_signup_choose_auth.jsx @@ -1,6 +1,8 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +import {FormattedMessage} from 'mm-intl'; + export default class ChooseAuthPage extends React.Component { constructor(props) { super(props); @@ -12,6 +14,7 @@ export default class ChooseAuthPage extends React.Component { buttons.push( <a className='btn btn-custom-login gitlab btn-full' + key='gitlab' href='#' onClick={ function clickGit(e) { @@ -21,7 +24,12 @@ export default class ChooseAuthPage extends React.Component { } > <span className='icon' /> - <span>{'Create new team with GitLab Account'}</span> + <span> + <FormattedMessage + id='choose_auth_page.gitlabCreate' + defaultMessage='Create new team with GitLab Account' + /> + </span> </a> ); } @@ -30,6 +38,7 @@ export default class ChooseAuthPage extends React.Component { buttons.push( <a className='btn btn-custom-login google btn-full' + key='google' href='#' onClick={ (e) => { @@ -39,7 +48,12 @@ export default class ChooseAuthPage extends React.Component { } > <span className='icon' /> - <span>{'Create new team with Google Apps Account'}</span> + <span> + <FormattedMessage + id='choose_auth_page.googleCreate' + defaultMessage='Create new team with Google Apps Account' + /> + </span> </a> ); } @@ -48,6 +62,7 @@ export default class ChooseAuthPage extends React.Component { buttons.push( <a className='btn btn-custom-login email btn-full' + key='email' href='#' onClick={ function clickEmail(e) { @@ -57,20 +72,37 @@ export default class ChooseAuthPage extends React.Component { } > <span className='fa fa-envelope' /> - <span>{'Create new team with email address'}</span> + <span> + <FormattedMessage + id='choose_auth_page.emailCreate' + defaultMessage='Create new team with email address' + /> + </span> </a> ); } if (buttons.length === 0) { - buttons = <span>{'No sign-up methods configured, please contact your system administrator.'}</span>; + buttons = ( + <span> + <FormattedMessage + id='choose_auth_page.noSignup' + defaultMessage='No sign-up methods configured, please contact your system administrator.' + /> + </span> + ); } return ( <div> {buttons} <div className='form-group margin--extra-2x'> - <span><a href='/find_team'>{'Find my teams'}</a></span> + <span><a href='/find_team'> + <FormattedMessage + id='choose_auth_page.find' + defaultMessage='Find my teams' + /> + </a></span> </div> </div> ); diff --git a/web/react/components/team_signup_with_email.jsx b/web/react/components/team_signup_with_email.jsx index 4150a0013..7dd645b25 100644 --- a/web/react/components/team_signup_with_email.jsx +++ b/web/react/components/team_signup_with_email.jsx @@ -4,7 +4,20 @@ import * as Utils from '../utils/utils.jsx'; import * as Client from '../utils/client.jsx'; -export default class EmailSignUpPage extends React.Component { +import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl'; + +const holders = defineMessages({ + emailError: { + id: 'email_signup.emailError', + defaultMessage: 'Please enter a valid email address' + }, + address: { + id: 'email_signup.address', + defaultMessage: 'Email Address' + } +}); + +class EmailSignUpPage extends React.Component { constructor() { super(); @@ -20,7 +33,7 @@ export default class EmailSignUpPage extends React.Component { team.email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase(); if (!team.email || !Utils.isEmail(team.email)) { - state.emailError = 'Please enter a valid email address'; + state.emailError = this.props.intl.formatMessage(holders.emailError); isValid = false; } else { state.emailError = null; @@ -67,7 +80,7 @@ export default class EmailSignUpPage extends React.Component { type='email' ref='email' className='form-control' - placeholder='Email Address' + placeholder={this.props.intl.formatMessage(holders.address)} maxLength='128' spellCheck='false' /> @@ -78,12 +91,20 @@ export default class EmailSignUpPage extends React.Component { className='btn btn-md btn-primary' type='submit' > - {'Create Team'} + <FormattedMessage + id='email_signup.createTeam' + defaultMessage='Create Team' + /> </button> {serverError} </div> <div className='form-group margin--extra-2x'> - <span><a href='/find_team'>{`Find my teams`}</a></span> + <span><a href='/find_team'> + <FormattedMessage + id='email_signup.find' + defaultMessage='Find my teams' + /> + </a></span> </div> </form> ); @@ -93,4 +114,7 @@ export default class EmailSignUpPage extends React.Component { EmailSignUpPage.defaultProps = { }; EmailSignUpPage.propTypes = { + intl: intlShape.isRequired }; + +export default injectIntl(EmailSignUpPage);
\ No newline at end of file diff --git a/web/react/components/team_signup_with_sso.jsx b/web/react/components/team_signup_with_sso.jsx index f4b323956..465f73fd2 100644 --- a/web/react/components/team_signup_with_sso.jsx +++ b/web/react/components/team_signup_with_sso.jsx @@ -5,7 +5,24 @@ import * as utils from '../utils/utils.jsx'; import * as client from '../utils/client.jsx'; import Constants from '../utils/constants.jsx'; -export default class SSOSignUpPage extends React.Component { +import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl'; + +const holders = defineMessages({ + team_error: { + id: 'sso_signup.team_error', + defaultMessage: 'Please enter a team name' + }, + length_error: { + id: 'sso_signup.length_error', + defaultMessage: 'Name must be 3 or more characters up to a maximum of 15' + }, + teamName: { + id: 'sso_signup.teamName', + defaultMessage: 'Enter name of new team' + } +}); + +class SSOSignUpPage extends React.Component { constructor(props) { super(props); @@ -16,6 +33,7 @@ export default class SSOSignUpPage extends React.Component { } handleSubmit(e) { e.preventDefault(); + const {formatMessage} = this.props.intl; var team = {}; var state = this.state; state.nameError = null; @@ -24,13 +42,13 @@ export default class SSOSignUpPage extends React.Component { team.display_name = this.state.name; if (!team.display_name) { - state.nameError = 'Please enter a team name'; + state.nameError = formatMessage(holders.team_error); this.setState(state); return; } if (team.display_name.length <= 2) { - state.nameError = 'Name must be 3 or more characters up to a maximum of 15'; + state.nameError = formatMessage(holders.length_error); this.setState(state); return; } @@ -80,24 +98,36 @@ export default class SSOSignUpPage extends React.Component { button = ( <a className='btn btn-custom-login gitlab btn-full' + key='gitlab' href='#' onClick={this.handleSubmit} disabled={disabled} > <span className='icon'/> - <span>{'Create team with GitLab Account'}</span> + <span> + <FormattedMessage + id='sso_signup.gitlab' + defaultMessage='Create team with GitLab Account' + /> + </span> </a> ); } else if (this.props.service === Constants.GOOGLE_SERVICE) { button = ( <a className='btn btn-custom-login google btn-full' + key='google' href='#' onClick={this.handleSubmit} disabled={disabled} > <span className='icon'/> - <span>{'Create team with Google Apps Account'}</span> + <span> + <FormattedMessage + id='sso_signup.google' + defaultMessage='Create team with Google Apps Account' + /> + </span> </a> ); } @@ -113,7 +143,7 @@ export default class SSOSignUpPage extends React.Component { type='text' ref='teamname' className='form-control' - placeholder='Enter name of new team' + placeholder={this.props.intl.formatMessage(holders.teamName)} maxLength='128' onChange={this.nameChange} spellCheck='false' @@ -125,7 +155,12 @@ export default class SSOSignUpPage extends React.Component { {serverError} </div> <div className='form-group margin--extra-2x'> - <span><a href='/find_team'>{'Find my teams'}</a></span> + <span><a href='/find_team'> + <FormattedMessage + id='sso_signup.find' + defaultMessage='Find my teams' + /> + </a></span> </div> </form> ); @@ -136,5 +171,8 @@ SSOSignUpPage.defaultProps = { service: '' }; SSOSignUpPage.propTypes = { + intl: intlShape.isRequired, service: React.PropTypes.string }; + +export default injectIntl(SSOSignUpPage);
\ No newline at end of file diff --git a/web/react/pages/find_team.jsx b/web/react/pages/find_team.jsx index c4653fd77..ee2cf0de1 100644 --- a/web/react/pages/find_team.jsx +++ b/web/react/pages/find_team.jsx @@ -2,12 +2,61 @@ // See License.txt for license information. import FindTeam from '../components/find_team.jsx'; +import * as Client from '../utils/client.jsx'; -function setupFindTeamPage() { +var IntlProvider = ReactIntl.IntlProvider; + +class Root extends React.Component { + constructor() { + super(); + this.state = { + translations: null, + loaded: false + }; + } + + static propTypes() { + return { + map: React.PropTypes.object.isRequired + }; + } + + componentWillMount() { + Client.getTranslations( + this.props.map.Locale, + (data) => { + this.setState({ + translations: data, + loaded: true + }); + }, + () => { + this.setState({ + loaded: true + }); + } + ); + } + + render() { + if (!this.state.loaded) { + return <div></div>; + } + + return ( + <IntlProvider + locale={this.props.map.Locale} + messages={this.state.translations} + > + <FindTeam /> + </IntlProvider> + ); + } +} + +global.window.setup_find_team_page = function setup(props) { ReactDOM.render( - <FindTeam />, + <Root map={props} />, document.getElementById('find-team') ); -} - -global.window.setup_find_team_page = setupFindTeamPage; +};
\ No newline at end of file diff --git a/web/react/pages/signup_team.jsx b/web/react/pages/signup_team.jsx index 8f4f86a7c..c80b65580 100644 --- a/web/react/pages/signup_team.jsx +++ b/web/react/pages/signup_team.jsx @@ -60,7 +60,7 @@ global.window.setup_signup_team_page = function setup(props) { for (var prop in props) { if (props.hasOwnProperty(prop)) { - if (prop !== 'Title') { + if (prop !== 'Title' && prop !== 'Locale' && prop !== 'Info') { teams.push({name: prop, display_name: props[prop]}); } } diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx index f1fade305..736b0ca27 100644 --- a/web/react/stores/socket_store.jsx +++ b/web/react/stores/socket_store.jsx @@ -176,6 +176,7 @@ function handleNewPostEvent(msg) { mentions = JSON.parse(msg.props.mentions); } + const channelType = msgProps.channel_type; const channel = ChannelStore.get(msg.channel_id); const user = UserStore.getCurrentUser(); const member = ChannelStore.getMember(msg.channel_id); @@ -187,7 +188,7 @@ function handleNewPostEvent(msg) { if (notifyLevel === 'none') { return; - } else if (notifyLevel === 'mention' && mentions.indexOf(user.id) === -1 && channel.type !== 'D') { + } else if (notifyLevel === 'mention' && mentions.indexOf(user.id) === -1 && channelType !== Constants.DM_CHANNEL) { return; } diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index 851bc5f6c..e1a4b8a8a 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -168,6 +168,7 @@ export default { OFFLINE_ICON_SVG: "<svg version='1.1'id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:cc='http://creativecommons.org/ns#' inkscape:version='0.48.4 r9939' sodipodi:docname='TRASH_1_4.svg'xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='-299 391 12 12'style='enable-background:new -299 391 12 12;' xml:space='preserve'> <sodipodi:namedview inkscape:cx='26.358185' inkscape:zoom='1.18' bordercolor='#666666' pagecolor='#ffffff' borderopacity='1' objecttolerance='10' inkscape:cy='139.7898' gridtolerance='10' guidetolerance='10' showgrid='false' showguides='true' id='namedview6' inkscape:pageopacity='0' inkscape:pageshadow='2' inkscape:guide-bbox='true' inkscape:window-width='1366' inkscape:current-layer='Layer_1' inkscape:window-height='705' inkscape:window-y='-8' inkscape:window-maximized='1' inkscape:window-x='-8'> <sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide> <sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide> </sodipodi:namedview> <g> <g> <ellipse class='offline--icon' cx='-294.5' cy='394' rx='2.5' ry='2.5'/> <path class='offline--icon' d='M-294.3,399.7c0-0.4,0.1-0.8,0.2-1.2c-0.1,0-0.2,0-0.4,0c-2.5,0-2.5-2-2.5-2s-1,0.1-1.2,0.5c-0.4,0.6-0.6,1.7-0.7,2.5 c0,0.1-0.1,0.5,0,0.6c0.2,1.3,2.2,2.3,4.4,2.4h0.1h0.1c0.3,0,0.7,0,1-0.1C-293.9,401.6-294.3,400.7-294.3,399.7z'/> </g> </g> <g> <path class='offline--icon' d='M-288.9,399.4l1.8-1.8c0.1-0.1,0.1-0.3,0-0.3l-0.7-0.7c-0.1-0.1-0.3-0.1-0.3,0l-1.8,1.8l-1.8-1.8c-0.1-0.1-0.3-0.1-0.3,0 l-0.7,0.7c-0.1,0.1-0.1,0.3,0,0.3l1.8,1.8l-1.8,1.8c-0.1,0.1-0.1,0.3,0,0.3l0.7,0.7c0.1,0.1,0.3,0.1,0.3,0l1.8-1.8l1.8,1.8 c0.1,0.1,0.3,0.1,0.3,0l0.7-0.7c0.1-0.1,0.1-0.3,0-0.3L-288.9,399.4z'/> </g> </svg>", MENU_ICON: "<svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='4px' height='16px' viewBox='0 0 8 32' enable-background='new 0 0 8 32' xml:space='preserve'> <g> <circle cx='4' cy='4.062' r='4'/> <circle cx='4' cy='16' r='4'/> <circle cx='4' cy='28' r='4'/> </g> </svg>", COMMENT_ICON: "<svg version='1.1' id='Layer_2' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='15px' height='15px' viewBox='1 1.5 15 15' enable-background='new 1 1.5 15 15' xml:space='preserve'> <g> <g> <path fill='#211B1B' d='M14,1.5H3c-1.104,0-2,0.896-2,2v8c0,1.104,0.896,2,2,2h1.628l1.884,3l1.866-3H14c1.104,0,2-0.896,2-2v-8 C16,2.396,15.104,1.5,14,1.5z M15,11.5c0,0.553-0.447,1-1,1H8l-1.493,2l-1.504-1.991L5,12.5H3c-0.552,0-1-0.447-1-1v-8 c0-0.552,0.448-1,1-1h11c0.553,0,1,0.448,1,1V11.5z'/> </g> </g> </svg>", + REPLY_ICON: "<svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'viewBox='-158 242 18 18' style='enable-background:new -158 242 18 18;' xml:space='preserve'> <path d='M-142.2,252.6c-2-3-4.8-4.7-8.3-4.8v-3.3c0-0.2-0.1-0.3-0.2-0.3s-0.3,0-0.4,0.1l-6.9,6.2c-0.1,0.1-0.1,0.2-0.1,0.3 c0,0.1,0,0.2,0.1,0.3l6.9,6.4c0.1,0.1,0.3,0.1,0.4,0.1c0.1-0.1,0.2-0.2,0.2-0.4v-3.8c4.2,0,7.4,0.4,9.6,4.4c0.1,0.1,0.2,0.2,0.3,0.2 c0,0,0.1,0,0.1,0c0.2-0.1,0.3-0.3,0.2-0.4C-140.2,257.3-140.6,255-142.2,252.6z M-150.8,252.5c-0.2,0-0.4,0.2-0.4,0.4v3.3l-6-5.5 l6-5.3v2.8c0,0.2,0.2,0.4,0.4,0.4c3.3,0,6,1.5,8,4.5c0.5,0.8,0.9,1.6,1.2,2.3C-144,252.8-147.1,252.5-150.8,252.5z'/> </svg>", UPDATE_TYPING_MS: 5000, THEMES: { default: { diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 82e9bc447..494c38bdb 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -714,7 +714,7 @@ export function applyTheme(theme) { if (theme.linkColor) { changeCss('a, a:focus, a:hover, .btn, .btn:focus, .btn:hover', 'color:' + theme.linkColor, 1); - changeCss('.post .comment-icon__container', 'fill:' + theme.linkColor, 1); + changeCss('.post .comment-icon__container, .post .post__reply', 'fill:' + theme.linkColor, 1); } if (theme.buttonBg) { |