Этот коммит содержится в:
=Corey Hulen
2016-02-01 14:44:17 -08:00
родитель ea71731f83 f28486c455
Коммит b4ec690051
201 изменённых файлов: 12609 добавлений и 2308 удалений

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

@@ -3,6 +3,8 @@
var Modal = ReactBootstrap.Modal;
import {FormattedMessage} from 'mm-intl';
export default class AboutBuildModal extends React.Component {
constructor(props) {
super(props);
@@ -17,13 +19,28 @@ export default class AboutBuildModal extends React.Component {
const config = global.window.mm_config;
const license = global.window.mm_license;
let title = 'Team Edition';
let title = (
<FormattedMessage
id='about.teamEdtion'
defaultMessage='Team Edition'
/>
);
let licensee;
if (config.BuildEnterpriseReady === 'true' && license.IsLicensed === 'true') {
title = 'Enterprise Edition';
title = (
<FormattedMessage
id='about.enterpriseEdition'
defaultMessage='Enterprise Edition'
/>
);
licensee = (
<div className='row form-group'>
<div className='col-sm-3 info__label'>{'Licensed by:'}</div>
<div className='col-sm-3 info__label'>
<FormattedMessage
id='about.licensed'
defaultMessage='Licensed by:'
/>
</div>
<div className='col-sm-9'>{license.Company}</div>
</div>
);
@@ -35,25 +52,50 @@ export default class AboutBuildModal extends React.Component {
onHide={this.doHide}
>
<Modal.Header closeButton={true}>
<Modal.Title>{'About Mattermost'}</Modal.Title>
<Modal.Title>
<FormattedMessage
id='about.title'
defaultMessage='About Mattermost'
/>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<h4>{`Mattermost ${title}`}</h4>
<h4>{'Mattermost'} {title}</h4>
{licensee}
<div className='row form-group'>
<div className='col-sm-3 info__label'>{'Version:'}</div>
<div className='col-sm-3 info__label'>
<FormattedMessage
id='about.version'
defaultMessage='Version:'
/>
</div>
<div className='col-sm-9'>{config.Version}</div>
</div>
<div className='row form-group'>
<div className='col-sm-3 info__label'>{'Build Number:'}</div>
<div className='col-sm-3 info__label'>
<FormattedMessage
id='about.number'
defaultMessage='Build Number:'
/>
</div>
<div className='col-sm-9'>{config.BuildNumber}</div>
</div>
<div className='row form-group'>
<div className='col-sm-3 info__label'>{'Build Date:'}</div>
<div className='col-sm-3 info__label'>
<FormattedMessage
id='about.date'
defaultMessage='Build Date:'
/>
</div>
<div className='col-sm-9'>{config.BuildDate}</div>
</div>
<div className='row form-group'>
<div className='col-sm-3 info__label'>{'Build Hash:'}</div>
<div className='col-sm-3 info__label'>
<FormattedMessage
id='about.hash'
defaultMessage='Build Hash:'
/>
</div>
<div className='col-sm-9'>{config.BuildHash}</div>
</div>
</Modal.Body>
@@ -63,7 +105,10 @@ export default class AboutBuildModal extends React.Component {
className='btn btn-default'
onClick={this.doHide}
>
{'Close'}
<FormattedMessage
id='about.close'
defaultMessage='Close'
/>
</button>
</Modal.Footer>
</Modal>

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

@@ -8,7 +8,188 @@ import * as AsyncClient from '../utils/async_client.jsx';
import LoadingScreen from './loading_screen.jsx';
import * as Utils from '../utils/utils.jsx';
export default class AccessHistoryModal extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
sessionRevoked: {
id: 'access_history.sessionRevoked',
defaultMessage: 'The session with id {sessionId} was revoked'
},
channelCreated: {
id: 'access_history.channelCreated',
defaultMessage: 'Created the {channelName} channel/group'
},
establishedDM: {
id: 'access_history.establishedDM',
defaultMessage: 'Established a direct message channel with {username}'
},
nameUpdated: {
id: 'access_history.nameUpdated',
defaultMessage: 'Updated the {channelName} channel/group name'
},
headerUpdated: {
id: 'access_history.headerUpdated',
defaultMessage: 'Updated the {channelName} channel/group header'
},
channelDeleted: {
id: 'access_history.channelDeleted',
defaultMessage: 'Deleted the channel/group with the URL {url}'
},
userAdded: {
id: 'access_history.userAdded',
defaultMessage: 'Added {username} to the {channelName} channel/group'
},
userRemoved: {
id: 'access_history.userRemoved',
defaultMessage: 'Removed {username} to the {channelName} channel/group'
},
attemptedRegisterApp: {
id: 'access_history.attemptedRegisterApp',
defaultMessage: 'Attempted to register a new OAuth Application with ID {id}'
},
attemptedAllowOAuthAccess: {
id: 'access_history.attemptedAllowOAuthAccess',
defaultMessage: 'Attempted to allow a new OAuth service access'
},
successfullOAuthAccess: {
id: 'access_history.successfullOAuthAccess',
defaultMessage: 'Successfully gave a new OAuth service access'
},
failedOAuthAccess: {
id: 'access_history.failedOAuthAccess',
defaultMessage: 'Failed to allow a new OAuth service access - the redirect URI did not match the previously registered callback'
},
attemptedOAuthToken: {
id: 'access_history.attemptedOAuthToken',
defaultMessage: 'Attempted to get an OAuth access token'
},
successfullOAuthToken: {
id: 'access_history.successfullOAuthToken',
defaultMessage: 'Successfully added a new OAuth service'
},
oauthTokenFailed: {
id: 'access_history.oauthTokenFailed',
defaultMessage: 'Failed to get an OAuth access token - {token}'
},
attemptedLogin: {
id: 'access_history.attemptedLogin',
defaultMessage: 'Attempted to login'
},
successfullLogin: {
id: 'access_history.successfullLogin',
defaultMessage: 'Successfully logged in'
},
failedLogin: {
id: 'access_history.failedLogin',
defaultMessage: 'FAILED login attempt'
},
updatePicture: {
id: 'access_history.updatePicture',
defaultMessage: 'Updated your profile picture'
},
updateGeneral: {
id: 'access_history.updateGeneral',
defaultMessage: 'Updated the general settings of your account'
},
attemptedPassword: {
id: 'access_history.attemptedPassword',
defaultMessage: 'Attempted to change password'
},
successfullPassword: {
id: 'access_history.successfullPassword',
defaultMessage: 'Successfully changed password'
},
failedPassword: {
id: 'access_history.failedPassword',
defaultMessage: 'Failed to change password - tried to update user password who was logged in through oauth'
},
updatedRol: {
id: 'access_history.updatedRol',
defaultMessage: 'Updated user role(s) to '
},
member: {
id: 'access_history.member',
defaultMessage: 'member'
},
accountActive: {
id: 'access_history.accountActive',
defaultMessage: 'Account made active'
},
accountInactive: {
id: 'access_history.accountInactive',
defaultMessage: 'Account made inactive'
},
by: {
id: 'access_history.by',
defaultMessage: ' by {username}'
},
byAdmin: {
id: 'access_history.byAdmin',
defaultMessage: ' by an admin'
},
sentEmail: {
id: 'access_history.sentEmail',
defaultMessage: 'Sent an email to {email} to reset your password'
},
attemptedReset: {
id: 'access_history.attemptedReset',
defaultMessage: 'Attempted to reset password'
},
successfullReset: {
id: 'access_history.successfullReset',
defaultMessage: 'Successfully reset password'
},
updateGlobalNotifications: {
id: 'access_history.updateGlobalNotifications',
defaultMessage: 'Updated your global notification settings'
},
attemptedWebhookCreate: {
id: 'access_history.attemptedWebhookCreate',
defaultMessage: 'Attempted to create a webhook'
},
succcessfullWebhookCreate: {
id: 'access_history.successfullWebhookCreate',
defaultMessage: 'Successfully created a webhook'
},
failedWebhookCreate: {
id: 'access_history.failedWebhookCreate',
defaultMessage: 'Failed to create a webhook - bad channel permissions'
},
attemptedWebhookDelete: {
id: 'access_history.attemptedWebhookDelete',
defaultMessage: 'Attempted to delete a webhook'
},
successfullWebhookDelete: {
id: 'access_history.successfullWebhookDelete',
defaultMessage: 'Successfully deleted a webhook'
},
failedWebhookDelete: {
id: 'access_history.failedWebhookDelete',
defaultMessage: 'Failed to delete a webhook - inappropriate conditions'
},
logout: {
id: 'access_history.logout',
defaultMessage: 'Logged out of your account'
},
verified: {
id: 'access_history.verified',
defaultMessage: 'Sucessfully verified your email address'
},
revokedAll: {
id: 'access_history.revokedAll',
defaultMessage: 'Revoked all current sessions for the team'
},
loginAttempt: {
id: 'access_history.loginAttempt',
defaultMessage: ' (Login attempt)'
},
loginFailure: {
id: 'access_history.loginFailure',
defaultMessage: ' (Login failure)'
}
});
class AccessHistoryModal extends React.Component {
constructor(props) {
super(props);
@@ -70,11 +251,12 @@ export default class AccessHistoryModal extends React.Component {
this.setState({moreInfo: newMoreInfo});
}
handleRevokedSession(sessionId) {
return 'The session with id ' + sessionId + ' was revoked';
return this.props.intl.formatMessage(holders.sessionRevoked, {sessionId: sessionId});
}
formatAuditInfo(currentAudit) {
const currentActionURL = currentAudit.action.replace(/\/api\/v[1-9]/, '');
const {formatMessage} = this.props.intl;
let currentAuditDesc = '';
if (currentActionURL.indexOf('/channels') === 0) {
@@ -96,17 +278,17 @@ export default class AccessHistoryModal extends React.Component {
switch (currentActionURL) {
case '/channels/create':
currentAuditDesc = 'Created the ' + channelName + ' channel/group';
currentAuditDesc = formatMessage(holders.channelCreated, {channelName: channelName});
break;
case '/channels/create_direct':
currentAuditDesc = 'Established a direct message channel with ' + Utils.getDirectTeammate(channelObj.id).username;
currentAuditDesc = formatMessage(holders.establishedDM, {username: Utils.getDirectTeammate(channelObj.id).username});
break;
case '/channels/update':
currentAuditDesc = 'Updated the ' + channelName + ' channel/group name';
currentAuditDesc = formatMessage(holders.nameUpdated, {channelName: channelName});
break;
case '/channels/update_desc': // support the old path
case '/channels/update_header':
currentAuditDesc = 'Updated the ' + channelName + ' channel/group header';
currentAuditDesc = formatMessage(holders.headerUpdated, {channelName: channelName});
break;
default: {
let userIdField = [];
@@ -123,11 +305,11 @@ export default class AccessHistoryModal extends React.Component {
}
if (/\/channels\/[A-Za-z0-9]+\/delete/.test(currentActionURL)) {
currentAuditDesc = 'Deleted the channel/group with the URL ' + channelURL;
currentAuditDesc = formatMessage(holders.channelDeleted, {url: channelURL});
} else if (/\/channels\/[A-Za-z0-9]+\/add/.test(currentActionURL)) {
currentAuditDesc = 'Added ' + username + ' to the ' + channelName + ' channel/group';
currentAuditDesc = formatMessage(holders.userAdded, {username: username, channelName: channelName});
} else if (/\/channels\/[A-Za-z0-9]+\/remove/.test(currentActionURL)) {
currentAuditDesc = 'Removed ' + username + ' from the ' + channelName + ' channel/group';
currentAuditDesc = formatMessage(holders.userRemoved, {username: username, channelName: channelName});
}
break;
@@ -141,31 +323,31 @@ export default class AccessHistoryModal extends React.Component {
const clientIdField = oauthInfo[0].split('=');
if (clientIdField[0] === 'client_id') {
currentAuditDesc = 'Attempted to register a new OAuth Application with ID ' + clientIdField[1];
currentAuditDesc = formatMessage(holders.attemptedRegisterApp, {id: clientIdField[1]});
}
break;
}
case '/oauth/allow':
if (oauthInfo[0] === 'attempt') {
currentAuditDesc = 'Attempted to allow a new OAuth service access';
currentAuditDesc = formatMessage(holders.attemptedAllowOAuthAccess);
} else if (oauthInfo[0] === 'success') {
currentAuditDesc = 'Successfully gave a new OAuth service access';
currentAuditDesc = formatMessage(holders.successfullOAuthAccess);
} else if (oauthInfo[0] === 'fail - redirect_uri did not match registered callback') {
currentAuditDesc = 'Failed to allow a new OAuth service access - the redirect URI did not match the previously registered callback';
currentAuditDesc = formatMessage(holders.failedOAuthAccess);
}
break;
case '/oauth/access_token':
if (oauthInfo[0] === 'attempt') {
currentAuditDesc = 'Attempted to get an OAuth access token';
currentAuditDesc = formatMessage(holders.attemptedOAuthToken);
} else if (oauthInfo[0] === 'success') {
currentAuditDesc = 'Successfully added a new OAuth service';
currentAuditDesc = formatMessage(holders.successfullOAuthToken);
} else {
const oauthTokenFailure = oauthInfo[0].split('-');
if (oauthTokenFailure[0].trim() === 'fail' && oauthTokenFailure[1]) {
currentAuditDesc = 'Failed to get an OAuth access token - ' + oauthTokenFailure[1].trim();
currentAuditDesc = formatMessage(oauthTokenFailure, {token: oauthTokenFailure[1].trim()});
}
}
@@ -179,11 +361,11 @@ export default class AccessHistoryModal extends React.Component {
switch (currentActionURL) {
case '/users/login':
if (userInfo[0] === 'attempt') {
currentAuditDesc = 'Attempted to login';
currentAuditDesc = formatMessage(holders.attemptedLogin);
} else if (userInfo[0] === 'success') {
currentAuditDesc = 'Successfully logged in';
currentAuditDesc = formatMessage(holders.successfullLogin);
} else if (userInfo[0]) {
currentAuditDesc = 'FAILED login attempt';
currentAuditDesc = formatMessage(holders.failedLogin);
}
break;
@@ -191,29 +373,29 @@ export default class AccessHistoryModal extends React.Component {
currentAuditDesc = this.handleRevokedSession(userInfo[0].split('=')[1]);
break;
case '/users/newimage':
currentAuditDesc = 'Updated your profile picture';
currentAuditDesc = formatMessage(holders.updatePicture);
break;
case '/users/update':
currentAuditDesc = 'Updated the general settings of your account';
currentAuditDesc = formatMessage(holders.updateGeneral);
break;
case '/users/newpassword':
if (userInfo[0] === 'attempted') {
currentAuditDesc = 'Attempted to change password';
currentAuditDesc = formatMessage(holders.attemptedPassword);
} else if (userInfo[0] === 'completed') {
currentAuditDesc = 'Successfully changed password';
currentAuditDesc = formatMessage(holders.successfullPassword);
} else if (userInfo[0] === 'failed - tried to update user password who was logged in through oauth') {
currentAuditDesc = 'Failed to change password - tried to update user password who was logged in through oauth';
currentAuditDesc = formatMessage(holders.failedPassword);
}
break;
case '/users/update_roles': {
const userRoles = userInfo[0].split('=')[1];
currentAuditDesc = 'Updated user role(s) to ';
currentAuditDesc = formatMessage(holders.updatedRol);
if (userRoles.trim()) {
currentAuditDesc += userRoles;
} else {
currentAuditDesc += 'member';
currentAuditDesc += formatMessage(holders.member);
}
break;
@@ -225,9 +407,9 @@ export default class AccessHistoryModal extends React.Component {
/* Either describes account activation/deactivation or a revoked session as part of an account deactivation */
if (updateType === 'active') {
if (updateField === 'true') {
currentAuditDesc = 'Account made active';
currentAuditDesc = formatMessage(holders.accountActive);
} else if (updateField === 'false') {
currentAuditDesc = 'Account made inactive';
currentAuditDesc = formatMessage(holders.accountInactive);
}
const actingUserInfo = userInfo[1].split('=');
@@ -235,9 +417,9 @@ export default class AccessHistoryModal extends React.Component {
const actingUser = UserStore.getProfile(actingUserInfo[1]);
const currentUser = UserStore.getCurrentUser();
if (currentUser && actingUser && (Utils.isAdmin(currentUser.roles) || Utils.isSystemAdmin(currentUser.roles))) {
currentAuditDesc += ' by ' + actingUser.username;
currentAuditDesc += formatMessage(holders.by, {username: actingUser.username});
} else if (currentUser && actingUser) {
currentAuditDesc += ' by an admin';
currentAuditDesc += formatMessage(holders.byAdmin);
}
}
} else if (updateType === 'session_id') {
@@ -247,18 +429,18 @@ export default class AccessHistoryModal extends React.Component {
break;
}
case '/users/send_password_reset':
currentAuditDesc = 'Sent an email to ' + userInfo[0].split('=')[1] + ' to reset your password';
currentAuditDesc = formatMessage(holders.sentEmail, {email: userInfo[0].split('=')[1]});
break;
case '/users/reset_password':
if (userInfo[0] === 'attempt') {
currentAuditDesc = 'Attempted to reset password';
currentAuditDesc = formatMessage(holders.attemptedReset);
} else if (userInfo[0] === 'success') {
currentAuditDesc = 'Successfully reset password';
currentAuditDesc = formatMessage(holders.successfullReset);
}
break;
case '/users/update_notify':
currentAuditDesc = 'Updated your global notification settings';
currentAuditDesc = formatMessage(holders.updateGlobalNotifications);
break;
default:
break;
@@ -269,21 +451,21 @@ export default class AccessHistoryModal extends React.Component {
switch (currentActionURL) {
case '/hooks/incoming/create':
if (webhookInfo[0] === 'attempt') {
currentAuditDesc = 'Attempted to create a webhook';
currentAuditDesc = formatMessage(holders.attemptedWebhookCreate);
} else if (webhookInfo[0] === 'success') {
currentAuditDesc = 'Successfully created a webhook';
currentAuditDesc = formatMessage(holders.succcessfullWebhookCreate);
} else if (webhookInfo[0] === 'fail - bad channel permissions') {
currentAuditDesc = 'Failed to create a webhook - bad channel permissions';
currentAuditDesc = formatMessage(holders.failedWebhookCreate);
}
break;
case '/hooks/incoming/delete':
if (webhookInfo[0] === 'attempt') {
currentAuditDesc = 'Attempted to delete a webhook';
currentAuditDesc = formatMessage(holders.attemptedWebhookDelete);
} else if (webhookInfo[0] === 'success') {
currentAuditDesc = 'Successfully deleted a webhook';
currentAuditDesc = formatMessage(holders.successfullWebhookDelete);
} else if (webhookInfo[0] === 'fail - inappropriate conditions') {
currentAuditDesc = 'Failed to delete a webhook - inappropriate conditions';
currentAuditDesc = formatMessage(holders.failedWebhookDelete);
}
break;
@@ -293,10 +475,10 @@ export default class AccessHistoryModal extends React.Component {
} else {
switch (currentActionURL) {
case '/logout':
currentAuditDesc = 'Logged out of your account';
currentAuditDesc = formatMessage(holders.logout);
break;
case '/verify_email':
currentAuditDesc = 'Sucessfully verified your email address';
currentAuditDesc = formatMessage(holders.verified);
break;
default:
break;
@@ -307,7 +489,7 @@ export default class AccessHistoryModal extends React.Component {
if (!currentAuditDesc) {
/* Currently not called anywhere */
if (currentAudit.extra_info.indexOf('revoked_all=') >= 0) {
currentAuditDesc = 'Revoked all current sessions for the team';
currentAuditDesc = formatMessage(holders.revokedAll);
} else {
let currentActionDesc = '';
if (currentActionURL && currentActionURL.lastIndexOf('/') !== -1) {
@@ -328,12 +510,14 @@ export default class AccessHistoryModal extends React.Component {
}
const currentDate = new Date(currentAudit.create_at);
const currentAuditInfo = currentDate.toDateString() + ' - ' + currentDate.toLocaleTimeString(navigator.language, {hour: '2-digit', minute: '2-digit'}) + ' | ' + currentAuditDesc;
const currentAuditInfo = currentDate.toLocaleDateString(global.window.mm_locale, {month: 'short', day: '2-digit', year: 'numeric'}) + ' - ' +
currentDate.toLocaleTimeString(global.window.mm_locale, {hour: '2-digit', minute: '2-digit'}) + ' | ' + currentAuditDesc;
return currentAuditInfo;
}
render() {
var accessList = [];
const {formatMessage} = this.props.intl;
for (var i = 0; i < this.state.audits.length; i++) {
const currentAudit = this.state.audits[i];
const currentAuditInfo = this.formatAuditInfo(currentAudit);
@@ -344,7 +528,10 @@ export default class AccessHistoryModal extends React.Component {
className='theme'
onClick={this.handleMoreInfo.bind(this, i)}
>
{'More info'}
<FormattedMessage
id='access_history.moreInfo'
defaultMessage='More info'
/>
</a>
);
@@ -354,17 +541,33 @@ export default class AccessHistoryModal extends React.Component {
if (currentAudit.action.search('/users/login') >= 0) {
if (currentAudit.extra_info === 'attempt') {
currentAudit.session_id += ' (Login attempt)';
currentAudit.session_id += formatMessage(holders.loginAttempt);
} else {
currentAudit.session_id += ' (Login failure)';
currentAudit.session_id += formatMessage(holders.loginFailure);
}
}
}
moreInfo = (
<div>
<div>{'IP: ' + currentAudit.ip_address}</div>
<div>{'Session ID: ' + currentAudit.session_id}</div>
<div>
<FormattedMessage
id='access_history.ip'
defaultMessage='IP: {ip}'
values={{
ip: currentAudit.ip_address
}}
/>
</div>
<div>
<FormattedMessage
id='access_history.session'
defaultMessage='Session ID: {id}'
values={{
id: currentAudit.session_id
}}
/>
</div>
</div>
);
}
@@ -404,7 +607,12 @@ export default class AccessHistoryModal extends React.Component {
bsSize='large'
>
<Modal.Header closeButton={true}>
<Modal.Title>{'Access History'}</Modal.Title>
<Modal.Title>
<FormattedMessage
id='access_history.title'
defaultMessage='Access History'
/>
</Modal.Title>
</Modal.Header>
<Modal.Body ref='modalBody'>
{content}
@@ -415,6 +623,9 @@ export default class AccessHistoryModal extends React.Component {
}
AccessHistoryModal.propTypes = {
intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
onHide: React.PropTypes.func.isRequired
};
export default injectIntl(AccessHistoryModal);

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

@@ -8,6 +8,8 @@ const Modal = ReactBootstrap.Modal;
import LoadingScreen from './loading_screen.jsx';
import * as Utils from '../utils/utils.jsx';
import {FormattedMessage} from 'mm-intl';
export default class ActivityLogModal extends React.Component {
constructor(props) {
super(props);
@@ -100,15 +102,33 @@ export default class ActivityLogModal extends React.Component {
if (currentSession.props.platform === 'Windows') {
devicePicture = 'fa fa-windows';
} else if (currentSession.device_id && currentSession.device_id.indexOf('apple:') === 0) {
devicePicture = 'fa fa-apple';
devicePlatform = (
<FormattedMessage
id='activity_log_modal.iphoneNativeApp'
defaultMessage='iPhone Native App'
/>
);
} else if (currentSession.device_id && currentSession.device_id.indexOf('android:') === 0) {
devicePlatform = (
<FormattedMessage
id='activity_log_modal.androidNativeApp'
defaultMessage='Android Native App'
/>
);
devicePicture = 'fa fa-android';
} else if (currentSession.props.platform === 'Macintosh' ||
currentSession.props.platform === 'iPhone') {
devicePicture = 'fa fa-apple';
} else if (currentSession.props.platform.browser.indexOf('Mattermost/') === 0) {
devicePicture = 'fa fa-apple';
devicePlatform = 'iPhone';
} else if (currentSession.props.platform === 'Linux') {
if (currentSession.props.os.indexOf('Android') >= 0) {
devicePlatform = 'Android';
devicePlatform = (
<FormattedMessage
id='activity_log_modal.android'
defaultMessage='Android'
/>
);
devicePicture = 'fa fa-android';
} else {
devicePicture = 'fa fa-linux';
@@ -119,10 +139,43 @@ export default class ActivityLogModal extends React.Component {
if (this.state.moreInfo[i]) {
moreInfo = (
<div>
<div>{`First time active: ${firstAccessTime.toDateString()}, ${lastAccessTime.toLocaleTimeString()}`}</div>
<div>{`OS: ${currentSession.props.os}`}</div>
<div>{`Browser: ${currentSession.props.browser}`}</div>
<div>{`Session ID: ${currentSession.id}`}</div>
<div>
<FormattedMessage
id='activity_log.firstTime'
defaultMessage='First time active: {date}, {time}'
values={{
date: firstAccessTime.toLocaleDateString(global.window.mm_locale, {month: 'short', day: '2-digit', year: 'numeric'}),
time: lastAccessTime.toLocaleTimeString(global.window.mm_locale, {hour: '2-digit', minute: '2-digit'})
}}
/>
</div>
<div>
<FormattedMessage
id='activity_log.os'
defaultMessage='OS: {os}'
values={{
os: currentSession.props.os
}}
/>
</div>
<div>
<FormattedMessage
id='activity_log.browser'
defaultMessage='Browser: {browser}'
values={{
browser: currentSession.props.browser
}}
/>
</div>
<div>
<FormattedMessage
id='activity_log.sessionId'
defaultMessage='Session ID: {id}'
values={{
id: currentSession.id
}}
/>
</div>
</div>
);
} else {
@@ -132,7 +185,10 @@ export default class ActivityLogModal extends React.Component {
href='#'
onClick={this.handleMoreInfo.bind(this, i)}
>
More info
<FormattedMessage
id='activity_log.moreInfo'
defaultMessage='More info'
/>
</a>
);
}
@@ -145,7 +201,16 @@ export default class ActivityLogModal extends React.Component {
<div className='activity-log__report'>
<div className='report__platform'><i className={devicePicture} />{devicePlatform}</div>
<div className='report__info'>
<div>{`Last activity: ${lastAccessTime.toDateString()}, ${lastAccessTime.toLocaleTimeString()}`}</div>
<div>
<FormattedMessage
id='activity_log.lastActivity'
defaultMessage='Last activity: {date}, {time}'
values={{
date: lastAccessTime.toLocaleDateString(global.window.mm_locale, {month: 'short', day: '2-digit', year: 'numeric'}),
time: lastAccessTime.toLocaleTimeString(global.window.mm_locale, {hour: '2-digit', minute: '2-digit'})
}}
/>
</div>
{moreInfo}
</div>
</div>
@@ -154,7 +219,10 @@ export default class ActivityLogModal extends React.Component {
onClick={this.submitRevoke.bind(this, currentSession.id)}
className='btn btn-primary'
>
Logout
<FormattedMessage
id='activity_log.logout'
defaultMessage='Logout'
/>
</button>
</div>
</div>
@@ -175,10 +243,20 @@ export default class ActivityLogModal extends React.Component {
bsSize='large'
>
<Modal.Header closeButton={true}>
<Modal.Title>{'Active Sessions'}</Modal.Title>
<Modal.Title>
<FormattedMessage
id='activity_log.activeSessions'
defaultMessage='Active Sessions'
/>
</Modal.Title>
</Modal.Header>
<Modal.Body ref='modalBody'>
<p className='session-help-text'>{'Sessions are created when you log in with your email and password to a new browser on a device. Sessions let you use Mattermost for up to 30 days without having to log in again. If you want to log out sooner, use the \'Logout\' button below to end a session.'}</p>
<p className='session-help-text'>
<FormattedMessage
id='activity_log.sessionsDescription'
defaultMessage="Sessions are created when you log in with your email and password to a new browser on a device. Sessions let you use Mattermost for up to 30 days without having to log in again. If you want to log out sooner, use the 'Logout' button below to end a session."
/>
</p>
{content}
</Modal.Body>
</Modal>

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

@@ -7,6 +7,8 @@ import TeamStore from '../../stores/team_store.jsx';
import Constants from '../../utils/constants.jsx';
import {FormattedMessage} from 'mm-intl';
function getStateFromStores() {
return {currentTeam: TeamStore.getCurrent()};
}
@@ -66,7 +68,13 @@ export default class AdminNavbarDropdown extends React.Component {
<a
href={Utils.getWindowLocationOrigin() + '/' + this.state.currentTeam.name}
>
{'Switch to ' + this.state.currentTeam.display_name}
<FormattedMessage
id='admin.nav.switch'
defaultMessage='Switch to {display_name}'
values={{
display_name: this.state.currentTeam.display_name
}}
/>
</a>
</li>
<li>
@@ -74,7 +82,10 @@ export default class AdminNavbarDropdown extends React.Component {
href='#'
onClick={this.handleLogoutClick}
>
{'Logout'}
<FormattedMessage
id='admin.nav.logout'
defaultMessage='Logout'
/>
</a>
</li>
<li className='divider'></li>
@@ -83,7 +94,10 @@ export default class AdminNavbarDropdown extends React.Component {
target='_blank'
href='/static/help/help.html'
>
{'Help'}
<FormattedMessage
id='admin.nav.help'
defaultMessage='Help'
/>
</a>
</li>
<li>
@@ -91,7 +105,10 @@ export default class AdminNavbarDropdown extends React.Component {
target='_blank'
href='/static/help/report_problem.html'
>
{'Report a Problem'}
<FormattedMessage
id='admin.nav.report'
defaultMessage='Report a Problem'
/>
</a>
</li>
</ul>

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

@@ -5,6 +5,8 @@ import AdminSidebarHeader from './admin_sidebar_header.jsx';
import SelectTeamModal from './select_team_modal.jsx';
import * as Utils from '../../utils/utils.jsx';
import {FormattedMessage} from 'mm-intl';
const Tooltip = ReactBootstrap.Tooltip;
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
@@ -82,12 +84,27 @@ export default class AdminSidebar extends React.Component {
render() {
var count = '*';
var teams = 'Loading';
var teams = (
<FormattedMessage
id='admin.sidebar.loading'
defaultMessage='Loading'
/>
);
const removeTooltip = (
<Tooltip id='remove-team-tooltip'>{'Remove team from sidebar menu'}</Tooltip>
<Tooltip id='remove-team-tooltip'>
<FormattedMessage
id='admin.sidebar.rmTeamSidebar'
defaultMessage='Remove team from sidebar menu'
/>
</Tooltip>
);
const addTeamTooltip = (
<Tooltip id='add-team-tooltip'>{'Add team from sidebar menu'}</Tooltip>
<Tooltip id='add-team-tooltip'>
<FormattedMessage
id='admin.sidebar.addTeamSidebar'
defaultMessage='Add team from sidebar menu'
/>
</Tooltip>
);
if (this.props.teams != null) {
@@ -134,7 +151,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('team_users', team.id)}
onClick={this.handleClick.bind(this, 'team_users', team.id)}
>
{'- Users'}
<FormattedMessage
id='admin.sidebar.users'
defaultMessage='- Users'
/>
</a>
</li>
<li>
@@ -143,7 +163,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('team_analytics', team.id)}
onClick={this.handleClick.bind(this, 'team_analytics', team.id)}
>
{'- Statistics'}
<FormattedMessage
id='admin.sidebar.statistics'
defaultMessage='- Statistics'
/>
</a>
</li>
</ul>
@@ -166,7 +189,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('ldap_settings')}
onClick={this.handleClick.bind(this, 'ldap_settings', null)}
>
{'LDAP Settings'}
<FormattedMessage
id='admin.sidebar.ldap'
defaultMessage='LDAP Settings'
/>
</a>
</li>
);
@@ -179,7 +205,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('license')}
onClick={this.handleClick.bind(this, 'license', null)}
>
{'Edition and License'}
<FormattedMessage
id='admin.sidebar.license'
defaultMessage='Edition and License'
/>
</a>
</li>
);
@@ -196,7 +225,12 @@ export default class AdminSidebar extends React.Component {
<li>
<h4>
<span className='icon fa fa-gear'></span>
<span>{'SITE REPORTS'}</span>
<span>
<FormattedMessage
id='admin.sidebar.reports'
defaultMessage='SITE REPORTS'
/>
</span>
</h4>
</li>
</ul>
@@ -207,7 +241,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('system_analytics')}
onClick={this.handleClick.bind(this, 'system_analytics', null)}
>
{'View Statistics'}
<FormattedMessage
id='admin.sidebar.view_statistics'
defaultMessage='View Statistics'
/>
</a>
</li>
</ul>
@@ -215,7 +252,12 @@ export default class AdminSidebar extends React.Component {
<li>
<h4>
<span className='icon fa fa-gear'></span>
<span>{'SETTINGS'}</span>
<span>
<FormattedMessage
id='admin.sidebar.settings'
defaultMessage='SETTINGS'
/>
</span>
</h4>
</li>
</ul>
@@ -226,7 +268,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('service_settings')}
onClick={this.handleClick.bind(this, 'service_settings', null)}
>
{'Service Settings'}
<FormattedMessage
id='admin.sidebar.service'
defaultMessage='Service Settings'
/>
</a>
</li>
<li>
@@ -235,7 +280,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('team_settings')}
onClick={this.handleClick.bind(this, 'team_settings', null)}
>
{'Team Settings'}
<FormattedMessage
id='admin.sidebar.team'
defaultMessage='Team Settings'
/>
</a>
</li>
<li>
@@ -244,7 +292,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('sql_settings')}
onClick={this.handleClick.bind(this, 'sql_settings', null)}
>
{'SQL Settings'}
<FormattedMessage
id='admin.sidebar.sql'
defaultMessage='SQL Settings'
/>
</a>
</li>
<li>
@@ -253,7 +304,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('email_settings')}
onClick={this.handleClick.bind(this, 'email_settings', null)}
>
{'Email Settings'}
<FormattedMessage
id='admin.sidebar.email'
defaultMessage='Email Settings'
/>
</a>
</li>
<li>
@@ -262,7 +316,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('image_settings')}
onClick={this.handleClick.bind(this, 'image_settings', null)}
>
{'File Settings'}
<FormattedMessage
id='admin.sidebar.file'
defaultMessage='File Settings'
/>
</a>
</li>
<li>
@@ -271,7 +328,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('log_settings')}
onClick={this.handleClick.bind(this, 'log_settings', null)}
>
{'Log Settings'}
<FormattedMessage
id='admin.sidebar.log'
defaultMessage='Log Settings'
/>
</a>
</li>
<li>
@@ -280,7 +340,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('rate_settings')}
onClick={this.handleClick.bind(this, 'rate_settings', null)}
>
{'Rate Limit Settings'}
<FormattedMessage
id='admin.sidebar.rate_limit'
defaultMessage='Rate Limit Settings'
/>
</a>
</li>
<li>
@@ -289,7 +352,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('privacy_settings')}
onClick={this.handleClick.bind(this, 'privacy_settings', null)}
>
{'Privacy Settings'}
<FormattedMessage
id='admin.sidebar.privacy'
defaultMessage='Privacy Settings'
/>
</a>
</li>
<li>
@@ -298,7 +364,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('gitlab_settings')}
onClick={this.handleClick.bind(this, 'gitlab_settings', null)}
>
{'GitLab Settings'}
<FormattedMessage
id='admin.sidebar.gitlab'
defaultMessage='GitLab Settings'
/>
</a>
</li>
{ldapSettings}
@@ -308,7 +377,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('legal_and_support_settings')}
onClick={this.handleClick.bind(this, 'legal_and_support_settings', null)}
>
{'Legal and Support Settings'}
<FormattedMessage
id='admin.sidebar.support'
defaultMessage='Legal and Support Settings'
/>
</a>
</li>
</ul>
@@ -316,7 +388,15 @@ export default class AdminSidebar extends React.Component {
<li>
<h4>
<span className='icon fa fa-gear'></span>
<span>{'TEAMS (' + count + ')'}</span>
<span>
<FormattedMessage
id='admin.sidebar.teams'
defaultMessage='TEAMS ({count})'
values={{
count: count
}}
/>
</span>
<span className='menu-icon--right'>
<OverlayTrigger
delayShow={1000}
@@ -345,7 +425,12 @@ export default class AdminSidebar extends React.Component {
<li>
<h4>
<span className='icon fa fa-gear'></span>
<span>{'OTHER'}</span>
<span>
<FormattedMessage
id='admin.sidebar.other'
defaultMessage='OTHER'
/>
</span>
</h4>
</li>
</ul>
@@ -357,7 +442,10 @@ export default class AdminSidebar extends React.Component {
className={this.isSelected('logs')}
onClick={this.handleClick.bind(this, 'logs', null)}
>
{'Logs'}
<FormattedMessage
id='admin.sidebar.logs'
defaultMessage='Logs'
/>
</a>
</li>
</ul>

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

@@ -5,6 +5,8 @@ import AdminNavbarDropdown from './admin_navbar_dropdown.jsx';
import UserStore from '../../stores/user_store.jsx';
import * as Utils from '../../utils/utils.jsx';
import {FormattedMessage} from 'mm-intl';
export default class SidebarHeader extends React.Component {
constructor(props) {
super(props);
@@ -51,7 +53,12 @@ export default class SidebarHeader extends React.Component {
{profilePicture}
<div className='header__info'>
<div className='user__name'>{'@' + me.username}</div>
<div className='team__name'>{'System Console'}</div>
<div className='team__name'>
<FormattedMessage
id='admin.sidebarHeader.systemConsole'
defaultMessage='System Console'
/>
</div>
</div>
</a>
<AdminNavbarDropdown ref='dropdown' />

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

@@ -8,6 +8,8 @@ import LineChart from './line_chart.jsx';
var Tooltip = ReactBootstrap.Tooltip;
var OverlayTrigger = ReactBootstrap.OverlayTrigger;
import {FormattedMessage} from 'mm-intl';
export default class Analytics extends React.Component {
constructor(props) {
super(props);
@@ -21,11 +23,23 @@ export default class Analytics extends React.Component {
serverError = <div className='form-group has-error'><label className='control-label'>{this.props.serverError}</label></div>;
}
let loading = (
<FormattedMessage
id='admin.analytics.loading'
defaultMessage='Loading...'
/>
);
var totalCount = (
<div className='col-sm-3'>
<div className='total-count'>
<div className='title'>{'Total Users'}<i className='fa fa-users'/></div>
<div className='content'>{this.props.uniqueUserCount == null ? 'Loading...' : this.props.uniqueUserCount}</div>
<div className='title'>
<FormattedMessage
id='admin.analytics.totalUsers'
defaultMessage='Total Users'
/>
<i className='fa fa-users'/></div>
<div className='content'>{this.props.uniqueUserCount == null ? loading : this.props.uniqueUserCount}</div>
</div>
</div>
);
@@ -33,8 +47,13 @@ export default class Analytics extends React.Component {
var openChannelCount = (
<div className='col-sm-3'>
<div className='total-count'>
<div className='title'>{'Public Channels'}<i className='fa fa-globe'/></div>
<div className='content'>{this.props.channelOpenCount == null ? 'Loading...' : this.props.channelOpenCount}</div>
<div className='title'>
<FormattedMessage
id='admin.analytics.publicChannels'
defaultMessage='Public Channels'
/>
<i className='fa fa-globe'/></div>
<div className='content'>{this.props.channelOpenCount == null ? loading : this.props.channelOpenCount}</div>
</div>
</div>
);
@@ -42,8 +61,13 @@ export default class Analytics extends React.Component {
var openPrivateCount = (
<div className='col-sm-3'>
<div className='total-count'>
<div className='title'>{'Private Groups'}<i className='fa fa-lock'/></div>
<div className='content'>{this.props.channelPrivateCount == null ? 'Loading...' : this.props.channelPrivateCount}</div>
<div className='title'>
<FormattedMessage
id='admin.analytics.privateGroups'
defaultMessage='Private Groups'
/>
<i className='fa fa-lock'/></div>
<div className='content'>{this.props.channelPrivateCount == null ? loading : this.props.channelPrivateCount}</div>
</div>
</div>
);
@@ -51,8 +75,13 @@ export default class Analytics extends React.Component {
var postCount = (
<div className='col-sm-3'>
<div className='total-count'>
<div className='title'>{'Total Posts'}<i className='fa fa-comment'/></div>
<div className='content'>{this.props.postCount == null ? 'Loading...' : this.props.postCount}</div>
<div className='title'>
<FormattedMessage
id='admin.analytics.totalPosts'
defaultMessage='Total Posts'
/>
<i className='fa fa-comment'/></div>
<div className='content'>{this.props.postCount == null ? loading : this.props.postCount}</div>
</div>
</div>
);
@@ -60,8 +89,13 @@ export default class Analytics extends React.Component {
var postCountsByDay = (
<div className='col-sm-12'>
<div className='total-count by-day'>
<div className='title'>{'Total Posts'}</div>
<div className='content'>{'Loading...'}</div>
<div className='title'>
<FormattedMessage
id='admin.analytics.totalPosts'
defaultMessage='Total Posts'
/>
</div>
<div className='content'>{loading}</div>
</div>
</div>
);
@@ -69,7 +103,14 @@ export default class Analytics extends React.Component {
if (this.props.postCountsDay != null) {
let content;
if (this.props.postCountsDay.labels.length === 0) {
content = 'Not enough data for a meaningful representation.';
content = (
<h5>
<FormattedMessage
id='admin.analytics.meaningful'
defaultMessage='Not enough data for a meaningful representation.'
/>
</h5>
);
} else {
content = (
<LineChart
@@ -82,7 +123,12 @@ export default class Analytics extends React.Component {
postCountsByDay = (
<div className='col-sm-12'>
<div className='total-count by-day'>
<div className='title'>{'Total Posts'}</div>
<div className='title'>
<FormattedMessage
id='admin.analytics.totalPosts'
defaultMessage='Total Posts'
/>
</div>
<div className='content'>
{content}
</div>
@@ -94,8 +140,13 @@ export default class Analytics extends React.Component {
var usersWithPostsByDay = (
<div className='col-sm-12'>
<div className='total-count by-day'>
<div className='title'>{'Active Users With Posts'}</div>
<div className='content'>{'Loading...'}</div>
<div className='title'>
<FormattedMessage
id='admin.analytics.activeUsers'
defaultMessage='Active Users With Posts'
/>
</div>
<div className='content'>{loading}</div>
</div>
</div>
);
@@ -103,7 +154,14 @@ export default class Analytics extends React.Component {
if (this.props.userCountsWithPostsDay != null) {
let content;
if (this.props.userCountsWithPostsDay.labels.length === 0) {
content = 'Not enough data for a meaningful representation.';
content = (
<h5>
<FormattedMessage
id='admin.analytics.meaningful'
defaultMessage='Not enough data for a meaningful representation.'
/>
</h5>
);
} else {
content = (
<LineChart
@@ -116,7 +174,12 @@ export default class Analytics extends React.Component {
usersWithPostsByDay = (
<div className='col-sm-12'>
<div className='total-count by-day'>
<div className='title'>{'Active Users With Posts'}</div>
<div className='title'>
<FormattedMessage
id='admin.analytics.activeUsers'
defaultMessage='Active Users With Posts'
/>
</div>
<div className='content'>
{content}
</div>
@@ -129,7 +192,7 @@ export default class Analytics extends React.Component {
if (this.props.recentActiveUsers != null) {
let content;
if (this.props.recentActiveUsers.length === 0) {
content = 'Loading...';
content = loading;
} else {
content = (
<table>
@@ -167,7 +230,12 @@ export default class Analytics extends React.Component {
recentActiveUser = (
<div className='col-sm-6'>
<div className='total-count recent-active-users'>
<div className='title'>{'Recent Active Users'}</div>
<div className='title'>
<FormattedMessage
id='admin.analytics.recentActive'
defaultMessage='Recent Active Users'
/>
</div>
<div className='content'>
{content}
</div>
@@ -180,7 +248,7 @@ export default class Analytics extends React.Component {
if (this.props.newlyCreatedUsers != null) {
let content;
if (this.props.newlyCreatedUsers.length === 0) {
content = 'Loading...';
content = loading;
} else {
content = (
<table>
@@ -218,7 +286,12 @@ export default class Analytics extends React.Component {
newUsers = (
<div className='col-sm-6'>
<div className='total-count recent-active-users'>
<div className='title'>{'Newly Created Users'}</div>
<div className='title'>
<FormattedMessage
id='admin.analytics.newlyCreated'
defaultMessage='Newly Created Users'
/>
</div>
<div className='content'>
{content}
</div>
@@ -229,7 +302,15 @@ export default class Analytics extends React.Component {
return (
<div className='wrapper--fixed team_statistics'>
<h3>{'Statistics for ' + this.props.title}</h3>
<h3>
<FormattedMessage
id='admin.analytics.title'
defaultMessage='Statistics for {title}'
values={{
title: this.props.title
}}
/>
</h3>
{serverError}
<div className='row'>
{totalCount}

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

@@ -5,7 +5,68 @@ import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import crypto from 'crypto';
export default class EmailSettings extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
var holders = defineMessages({
notificationDisplayExample: {
id: 'admin.email.notificationDisplayExample',
defaultMessage: 'Ex: "Mattermost Notification", "System", "No-Reply"'
},
notificationEmailExample: {
id: 'admin.email.notificationEmailExample',
defaultMessage: 'Ex: "mattermost@yourcompany.com", "admin@yourcompany.com"'
},
smtpUsernameExample: {
id: 'admin.email.smtpUsernameExample',
defaultMessage: 'Ex: "admin@yourcompany.com", "AKIADTOVBGERKLCBV"'
},
smtpPasswordExample: {
id: 'admin.email.smtpPasswordExample',
defaultMessage: 'Ex: "yourpassword", "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
},
smtpServerExample: {
id: 'admin.email.smtpServerExample',
defaultMessage: 'Ex: "smtp.yourcompany.com", "email-smtp.us-east-1.amazonaws.com"'
},
smtpPortExample: {
id: 'admin.email.smtpPortExample',
defaultMessage: 'Ex: "25", "465"'
},
connectionSecurityNone: {
id: 'admin.email.connectionSecurityNone',
defaultMessage: 'None'
},
connectionSecurityTls: {
id: 'admin.email.connectionSecurityTls',
defaultMessage: 'TLS (Recommended)'
},
connectionSecurityStart: {
id: 'admin.email.connectionSecurityStart',
defaultMessage: 'STARTTLS'
},
inviteSaltExample: {
id: 'admin.email.inviteSaltExample',
defaultMessage: 'Ex "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo"'
},
passwordSaltExample: {
id: 'admin.email.passwordSaltExample',
defaultMessage: 'Ex "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo"'
},
pushServerEx: {
id: 'admin.email.pushServerEx',
defaultMessage: 'E.g.: "https://push-test.mattermost.com"'
},
testing: {
id: 'admin.email.testing',
defaultMessage: 'Testing...'
},
saving: {
id: 'admin.email.saving',
defaultMessage: 'Saving Config...'
}
});
class EmailSettings extends React.Component {
constructor(props) {
super(props);
@@ -156,6 +217,7 @@ export default class EmailSettings extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
var serverError = '';
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
@@ -170,7 +232,11 @@ export default class EmailSettings extends React.Component {
if (this.state.emailSuccess) {
emailSuccess = (
<div className='alert alert-success'>
<i className='fa fa-check'></i>{'No errors were reported while sending an email. Please check your inbox to make sure.'}
<i className='fa fa-check'></i>
<FormattedMessage
id='admin.email.emailSuccess'
defaultMessage='No errors were reported while sending an email. Please check your inbox to make sure.'
/>
</div>
);
}
@@ -179,14 +245,26 @@ export default class EmailSettings extends React.Component {
if (this.state.emailFail) {
emailSuccess = (
<div className='alert alert-warning'>
<i className='fa fa-warning'></i>{'Connection unsuccessful: ' + this.state.emailFail}
<i className='fa fa-warning'></i>
<FormattedMessage
id='admin.email.emailFail'
defaultMessage='Connection unsuccessful: {error}'
values={{
error: this.state.emailFail
}}
/>
</div>
);
}
return (
<div className='wrapper--fixed'>
<h3>{'Email Settings'}</h3>
<h3>
<FormattedMessage
id='admin.email.emailSettings'
defaultMessage='Email Settings'
/>
</h3>
<form
className='form-horizontal'
role='form'
@@ -197,7 +275,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='allowSignUpWithEmail'
>
{'Allow Sign Up With Email: '}
<FormattedMessage
id='admin.email.allowSignupTitle'
defaultMessage='Allow Sign Up With Email: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -209,7 +290,10 @@ export default class EmailSettings extends React.Component {
defaultChecked={this.props.config.EmailSettings.EnableSignUpWithEmail}
onChange={this.handleChange.bind(this, 'allowSignUpWithEmail_true')}
/>
{'true'}
<FormattedMessage
id='admin.email.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -219,9 +303,17 @@ export default class EmailSettings extends React.Component {
defaultChecked={!this.props.config.EmailSettings.EnableSignUpWithEmail}
onChange={this.handleChange.bind(this, 'allowSignUpWithEmail_false')}
/>
{'false'}
<FormattedMessage
id='admin.email.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When true, Mattermost allows team creation and account signup using email and password. This value should be false only when you want to limit signup to a single-sign-on service like OAuth or LDAP.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.email.allowSignupDescription'
defaultMessage='When true, Mattermost allows team creation and account signup using email and password. This value should be false only when you want to limit signup to a single-sign-on service like OAuth or LDAP.'
/>
</p>
</div>
</div>
@@ -230,7 +322,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='sendEmailNotifications'
>
{'Send Email Notifications: '}
<FormattedMessage
id='admin.email.notificationsTitle'
defaultMessage='Send Email Notifications: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -242,7 +337,10 @@ export default class EmailSettings extends React.Component {
defaultChecked={this.props.config.EmailSettings.SendEmailNotifications}
onChange={this.handleChange.bind(this, 'sendEmailNotifications_true')}
/>
{'true'}
<FormattedMessage
id='admin.email.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -252,9 +350,17 @@ export default class EmailSettings extends React.Component {
defaultChecked={!this.props.config.EmailSettings.SendEmailNotifications}
onChange={this.handleChange.bind(this, 'sendEmailNotifications_false')}
/>
{'false'}
<FormattedMessage
id='admin.email.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'Typically set to true in production. When true, Mattermost attempts to send email notifications. Developers may set this field to false to skip email setup for faster development.\nSetting this to true removes the Preview Mode banner (requires logging out and logging back in after setting is changed).'}</p>
<p className='help-text'>
<FormattedHTMLMessage
id='admin.email.notificationsDescription'
defaultMessage='Typically set to true in production. When true, Mattermost attempts to send email notifications. Developers may set this field to false to skip email setup for faster development.<br />Setting this to true removes the Preview Mode banner (requires logging out and logging back in after setting is changed).'
/>
</p>
</div>
</div>
@@ -263,7 +369,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='requireEmailVerification'
>
{'Require Email Verification: '}
<FormattedMessage
id='admin.email.requireVerificationTitle'
defaultMessage='Require Email Verification: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -276,7 +385,10 @@ export default class EmailSettings extends React.Component {
onChange={this.handleChange.bind(this, 'requireEmailVerification_true')}
disabled={!this.state.sendEmailNotifications}
/>
{'true'}
<FormattedMessage
id='admin.email.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -287,9 +399,17 @@ export default class EmailSettings extends React.Component {
onChange={this.handleChange.bind(this, 'requireEmailVerification_false')}
disabled={!this.state.sendEmailNotifications}
/>
{'false'}
<FormattedMessage
id='admin.email.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'Typically set to true in production. When true, Mattermost requires email verification after account creation prior to allowing login. Developers may set this field to false so skip sending verification emails for faster development.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.email.requireVerificationDescription'
defaultMessage='Typically set to true in production. When true, Mattermost requires email verification after account creation prior to allowing login. Developers may set this field to false so skip sending verification emails for faster development.'
/>
</p>
</div>
</div>
@@ -298,7 +418,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='feedbackName'
>
{'Notification Display Name:'}
<FormattedMessage
id='admin.email.notificationDisplayTitle'
defaultMessage='Notification Display Name:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -306,12 +429,17 @@ export default class EmailSettings extends React.Component {
className='form-control'
id='feedbackName'
ref='feedbackName'
placeholder='E.g.: "Mattermost Notification", "System", "No-Reply"'
placeholder={formatMessage(holders.notificationDisplayExample)}
defaultValue={this.props.config.EmailSettings.FeedbackName}
onChange={this.handleChange}
disabled={!this.state.sendEmailNotifications}
/>
<p className='help-text'>{'Display name on email account used when sending notification emails from Mattermost.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.email.notificationDisplayDescription'
defaultMessage='Display name on email account used when sending notification emails from Mattermost.'
/>
</p>
</div>
</div>
@@ -320,7 +448,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='feedbackEmail'
>
{'Notification Email Address:'}
<FormattedMessage
id='admin.email.notificationEmailTitle'
defaultMessage='Notification Email Address:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -328,12 +459,17 @@ export default class EmailSettings extends React.Component {
className='form-control'
id='feedbackEmail'
ref='feedbackEmail'
placeholder='E.g.: "mattermost@yourcompany.com", "admin@yourcompany.com"'
placeholder={formatMessage(holders.notificationEmailExample)}
defaultValue={this.props.config.EmailSettings.FeedbackEmail}
onChange={this.handleChange}
disabled={!this.state.sendEmailNotifications}
/>
<p className='help-text'>{'Email address displayed on email account used when sending notification emails from Mattermost.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.email.notificationEmailDescription'
defaultMessage='Email address displayed on email account used when sending notification emails from Mattermost.'
/>
</p>
</div>
</div>
@@ -342,7 +478,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='SMTPUsername'
>
{'SMTP Username:'}
<FormattedMessage
id='admin.email.smtpUsernameTitle'
defaultMessage='SMTP Username:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -350,12 +489,17 @@ export default class EmailSettings extends React.Component {
className='form-control'
id='SMTPUsername'
ref='SMTPUsername'
placeholder='E.g.: "admin@yourcompany.com", "AKIADTOVBGERKLCBV"'
placeholder={formatMessage(holders.smtpUsernameExample)}
defaultValue={this.props.config.EmailSettings.SMTPUsername}
onChange={this.handleChange}
disabled={!this.state.sendEmailNotifications}
/>
<p className='help-text'>{' Obtain this credential from administrator setting up your email server.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.email.smtpUsernameDescription'
defaultMessage=' Obtain this credential from administrator setting up your email server.'
/>
</p>
</div>
</div>
@@ -364,7 +508,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='SMTPPassword'
>
{'SMTP Password:'}
<FormattedMessage
id='admin.email.smtpPasswordTitle'
defaultMessage='SMTP Password:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -372,12 +519,17 @@ export default class EmailSettings extends React.Component {
className='form-control'
id='SMTPPassword'
ref='SMTPPassword'
placeholder='E.g.: "yourpassword", "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
placeholder={formatMessage(holders.smtpPasswordExample)}
defaultValue={this.props.config.EmailSettings.SMTPPassword}
onChange={this.handleChange}
disabled={!this.state.sendEmailNotifications}
/>
<p className='help-text'>{' Obtain this credential from administrator setting up your email server.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.email.smtpPasswordDescription'
defaultMessage=' Obtain this credential from administrator setting up your email server.'
/>
</p>
</div>
</div>
@@ -386,7 +538,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='SMTPServer'
>
{'SMTP Server:'}
<FormattedMessage
id='admin.email.smtpServerTitle'
defaultMessage='SMTP Server:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -394,12 +549,17 @@ export default class EmailSettings extends React.Component {
className='form-control'
id='SMTPServer'
ref='SMTPServer'
placeholder='E.g.: "smtp.yourcompany.com", "email-smtp.us-east-1.amazonaws.com"'
placeholder={formatMessage(holders.smtpServerExample)}
defaultValue={this.props.config.EmailSettings.SMTPServer}
onChange={this.handleChange}
disabled={!this.state.sendEmailNotifications}
/>
<p className='help-text'>{'Location of SMTP email server.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.email.smtpServerDescription'
defaultMessage='Location of SMTP email server.'
/>
</p>
</div>
</div>
@@ -408,7 +568,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='SMTPPort'
>
{'SMTP Port:'}
<FormattedMessage
id='admin.email.smtpPortTitle'
defaultMessage='SMTP Port:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -416,12 +579,17 @@ export default class EmailSettings extends React.Component {
className='form-control'
id='SMTPPort'
ref='SMTPPort'
placeholder='E.g.: "25", "465"'
placeholder={formatMessage(holders.smtpPortExample)}
defaultValue={this.props.config.EmailSettings.SMTPPort}
onChange={this.handleChange}
disabled={!this.state.sendEmailNotifications}
/>
<p className='help-text'>{'Port of SMTP email server.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.email.smtpPortDescription'
defaultMessage='Port of SMTP email server.'
/>
</p>
</div>
</div>
@@ -430,7 +598,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='ConnectionSecurity'
>
{'Connection Security:'}
<FormattedMessage
id='admin.email.connectionSecurityTitle'
defaultMessage='Connection Security:'
/>
</label>
<div className='col-sm-8'>
<select
@@ -441,9 +612,9 @@ export default class EmailSettings extends React.Component {
onChange={this.handleChange}
disabled={!this.state.sendEmailNotifications}
>
<option value=''>{'None'}</option>
<option value='TLS'>{'TLS (Recommended)'}</option>
<option value='STARTTLS'>{'STARTTLS'}</option>
<option value=''>{formatMessage(holders.connectionSecurityNone)}</option>
<option value='TLS'>{formatMessage(holders.connectionSecurityTls)}</option>
<option value='STARTTLS'>{formatMessage(holders.connectionSecurityStart)}</option>
</select>
<div className='help-text'>
<table
@@ -451,9 +622,29 @@ export default class EmailSettings extends React.Component {
cellPadding='5'
>
<tbody>
<tr><td className='help-text'>{'None'}</td><td className='help-text'>{'Mattermost will send email over an unsecure connection.'}</td></tr>
<tr><td className='help-text'>{'TLS'}</td><td className='help-text'>{'Encrypts the communication between Mattermost and your email server.'}</td></tr>
<tr><td className='help-text'>{'STARTTLS'}</td><td className='help-text'>{'Takes an existing insecure connection and attempts to upgrade it to a secure connection using TLS.'}</td></tr>
<tr><td className='help-text'>
<FormattedMessage
id='admin.email.connectionSecurityNone'
defaultMessage='None'
/>
</td><td className='help-text'>
<FormattedMessage
id='admin.email.connectionSecurityNoneDescription'
defaultMessage='Mattermost will send email over an unsecure connection.'
/>
</td></tr>
<tr><td className='help-text'>{'TLS'}</td><td className='help-text'>
<FormattedMessage
id='admin.email.connectionSecurityTlsDescription'
defaultMessage='Encrypts the communication between Mattermost and your email server.'
/>
</td></tr>
<tr><td className='help-text'>{'STARTTLS'}</td><td className='help-text'>
<FormattedMessage
id='admin.email.connectionSecurityStartDescription'
defaultMessage='Takes an existing insecure connection and attempts to upgrade it to a secure connection using TLS.'
/>
</td></tr>
</tbody>
</table>
</div>
@@ -463,9 +654,12 @@ export default class EmailSettings extends React.Component {
onClick={this.handleTestConnection}
disabled={!this.state.sendEmailNotifications}
id='connection-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Testing...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.testing)}
>
{'Test Connection'}
<FormattedMessage
id='admin.email.connectionSecurityTest'
defaultMessage='Test Connection'
/>
</button>
{emailSuccess}
{emailFail}
@@ -478,7 +672,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='InviteSalt'
>
{'Invite Salt:'}
<FormattedMessage
id='admin.email.inviteSaltTitle'
defaultMessage='Invite Salt:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -486,19 +683,27 @@ export default class EmailSettings extends React.Component {
className='form-control'
id='InviteSalt'
ref='InviteSalt'
placeholder='E.g.: "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo"'
placeholder={formatMessage(holders.inviteSaltExample)}
defaultValue={this.props.config.EmailSettings.InviteSalt}
onChange={this.handleChange}
disabled={!this.state.sendEmailNotifications}
/>
<p className='help-text'>{'32-character salt added to signing of email invites. Randomly generated on install. Click "Re-Generate" to create new salt.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.email.inviteSaltDescription'
defaultMessage='32-character salt added to signing of email invites. Randomly generated on install. Click "Re-Generate" to create new salt.'
/>
</p>
<div className='help-text'>
<button
className='btn btn-default'
onClick={this.handleGenerateInvite}
disabled={!this.state.sendEmailNotifications}
>
{'Re-Generate'}
<FormattedMessage
id='admin.email.regenerate'
defaultMessage='Re-Generate'
/>
</button>
</div>
</div>
@@ -509,7 +714,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='PasswordResetSalt'
>
{'Password Reset Salt:'}
<FormattedMessage
id='admin.email.passwordSaltTitle'
defaultMessage='Password Reset Salt:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -517,19 +725,27 @@ export default class EmailSettings extends React.Component {
className='form-control'
id='PasswordResetSalt'
ref='PasswordResetSalt'
placeholder='E.g.: "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo"'
placeholder={formatMessage(holders.passwordSaltExample)}
defaultValue={this.props.config.EmailSettings.PasswordResetSalt}
onChange={this.handleChange}
disabled={!this.state.sendEmailNotifications}
/>
<p className='help-text'>{'32-character salt added to signing of password reset emails. Randomly generated on install. Click "Re-Generate" to create new salt.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.email.passwordSaltDescription'
defaultMessage='32-character salt added to signing of password reset emails. Randomly generated on install. Click "Re-Generate" to create new salt.'
/>
</p>
<div className='help-text'>
<button
className='btn btn-default'
onClick={this.handleGenerateReset}
disabled={!this.state.sendEmailNotifications}
>
{'Re-Generate'}
<FormattedMessage
id='admin.email.regenerate'
defaultMessage='Re-Generate'
/>
</button>
</div>
</div>
@@ -540,7 +756,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='sendPushNotifications'
>
{'Send Push Notifications: '}
<FormattedMessage
id='admin.email.pushTitle'
defaultMessage='Send Push Notifications: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -552,7 +771,10 @@ export default class EmailSettings extends React.Component {
defaultChecked={this.props.config.EmailSettings.SendPushNotifications}
onChange={this.handleChange.bind(this, 'sendPushNotifications_true')}
/>
{'true'}
<FormattedMessage
id='admin.email.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -562,9 +784,17 @@ export default class EmailSettings extends React.Component {
defaultChecked={!this.props.config.EmailSettings.SendPushNotifications}
onChange={this.handleChange.bind(this, 'sendPushNotifications_false')}
/>
{'false'}
<FormattedMessage
id='admin.email.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'Typically set to true in production. When true, Mattermost attempts to send iOS and Android push notifications through the push notification server.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.email.pushDesc'
defaultMessage='Typically set to true in production. When true, Mattermost attempts to send iOS and Android push notifications through the push notification server.'
/>
</p>
</div>
</div>
@@ -573,7 +803,10 @@ export default class EmailSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='PushNotificationServer'
>
{'Push Notification Server:'}
<FormattedMessage
id='admin.email.pushServerTitle'
defaultMessage='Push Notification Server:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -581,12 +814,17 @@ export default class EmailSettings extends React.Component {
className='form-control'
id='PushNotificationServer'
ref='PushNotificationServer'
placeholder='E.g.: "https://push-test.mattermost.com"'
placeholder={formatMessage(holders.pushServerEx)}
defaultValue={this.props.config.EmailSettings.PushNotificationServer}
onChange={this.handleChange}
disabled={!this.state.sendPushNotifications}
/>
<p className='help-text'>{'Location of Mattermost push notification service you can set up behind your firewall using https://github.com/mattermost/push-proxy. For testing you can use https://push-test.mattermost.com, which connects to the sample Mattermost iOS app in the public Apple AppStore. Please do not use test service for production deployments.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.email.pushServerDesc'
defaultMessage='Location of Mattermost push notification service you can set up behind your firewall using https://github.com/mattermost/push-proxy. For testing you can use https://push-test.mattermost.com, which connects to the sample Mattermost iOS app in the public Apple AppStore. Please do not use test service for production deployments.'
/>
</p>
</div>
</div>
@@ -599,9 +837,12 @@ export default class EmailSettings extends React.Component {
className={saveClass}
onClick={this.handleSubmit}
id='save-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Saving Config...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
>
{'Save'}
<FormattedMessage
id='admin.email.save'
defaultMessage='Save'
/>
</button>
</div>
</div>
@@ -613,5 +854,8 @@ export default class EmailSettings extends React.Component {
}
EmailSettings.propTypes = {
intl: intlShape.isRequired,
config: React.PropTypes.object
};
export default injectIntl(EmailSettings);

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

@@ -4,7 +4,36 @@
import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
export default class GitLabSettings extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const holders = defineMessages({
clientIdExample: {
id: 'admin.gitlab.clientIdExample',
defaultMessage: 'Ex "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
},
clientSecretExample: {
id: 'admin.gitlab.clientSecretExample',
defaultMessage: 'Ex "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
},
authExample: {
id: 'admin.gitlab.authExample',
defaultMessage: 'Ex ""'
},
tokenExample: {
id: 'admin.gitlab.tokenExample',
defaultMessage: 'Ex ""'
},
userExample: {
id: 'admin.gitlab.userExample',
defaultMessage: 'Ex ""'
},
saving: {
id: 'admin.gitlab.saving',
defaultMessage: 'Saving Config...'
}
});
class GitLabSettings extends React.Component {
constructor(props) {
super(props);
@@ -65,6 +94,7 @@ export default class GitLabSettings extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
var serverError = '';
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
@@ -78,7 +108,12 @@ export default class GitLabSettings extends React.Component {
return (
<div className='wrapper--fixed'>
<h3>{'GitLab Settings'}</h3>
<h3>
<FormattedMessage
id='admin.gitlab.settingsTitle'
defaultMessage='GitLab Settings'
/>
</h3>
<form
className='form-horizontal'
role='form'
@@ -89,7 +124,10 @@ export default class GitLabSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='Enable'
>
{'Enable Sign Up With GitLab: '}
<FormattedMessage
id='admin.gitlab.enableTitle'
defaultMessage='Enable Sign Up With GitLab: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -101,7 +139,10 @@ export default class GitLabSettings extends React.Component {
defaultChecked={this.props.config.GitLabSettings.Enable}
onChange={this.handleChange.bind(this, 'EnableTrue')}
/>
{'true'}
<FormattedMessage
id='admin.gitlab.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -111,18 +152,23 @@ export default class GitLabSettings extends React.Component {
defaultChecked={!this.props.config.GitLabSettings.Enable}
onChange={this.handleChange.bind(this, 'EnableFalse')}
/>
{'false'}
<FormattedMessage
id='admin.gitlab.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>
{'When true, Mattermost allows team creation and account signup using GitLab OAuth.'} <br/>
<FormattedMessage
id='admin.gitlab.enableDescription'
defaultMessage='When true, Mattermost allows team creation and account signup using GitLab OAuth.'
/>
<br/>
</p>
<div className='help-text'>
<ol>
<li>{'Log in to your GitLab account and go to Applications -> Profile Settings.'}</li>
<li>{'Enter Redirect URIs "<your-mattermost-url>/login/gitlab/complete" (example: http://localhost:8065/login/gitlab/complete) and "<your-mattermost-url>/signup/gitlab/complete". '}</li>
<li>{'Then use "Secret" and "Id" fields from GitLab to complete the options below.'}</li>
<li>{'Complete the Endpoint URLs below. '}</li>
</ol>
<FormattedHTMLMessage
id='admin.gitlab.EnableHtmlDesc'
defaultMessage='<ol><li>Log in to your GitLab account and go to Applications -> Profile Settings.</li><li>Enter Redirect URIs "<your-mattermost-url>/login/gitlab/complete" (example: http://localhost:8065/login/gitlab/complete) and "<your-mattermost-url>/signup/gitlab/complete". </li><li>Then use "Secret" and "Id" fields from GitLab to complete the options below.</li><li>Complete the Endpoint URLs below. </li></ol>'
/>
</div>
</div>
</div>
@@ -132,7 +178,10 @@ export default class GitLabSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='Id'
>
{'Id:'}
<FormattedMessage
id='admin.gitlab.clientIdTitle'
defaultMessage='Id:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -140,12 +189,17 @@ export default class GitLabSettings extends React.Component {
className='form-control'
id='Id'
ref='Id'
placeholder='Ex "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
placeholder={formatMessage(holders.clientIdExample)}
defaultValue={this.props.config.GitLabSettings.Id}
onChange={this.handleChange}
disabled={!this.state.Enable}
/>
<p className='help-text'>{'Obtain this value via the instructions above for logging into GitLab'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.gitlab.clientIdDescription'
defaultMessage='Obtain this value via the instructions above for logging into GitLab'
/>
</p>
</div>
</div>
@@ -154,7 +208,10 @@ export default class GitLabSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='Secret'
>
{'Secret:'}
<FormattedMessage
id='admin.gitlab.clientSecretTitle'
defaultMessage='Secret:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -162,12 +219,17 @@ export default class GitLabSettings extends React.Component {
className='form-control'
id='Secret'
ref='Secret'
placeholder='Ex "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
placeholder={formatMessage(holders.clientSecretExample)}
defaultValue={this.props.config.GitLabSettings.Secret}
onChange={this.handleChange}
disabled={!this.state.Enable}
/>
<p className='help-text'>{'Obtain this value via the instructions above for logging into GitLab.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.gitab.clientSecretDescription'
defaultMessage='Obtain this value via the instructions above for logging into GitLab.'
/>
</p>
</div>
</div>
@@ -176,7 +238,10 @@ export default class GitLabSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='AuthEndpoint'
>
{'Auth Endpoint:'}
<FormattedMessage
id='admin.gitlab.authTitle'
defaultMessage='Auth Endpoint:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -184,12 +249,17 @@ export default class GitLabSettings extends React.Component {
className='form-control'
id='AuthEndpoint'
ref='AuthEndpoint'
placeholder='Ex ""'
placeholder={formatMessage(holders.authExample)}
defaultValue={this.props.config.GitLabSettings.AuthEndpoint}
onChange={this.handleChange}
disabled={!this.state.Enable}
/>
<p className='help-text'>{'Enter https://<your-gitlab-url>/oauth/authorize (example https://example.com:3000/oauth/authorize). Make sure you use HTTP or HTTPS in your URL depending on your server configuration.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.gitlab.authDescription'
defaultMessage='Enter https://<your-gitlab-url>/oauth/authorize (example https://example.com:3000/oauth/authorize). Make sure you use HTTP or HTTPS in your URL depending on your server configuration.'
/>
</p>
</div>
</div>
@@ -198,7 +268,10 @@ export default class GitLabSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='TokenEndpoint'
>
{'Token Endpoint:'}
<FormattedMessage
id='admin.gitlab.tokenTitle'
defaultMessage='Token Endpoint:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -206,12 +279,17 @@ export default class GitLabSettings extends React.Component {
className='form-control'
id='TokenEndpoint'
ref='TokenEndpoint'
placeholder='Ex ""'
placeholder={formatMessage(holders.tokenExample)}
defaultValue={this.props.config.GitLabSettings.TokenEndpoint}
onChange={this.handleChange}
disabled={!this.state.Enable}
/>
<p className='help-text'>{'Enter https://<your-gitlab-url>/oauth/token. Make sure you use HTTP or HTTPS in your URL depending on your server configuration.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.gitlab.tokenDescription'
defaultMessage='Enter https://<your-gitlab-url>/oauth/token. Make sure you use HTTP or HTTPS in your URL depending on your server configuration.'
/>
</p>
</div>
</div>
@@ -220,7 +298,10 @@ export default class GitLabSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='UserApiEndpoint'
>
{'User API Endpoint:'}
<FormattedMessage
id='admin.gitlab.userTitle'
defaultMessage='User API Endpoint:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -228,12 +309,17 @@ export default class GitLabSettings extends React.Component {
className='form-control'
id='UserApiEndpoint'
ref='UserApiEndpoint'
placeholder='Ex ""'
placeholder={formatMessage(holders.userExample)}
defaultValue={this.props.config.GitLabSettings.UserApiEndpoint}
onChange={this.handleChange}
disabled={!this.state.Enable}
/>
<p className='help-text'>{'Enter https://<your-gitlab-url>/api/v3/user. Make sure you use HTTP or HTTPS in your URL depending on your server configuration.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.gitlab.userDescription'
defaultMessage='Enter https://<your-gitlab-url>/api/v3/user. Make sure you use HTTP or HTTPS in your URL depending on your server configuration.'
/>
</p>
</div>
</div>
@@ -246,9 +332,12 @@ export default class GitLabSettings extends React.Component {
className={saveClass}
onClick={this.handleSubmit}
id='save-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Saving Config...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
>
{'Save'}
<FormattedMessage
id='admin.gitlab.save'
defaultMessage='Save'
/>
</button>
</div>
</div>
@@ -283,5 +372,8 @@ export default class GitLabSettings extends React.Component {
// </div>
GitLabSettings.propTypes = {
intl: intlShape.isRequired,
config: React.PropTypes.object
};
export default injectIntl(GitLabSettings);

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

@@ -5,7 +5,76 @@ import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import crypto from 'crypto';
export default class FileSettings extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
storeDisabled: {
id: 'admin.image.storeDisabled',
defaultMessage: 'Disable File Storage'
},
storeLocal: {
id: 'admin.image.storeLocal',
defaultMessage: 'Local File System'
},
storeAmazonS3: {
id: 'admin.image.storeAmazonS3',
defaultMessage: 'Amazon S3'
},
localExample: {
id: 'admin.image.localExample',
defaultMessage: 'Ex "./data/"'
},
amazonS3IdExample: {
id: 'admin.image.amazonS3IdExample',
defaultMessage: 'Ex "AKIADTOVBGERKLCBV"'
},
amazonS3SecretExample: {
id: 'admin.image.amazonS3SecretExample',
defaultMessage: 'Ex "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
},
amazonS3BucketExample: {
id: 'admin.image.amazonS3BucketExample',
defaultMessage: 'Ex "mattermost-media"'
},
amazonS3RegionExample: {
id: 'admin.image.amazonS3RegionExample',
defaultMessage: 'Ex "us-east-1"'
},
thumbWidthExample: {
id: 'admin.image.thumbWidthExample',
defaultMessage: 'Ex "120"'
},
thumbHeightExample: {
id: 'admin.image.thumbHeightExample',
defaultMessage: 'Ex "100"'
},
previewWidthExample: {
id: 'admin.image.previewWidthExample',
defaultMessage: 'Ex "1024"'
},
previewHeightExample: {
id: 'admin.image.previewHeightExample',
defaultMessage: 'Ex "0"'
},
profileWidthExample: {
id: 'admin.image.profileWidthExample',
defaultMessage: 'Ex "1024"'
},
profileHeightExample: {
id: 'admin.image.profileHeightExample',
defaultMessage: 'Ex "0"'
},
publicLinkExample: {
id: 'admin.image.publicLinkExample',
defaultMessage: 'Ex "gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6"'
},
saving: {
id: 'admin.image.saving',
defaultMessage: 'Saving Config...'
}
});
class FileSettings extends React.Component {
constructor(props) {
super(props);
@@ -120,6 +189,7 @@ export default class FileSettings extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
var serverError = '';
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
@@ -143,7 +213,12 @@ export default class FileSettings extends React.Component {
return (
<div className='wrapper--fixed'>
<h3>{'File Settings'}</h3>
<h3>
<FormattedMessage
id='admin.image.fileSettings'
defaultMessage='File Settings'
/>
</h3>
<form
className='form-horizontal'
role='form'
@@ -154,7 +229,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='DriverName'
>
{'Store Files In:'}
<FormattedMessage
id='admin.image.storeTitle'
defaultMessage='Store Files In:'
/>
</label>
<div className='col-sm-8'>
<select
@@ -164,9 +242,9 @@ export default class FileSettings extends React.Component {
defaultValue={this.props.config.FileSettings.DriverName}
onChange={this.handleChange.bind(this, 'DriverName')}
>
<option value=''>{'Disable File Storage'}</option>
<option value='local'>{'Local File System'}</option>
<option value='amazons3'>{'Amazon S3'}</option>
<option value=''>{formatMessage(holders.storeDisabled)}</option>
<option value='local'>{formatMessage(holders.storeLocal)}</option>
<option value='amazons3'>{formatMessage(holders.storeAmazonS3)}</option>
</select>
</div>
</div>
@@ -176,7 +254,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='Directory'
>
{'Local Directory Location:'}
<FormattedMessage
id='admin.image.localTitle'
defaultMessage='Local Directory Location:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -184,12 +265,17 @@ export default class FileSettings extends React.Component {
className='form-control'
id='Directory'
ref='Directory'
placeholder='Ex "./data/"'
placeholder={formatMessage(holders.localExample)}
defaultValue={this.props.config.FileSettings.Directory}
onChange={this.handleChange}
disabled={!enableFile}
/>
<p className='help-text'>{'Directory to which image files are written. If blank, will be set to ./data/.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.image.localDescription'
defaultMessage='Directory to which image files are written. If blank, will be set to ./data/.'
/>
</p>
</div>
</div>
@@ -198,7 +284,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='AmazonS3AccessKeyId'
>
{'Amazon S3 Access Key Id:'}
<FormattedMessage
id='admin.image.amazonS3IdTitle'
defaultMessage='Amazon S3 Access Key Id:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -206,12 +295,17 @@ export default class FileSettings extends React.Component {
className='form-control'
id='AmazonS3AccessKeyId'
ref='AmazonS3AccessKeyId'
placeholder='Ex "AKIADTOVBGERKLCBV"'
placeholder={formatMessage(holders.amazonS3IdExample)}
defaultValue={this.props.config.FileSettings.AmazonS3AccessKeyId}
onChange={this.handleChange}
disabled={!enableS3}
/>
<p className='help-text'>{'Obtain this credential from your Amazon EC2 administrator.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.image.amazonS3IdDescription'
defaultMessage='Obtain this credential from your Amazon EC2 administrator.'
/>
</p>
</div>
</div>
@@ -220,7 +314,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='AmazonS3SecretAccessKey'
>
{'Amazon S3 Secret Access Key:'}
<FormattedMessage
id='admin.image.amazonS3SecretTitle'
defaultMessage='Amazon S3 Secret Access Key:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -228,12 +325,17 @@ export default class FileSettings extends React.Component {
className='form-control'
id='AmazonS3SecretAccessKey'
ref='AmazonS3SecretAccessKey'
placeholder='Ex "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
placeholder={formatMessage(holders.amazonS3SecretExample)}
defaultValue={this.props.config.FileSettings.AmazonS3SecretAccessKey}
onChange={this.handleChange}
disabled={!enableS3}
/>
<p className='help-text'>{'Obtain this credential from your Amazon EC2 administrator.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.image.amazonS3SecretDescription'
defaultMessage='Obtain this credential from your Amazon EC2 administrator.'
/>
</p>
</div>
</div>
@@ -242,7 +344,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='AmazonS3Bucket'
>
{'Amazon S3 Bucket:'}
<FormattedMessage
id='admin.image.amazonS3BucketTitle'
defaultMessage='Amazon S3 Bucket:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -250,12 +355,17 @@ export default class FileSettings extends React.Component {
className='form-control'
id='AmazonS3Bucket'
ref='AmazonS3Bucket'
placeholder='Ex "mattermost-media"'
placeholder={formatMessage(holders.amazonS3BucketExample)}
defaultValue={this.props.config.FileSettings.AmazonS3Bucket}
onChange={this.handleChange}
disabled={!enableS3}
/>
<p className='help-text'>{'Name you selected for your S3 bucket in AWS.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.image.amazonS3BucketDescription'
defaultMessage='Name you selected for your S3 bucket in AWS.'
/>
</p>
</div>
</div>
@@ -264,7 +374,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='AmazonS3Region'
>
{'Amazon S3 Region:'}
<FormattedMessage
id='admin.image.amazonS3RegionTitle'
defaultMessage='Amazon S3 Region:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -272,12 +385,17 @@ export default class FileSettings extends React.Component {
className='form-control'
id='AmazonS3Region'
ref='AmazonS3Region'
placeholder='Ex "us-east-1"'
placeholder={formatMessage(holders.amazonS3RegionExample)}
defaultValue={this.props.config.FileSettings.AmazonS3Region}
onChange={this.handleChange}
disabled={!enableS3}
/>
<p className='help-text'>{'AWS region you selected for creating your S3 bucket.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.image.amazonS3RegionDescription'
defaultMessage='AWS region you selected for creating your S3 bucket.'
/>
</p>
</div>
</div>
@@ -286,7 +404,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='ThumbnailWidth'
>
{'Thumbnail Width:'}
<FormattedMessage
id='admin.image.thumbWidthTitle'
defaultMessage='Thumbnail Width:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -294,11 +415,16 @@ export default class FileSettings extends React.Component {
className='form-control'
id='ThumbnailWidth'
ref='ThumbnailWidth'
placeholder='Ex "120"'
placeholder={formatMessage(holders.thumbWidthExample)}
defaultValue={this.props.config.FileSettings.ThumbnailWidth}
onChange={this.handleChange}
/>
<p className='help-text'>{'Width of thumbnails generated from uploaded images. Updating this value changes how thumbnail images render in future, but does not change images created in the past.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.image.thumbWidthDescription'
defaultMessage='Width of thumbnails generated from uploaded images. Updating this value changes how thumbnail images render in future, but does not change images created in the past.'
/>
</p>
</div>
</div>
@@ -307,7 +433,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='ThumbnailHeight'
>
{'Thumbnail Height:'}
<FormattedMessage
id='admin.image.thumbHeightTitle'
defaultMessage='Thumbnail Height:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -315,11 +444,16 @@ export default class FileSettings extends React.Component {
className='form-control'
id='ThumbnailHeight'
ref='ThumbnailHeight'
placeholder='Ex "100"'
placeholder={formatMessage(holders.thumbHeightExample)}
defaultValue={this.props.config.FileSettings.ThumbnailHeight}
onChange={this.handleChange}
/>
<p className='help-text'>{'Height of thumbnails generated from uploaded images. Updating this value changes how thumbnail images render in future, but does not change images created in the past.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.image.thumbHeightDescription'
defaultMessage='Height of thumbnails generated from uploaded images. Updating this value changes how thumbnail images render in future, but does not change images created in the past.'
/>
</p>
</div>
</div>
@@ -328,7 +462,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='PreviewWidth'
>
{'Preview Width:'}
<FormattedMessage
id='admin.image.previewWidthTitle'
defaultMessage='Preview Width:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -336,11 +473,16 @@ export default class FileSettings extends React.Component {
className='form-control'
id='PreviewWidth'
ref='PreviewWidth'
placeholder='Ex "1024"'
placeholder={formatMessage(holders.previewWidthExample)}
defaultValue={this.props.config.FileSettings.PreviewWidth}
onChange={this.handleChange}
/>
<p className='help-text'>{'Maximum width of preview image. Updating this value changes how preview images render in future, but does not change images created in the past.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.image.previewWidthDescription'
defaultMessage='Maximum width of preview image. Updating this value changes how preview images render in future, but does not change images created in the past.'
/>
</p>
</div>
</div>
@@ -349,7 +491,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='PreviewHeight'
>
{'Preview Height:'}
<FormattedMessage
id='admin.image.previewHeightTitle'
defaultMessage='Preview Height:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -357,11 +502,16 @@ export default class FileSettings extends React.Component {
className='form-control'
id='PreviewHeight'
ref='PreviewHeight'
placeholder='Ex "0"'
placeholder={formatMessage(holders.previewHeightExample)}
defaultValue={this.props.config.FileSettings.PreviewHeight}
onChange={this.handleChange}
/>
<p className='help-text'>{'Maximum height of preview image ("0": Sets to auto-size). Updating this value changes how preview images render in future, but does not change images created in the past.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.image.previewHeightDescription'
defaultMessage='Maximum height of preview image ("0": Sets to auto-size). Updating this value changes how preview images render in future, but does not change images created in the past.'
/>
</p>
</div>
</div>
@@ -370,7 +520,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='ProfileWidth'
>
{'Profile Width:'}
<FormattedMessage
id='admin.image.profileWidthTitle'
defaultMessage='Profile Width:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -378,11 +531,16 @@ export default class FileSettings extends React.Component {
className='form-control'
id='ProfileWidth'
ref='ProfileWidth'
placeholder='Ex "1024"'
placeholder={formatMessage(holders.profileWidthExample)}
defaultValue={this.props.config.FileSettings.ProfileWidth}
onChange={this.handleChange}
/>
<p className='help-text'>{'Width of profile picture.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.image.profileWidthDescription'
defaultMessage='Width of profile picture.'
/>
</p>
</div>
</div>
@@ -391,7 +549,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='ProfileHeight'
>
{'Profile Height:'}
<FormattedMessage
id='admin.image.profileHeightTitle'
defaultMessage='Profile Height:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -399,11 +560,16 @@ export default class FileSettings extends React.Component {
className='form-control'
id='ProfileHeight'
ref='ProfileHeight'
placeholder='Ex "0"'
placeholder={formatMessage(holders.profileHeightExample)}
defaultValue={this.props.config.FileSettings.ProfileHeight}
onChange={this.handleChange}
/>
<p className='help-text'>{'Height of profile picture.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.image.profileHeightDescription'
defaultMessage='Height of profile picture.'
/>
</p>
</div>
</div>
@@ -412,7 +578,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='EnablePublicLink'
>
{'Share Public File Link: '}
<FormattedMessage
id='admin.image.shareTitle'
defaultMessage='Share Public File Link: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -424,7 +593,10 @@ export default class FileSettings extends React.Component {
defaultChecked={this.props.config.FileSettings.EnablePublicLink}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.image.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -434,9 +606,17 @@ export default class FileSettings extends React.Component {
defaultChecked={!this.props.config.FileSettings.EnablePublicLink}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.image.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'Allow users to share public links to files and images.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.image.shareDescription'
defaultMessage='Allow users to share public links to files and images.'
/>
</p>
</div>
</div>
@@ -445,7 +625,10 @@ export default class FileSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='PublicLinkSalt'
>
{'Public Link Salt:'}
<FormattedMessage
id='admin.image.publicLinkTitle'
defaultMessage='Public Link Salt:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -453,17 +636,25 @@ export default class FileSettings extends React.Component {
className='form-control'
id='PublicLinkSalt'
ref='PublicLinkSalt'
placeholder='Ex "gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6"'
placeholder={formatMessage(holders.publicLinkExample)}
defaultValue={this.props.config.FileSettings.PublicLinkSalt}
onChange={this.handleChange}
/>
<p className='help-text'>{'32-character salt added to signing of public image links. Randomly generated on install. Click "Re-Generate" to create new salt.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.image.publicLinkDescription'
defaultMessage='32-character salt added to signing of public image links. Randomly generated on install. Click "Re-Generate" to create new salt.'
/>
</p>
<div className='help-text'>
<button
className='btn btn-default'
onClick={this.handleGenerate}
>
{'Re-Generate'}
<FormattedMessage
id='admin.image.regenerate'
defaultMessage='Re-Generate'
/>
</button>
</div>
</div>
@@ -478,9 +669,12 @@ export default class FileSettings extends React.Component {
className={saveClass}
onClick={this.handleSubmit}
id='save-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Saving Config...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
>
{'Save'}
<FormattedMessage
id='admin.image.save'
defaultMessage='Save'
/>
</button>
</div>
</div>
@@ -492,5 +686,8 @@ export default class FileSettings extends React.Component {
}
FileSettings.propTypes = {
intl: intlShape.isRequired,
config: React.PropTypes.object
};
export default injectIntl(FileSettings);

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

@@ -4,10 +4,55 @@
import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const DEFAULT_LDAP_PORT = 389;
const DEFAULT_QUERY_TIMEOUT = 60;
export default class LdapSettings extends React.Component {
var holders = defineMessages({
serverEx: {
id: 'admin.ldap.serverEx',
defaultMessage: 'Ex "10.0.0.23"'
},
portEx: {
id: 'admin.ldap.portEx',
defaultMessage: 'Ex "389"'
},
baseEx: {
id: 'admin.ldap.baseEx',
defaultMessage: 'Ex "dc=mydomain,dc=com"'
},
firstnameAttrEx: {
id: 'admin.ldap.firstnameAttrEx',
defaultMessage: 'Ex "givenName"'
},
lastnameAttrEx: {
id: 'admin.ldap.lastnameAttrEx',
defaultMessage: 'Ex "sn"'
},
emailAttrEx: {
id: 'admin.ldap.emailAttrEx',
defaultMessage: 'Ex "mail"'
},
usernameAttrEx: {
id: 'admin.ldap.usernameAttrEx',
defaultMessage: 'Ex "sAMAccountName"'
},
idAttrEx: {
id: 'admin.ldap.idAttrEx',
defaultMessage: 'Ex "sAMAccountName"'
},
queryEx: {
id: 'admin.ldap.queryEx',
defaultMessage: 'Ex "60"'
},
saving: {
id: 'admin.ldap.saving',
defaultMessage: 'Saving Config...'
}
});
class LdapSettings extends React.Component {
constructor(props) {
super(props);
@@ -80,6 +125,7 @@ export default class LdapSettings extends React.Component {
);
}
render() {
const {formatMessage} = this.props.intl;
let serverError = '';
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
@@ -97,8 +143,18 @@ export default class LdapSettings extends React.Component {
bannerContent = (
<div className='banner'>
<div className='banner__content'>
<h4 className='banner__heading'>{'Note:'}</h4>
<p>{'If a user attribute changes on the LDAP server it will be updated the next time the user enters their credentials to log in to Mattermost. This includes if a user is made inactive or removed from an LDAP server. Synchronization with LDAP servers is planned in a future release.'}</p>
<h4 className='banner__heading'>
<FormattedMessage
id='admin.ldap.bannerHeading'
defaultMessage='Note:'
/>
</h4>
<p>
<FormattedMessage
id='admin.ldap.bannerDesc'
defaultMessage='If a user attribute changes on the LDAP server it will be updated the next time the user enters their credentials to log in to Mattermost. This includes if a user is made inactive or removed from an LDAP server. Synchronization with LDAP servers is planned in a future release.'
/>
</p>
</div>
</div>
);
@@ -106,17 +162,10 @@ export default class LdapSettings extends React.Component {
bannerContent = (
<div className='banner warning'>
<div className='banner__content'>
<h4 className='banner__heading'>{'Note:'}</h4>
<p>
{'LDAP is an enterprise feature. Your current license does not support LDAP. Click '}
<a
href='http://mattermost.com'
target='_blank'
>
{'here'}
</a>
{' for information and pricing on enterprise licenses.'}
</p>
<FormattedHTMLMessage
id='admin.ldap.noLicense'
defaultMessage='<h4 className="banner__heading">Note:</h4><p>LDAP is an enterprise feature. Your current license does not support LDAP. Click <a href="http://mattermost.com"target="_blank">here</a> for information and pricing on enterprise licenses.</p>'
/>
</div>
</div>
);
@@ -125,7 +174,12 @@ export default class LdapSettings extends React.Component {
return (
<div className='wrapper--fixed'>
{bannerContent}
<h3>{'LDAP Settings'}</h3>
<h3>
<FormattedMessage
id='admin.ldap.title'
defaultMessage='LDAP Settings'
/>
</h3>
<form
className='form-horizontal'
role='form'
@@ -135,7 +189,10 @@ export default class LdapSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='Enable'
>
{'Enable Login With LDAP:'}
<FormattedMessage
id='admin.ldap.enableTitle'
defaultMessage='Enable Login With LDAP:'
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -148,7 +205,10 @@ export default class LdapSettings extends React.Component {
onChange={this.handleEnable}
disabled={!licenseEnabled}
/>
{'true'}
<FormattedMessage
id='admin.ldap.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -158,9 +218,17 @@ export default class LdapSettings extends React.Component {
defaultChecked={!this.props.config.LdapSettings.Enable}
onChange={this.handleDisable}
/>
{'false'}
<FormattedMessage
id='admin.ldap.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When true, Mattermost allows login using LDAP'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.ldap.enableDesc'
defaultMessage='When true, Mattermost allows login using LDAP'
/>
</p>
</div>
</div>
<div className='form-group'>
@@ -168,7 +236,10 @@ export default class LdapSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='LdapServer'
>
{'LDAP Server:'}
<FormattedMessage
id='admin.ldap.serverTitle'
defaultMessage='LDAP Server:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -176,12 +247,17 @@ export default class LdapSettings extends React.Component {
className='form-control'
id='LdapServer'
ref='LdapServer'
placeholder='Ex "10.0.0.23"'
placeholder={formatMessage(holders.serverEx)}
defaultValue={this.props.config.LdapSettings.LdapServer}
onChange={this.handleChange}
disabled={!this.state.enable}
/>
<p className='help-text'>{'The domain or IP address of LDAP server.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.ldap.serverDesc'
defaultMessage='The domain or IP address of LDAP server.'
/>
</p>
</div>
</div>
<div className='form-group'>
@@ -189,7 +265,10 @@ export default class LdapSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='LdapPort'
>
{'LDAP Port:'}
<FormattedMessage
id='admin.ldap.portTitle'
defaultMessage='LDAP Port:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -197,12 +276,17 @@ export default class LdapSettings extends React.Component {
className='form-control'
id='LdapPort'
ref='LdapPort'
placeholder='Ex "389"'
placeholder={formatMessage(holders.portEx)}
defaultValue={this.props.config.LdapSettings.LdapPort}
onChange={this.handleChange}
disabled={!this.state.enable}
/>
<p className='help-text'>{'The port Mattermost will use to connect to the LDAP server. Default is 389.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.ldap.portDesc'
defaultMessage='The port Mattermost will use to connect to the LDAP server. Default is 389.'
/>
</p>
</div>
</div>
<div className='form-group'>
@@ -210,7 +294,10 @@ export default class LdapSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='BaseDN'
>
{'BaseDN:'}
<FormattedMessage
id='admin.ldap.baseTitle'
defaultMessage='BaseDN:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -218,12 +305,17 @@ export default class LdapSettings extends React.Component {
className='form-control'
id='BaseDN'
ref='BaseDN'
placeholder='Ex "dc=mydomain,dc=com"'
placeholder={formatMessage(holders.baseEx)}
defaultValue={this.props.config.LdapSettings.BaseDN}
onChange={this.handleChange}
disabled={!this.state.enable}
/>
<p className='help-text'>{'The Base DN is the Distinguished Name of the location where Mattermost should start its search for users in the LDAP tree.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.ldap.baseDesc'
defaultMessage='The Base DN is the Distinguished Name of the location where Mattermost should start its search for users in the LDAP tree.'
/>
</p>
</div>
</div>
<div className='form-group'>
@@ -231,7 +323,10 @@ export default class LdapSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='BindUsername'
>
{'Bind Username:'}
<FormattedMessage
id='admin.ldap.bindUserTitle'
defaultMessage='Bind Username:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -244,7 +339,12 @@ export default class LdapSettings extends React.Component {
onChange={this.handleChange}
disabled={!this.state.enable}
/>
<p className='help-text'>{'The username used to perform the LDAP search. This should typically be an account created specifically for use with Mattermost. It should have access limited to read the portion of the LDAP tree specified in the BaseDN field.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.ldap.bindUserDesc'
defaultMessage='The username used to perform the LDAP search. This should typically be an account created specifically for use with Mattermost. It should have access limited to read the portion of the LDAP tree specified in the BaseDN field.'
/>
</p>
</div>
</div>
<div className='form-group'>
@@ -252,7 +352,10 @@ export default class LdapSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='BindPassword'
>
{'Bind Password:'}
<FormattedMessage
id='admin.ldap.bindPwdTitle'
defaultMessage='Bind Password:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -265,7 +368,12 @@ export default class LdapSettings extends React.Component {
onChange={this.handleChange}
disabled={!this.state.enable}
/>
<p className='help-text'>{'Password of the user given in "Bind Username".'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.ldap.bindPwdDesc'
defaultMessage='Password of the user given in "Bind Username".'
/>
</p>
</div>
</div>
<div className='form-group'>
@@ -273,7 +381,10 @@ export default class LdapSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='FirstNameAttribute'
>
{'First Name Attrubute'}
<FormattedMessage
id='admin.ldap.firstnameAttrTitle'
defaultMessage='First Name Attrubute'
/>
</label>
<div className='col-sm-8'>
<input
@@ -281,12 +392,17 @@ export default class LdapSettings extends React.Component {
className='form-control'
id='FirstNameAttribute'
ref='FirstNameAttribute'
placeholder='Ex "givenName"'
placeholder={formatMessage(holders.firstnameAttrEx)}
defaultValue={this.props.config.LdapSettings.FirstNameAttribute}
onChange={this.handleChange}
disabled={!this.state.enable}
/>
<p className='help-text'>{'The attribute in the LDAP server that will be used to populate the first name of users in Mattermost.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.ldap.firstnameAttrDesc'
defaultMessage='The attribute in the LDAP server that will be used to populate the first name of users in Mattermost.'
/>
</p>
</div>
</div>
<div className='form-group'>
@@ -294,7 +410,10 @@ export default class LdapSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='LastNameAttribute'
>
{'Last Name Attribute:'}
<FormattedMessage
id='admin.ldap.lastnameAttrTitle'
defaultMessage='Last Name Attribute:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -302,12 +421,17 @@ export default class LdapSettings extends React.Component {
className='form-control'
id='LastNameAttribute'
ref='LastNameAttribute'
placeholder='Ex "sn"'
placeholder={formatMessage(holders.lastnameAttrEx)}
defaultValue={this.props.config.LdapSettings.LastNameAttribute}
onChange={this.handleChange}
disabled={!this.state.enable}
/>
<p className='help-text'>{'The attribute in the LDAP server that will be used to populate the last name of users in Mattermost.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.ldap.lastnameAttrDesc'
defaultMessage='The attribute in the LDAP server that will be used to populate the last name of users in Mattermost.'
/>
</p>
</div>
</div>
<div className='form-group'>
@@ -315,7 +439,10 @@ export default class LdapSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='EmailAttribute'
>
{'Email Attribute:'}
<FormattedMessage
id='admin.ldap.emailAttrTitle'
defaultMessage='Email Attribute:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -323,12 +450,17 @@ export default class LdapSettings extends React.Component {
className='form-control'
id='EmailAttribute'
ref='EmailAttribute'
placeholder='Ex "mail"'
placeholder={formatMessage(holders.emailAttrEx)}
defaultValue={this.props.config.LdapSettings.EmailAttribute}
onChange={this.handleChange}
disabled={!this.state.enable}
/>
<p className='help-text'>{'The attribute in the LDAP server that will be used to populate the email addresses of users in Mattermost.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.ldap.emailAttrDesc'
defaultMessage='The attribute in the LDAP server that will be used to populate the email addresses of users in Mattermost.'
/>
</p>
</div>
</div>
<div className='form-group'>
@@ -336,7 +468,10 @@ export default class LdapSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='UsernameAttribute'
>
{'Username Attribute:'}
<FormattedMessage
id='admin.ldap.usernameAttrTitle'
defaultMessage='Username Attribute:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -344,12 +479,17 @@ export default class LdapSettings extends React.Component {
className='form-control'
id='UsernameAttribute'
ref='UsernameAttribute'
placeholder='Ex "sAMAccountName"'
placeholder={formatMessage(holders.usernameAttrEx)}
defaultValue={this.props.config.LdapSettings.UsernameAttribute}
onChange={this.handleChange}
disabled={!this.state.enable}
/>
<p className='help-text'>{'The attribute in the LDAP server that will be used to populate the username field in Mattermost. This may be the same as the ID Attribute.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.ldap.uernameAttrDesc'
defaultMessage='The attribute in the LDAP server that will be used to populate the username field in Mattermost. This may be the same as the ID Attribute.'
/>
</p>
</div>
</div>
<div className='form-group'>
@@ -357,7 +497,10 @@ export default class LdapSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='IdAttribute'
>
{'Id Attribute: '}
<FormattedMessage
id='admin.ldap.idAttrTitle'
defaultMessage='Id Attribute: '
/>
</label>
<div className='col-sm-8'>
<input
@@ -365,12 +508,17 @@ export default class LdapSettings extends React.Component {
className='form-control'
id='IdAttribute'
ref='IdAttribute'
placeholder='Ex "sAMAccountName"'
placeholder={formatMessage(holders.idAttrEx)}
defaultValue={this.props.config.LdapSettings.IdAttribute}
onChange={this.handleChange}
disabled={!this.state.enable}
/>
<p className='help-text'>{'The attribute in the LDAP server that will be used as a unique identifier in Mattermost. It should be an LDAP attribute with a value that does not change, such as username or uid. If a users Id Attribute changes, it will create a new Mattermost account unassociated with their old one. This is the value used to log in to Mattermost in the "LDAP Username" field on the sign in page. Normally this attribute is the same as the “Username Attribute” field above. If your team typically uses domain\\username to sign in to other services with LDAP, you may choose to put domain\\username in this field to maintain consistency between sites.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.ldap.idAttrDesc'
defaultMessage='The attribute in the LDAP server that will be used as a unique identifier in Mattermost. It should be an LDAP attribute with a value that does not change, such as username or uid. If a users Id Attribute changes, it will create a new Mattermost account unassociated with their old one. This is the value used to log in to Mattermost in the "LDAP Username" field on the sign in page. Normally this attribute is the same as the “Username Attribute” field above. If your team typically uses domain\\username to sign in to other services with LDAP, you may choose to put domain\\username in this field to maintain consistency between sites.'
/>
</p>
</div>
</div>
<div className='form-group'>
@@ -378,7 +526,10 @@ export default class LdapSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='QueryTimeout'
>
{'Query Timeout (seconds):'}
<FormattedMessage
id='admin.ldap.queryTitle'
defaultMessage='Query Timeout (seconds):'
/>
</label>
<div className='col-sm-8'>
<input
@@ -386,12 +537,17 @@ export default class LdapSettings extends React.Component {
className='form-control'
id='QueryTimeout'
ref='QueryTimeout'
placeholder='Ex "60"'
placeholder={formatMessage(holders.queryEx)}
defaultValue={this.props.config.LdapSettings.QueryTimeout}
onChange={this.handleChange}
disabled={!this.state.enable}
/>
<p className='help-text'>{'The timeout value for queries to the LDAP server. Increase if you are getting timeout errors caused by a slow LDAP server.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.ldap.queryDesc'
defaultMessage='The timeout value for queries to the LDAP server. Increase if you are getting timeout errors caused by a slow LDAP server.'
/>
</p>
</div>
</div>
<div className='form-group'>
@@ -403,9 +559,12 @@ export default class LdapSettings extends React.Component {
className={saveClass}
onClick={this.handleSubmit}
id='save-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Saving Config...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
>
{'Save'}
<FormattedMessage
id='admin.ldap.save'
defaultMessage='Save'
/>
</button>
</div>
</div>
@@ -418,5 +577,8 @@ LdapSettings.defaultProps = {
};
LdapSettings.propTypes = {
intl: intlShape.isRequired,
config: React.PropTypes.object
};
export default injectIntl(LdapSettings);

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

@@ -4,7 +4,16 @@
import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
export default class LegalAndSupportSettings extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
var holders = defineMessages({
saving: {
id: 'admin.support.saving',
defaultMessage: 'Saving Config...'
}
});
class LegalAndSupportSettings extends React.Component {
constructor(props) {
super(props);
@@ -69,7 +78,12 @@ export default class LegalAndSupportSettings extends React.Component {
return (
<div className='wrapper--fixed'>
<h3>{'Legal and Support Settings'}</h3>
<h3>
<FormattedMessage
id='admin.support.title'
defaultMessage='Legal and Support Settings'
/>
</h3>
<form
className='form-horizontal'
role='form'
@@ -80,7 +94,10 @@ export default class LegalAndSupportSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='TermsOfServiceLink'
>
{'Terms of Service link:'}
<FormattedMessage
id='admin.support.termsTitle'
defaultMessage='Terms of Service link:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -91,7 +108,12 @@ export default class LegalAndSupportSettings extends React.Component {
defaultValue={this.props.config.SupportSettings.TermsOfServiceLink}
onChange={this.handleChange}
/>
<p className='help-text'>{'Link to Terms of Service available to users on desktop and on mobile. Leaving this blank will hide the option to display a notice.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.support.termsDesc'
defaultMessage='Link to Terms of Service available to users on desktop and on mobile. Leaving this blank will hide the option to display a notice.'
/>
</p>
</div>
</div>
@@ -100,7 +122,10 @@ export default class LegalAndSupportSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='PrivacyPolicyLink'
>
{'Privacy Policy link:'}
<FormattedMessage
id='admin.support.privacyTitle'
defaultMessage='Privacy Policy link:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -111,7 +136,12 @@ export default class LegalAndSupportSettings extends React.Component {
defaultValue={this.props.config.SupportSettings.PrivacyPolicyLink}
onChange={this.handleChange}
/>
<p className='help-text'>{'Link to Privacy Policy available to users on desktop and on mobile. Leaving this blank will hide the option to display a notice.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.support.privacyDesc'
defaultMessage='Link to Privacy Policy available to users on desktop and on mobile. Leaving this blank will hide the option to display a notice.'
/>
</p>
</div>
</div>
@@ -120,7 +150,10 @@ export default class LegalAndSupportSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='AboutLink'
>
{'About link:'}
<FormattedMessage
id='admin.support.aboutTitle'
defaultMessage='About link:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -131,7 +164,12 @@ export default class LegalAndSupportSettings extends React.Component {
defaultValue={this.props.config.SupportSettings.AboutLink}
onChange={this.handleChange}
/>
<p className='help-text'>{'Link to About page for more information on your Mattermost deployment, for example its purpose and audience within your organization. Defaults to Mattermost information page.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.support.aboutDesc'
defaultMessage='Link to About page for more information on your Mattermost deployment, for example its purpose and audience within your organization. Defaults to Mattermost information page.'
/>
</p>
</div>
</div>
@@ -140,7 +178,10 @@ export default class LegalAndSupportSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='HelpLink'
>
{'Help link:'}
<FormattedMessage
id='admin.support.helpTitle'
defaultMessage='Help link:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -151,7 +192,12 @@ export default class LegalAndSupportSettings extends React.Component {
defaultValue={this.props.config.SupportSettings.HelpLink}
onChange={this.handleChange}
/>
<p className='help-text'>{'Link to help documentation from team site main menu. Typically not changed unless your organization chooses to create custom documentation.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.support.helpDesc'
defaultMessage='Link to help documentation from team site main menu. Typically not changed unless your organization chooses to create custom documentation.'
/>
</p>
</div>
</div>
@@ -160,7 +206,10 @@ export default class LegalAndSupportSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='ReportAProblemLink'
>
{'Report a Problem link:'}
<FormattedMessage
id='admin.support.problemTitle'
defaultMessage='Report a Problem link:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -171,7 +220,12 @@ export default class LegalAndSupportSettings extends React.Component {
defaultValue={this.props.config.SupportSettings.ReportAProblemLink}
onChange={this.handleChange}
/>
<p className='help-text'>{'Link to help documentation from team site main menu. By default this points to the peer-to-peer troubleshooting forum where users can search for, find and request help with technical issues.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.support.problemDesc'
defaultMessage='Link to help documentation from team site main menu. By default this points to the peer-to-peer troubleshooting forum where users can search for, find and request help with technical issues.'
/>
</p>
</div>
</div>
@@ -180,7 +234,10 @@ export default class LegalAndSupportSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='SupportEmail'
>
{'Support email:'}
<FormattedMessage
id='admin.support.emailTitle'
defaultMessage='Support email:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -191,7 +248,12 @@ export default class LegalAndSupportSettings extends React.Component {
defaultValue={this.props.config.SupportSettings.SupportEmail}
onChange={this.handleChange}
/>
<p className='help-text'>{'Email shown during tutorial for end users to ask support questions.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.support.emailHelp'
defaultMessage='Email shown during tutorial for end users to ask support questions.'
/>
</p>
</div>
</div>
@@ -204,9 +266,12 @@ export default class LegalAndSupportSettings extends React.Component {
className={saveClass}
onClick={this.handleSubmit}
id='save-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Saving Config...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.saving)}
>
{'Save'}
<FormattedMessage
id='admin.support.save'
defaultMessage='Save'
/>
</button>
</div>
</div>
@@ -218,5 +283,8 @@ export default class LegalAndSupportSettings extends React.Component {
}
LegalAndSupportSettings.propTypes = {
intl: intlShape.isRequired,
config: React.PropTypes.object
};
export default injectIntl(LegalAndSupportSettings);

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

@@ -4,7 +4,20 @@
import * as Utils from '../../utils/utils.jsx';
import * as Client from '../../utils/client.jsx';
export default class LicenseSettings extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const holders = defineMessages({
removing: {
id: 'admin.license.removing',
defaultMessage: 'Removing License...'
},
uploading: {
id: 'admin.license.uploading',
defaultMessage: 'Uploading License...'
}
});
class LicenseSettings extends React.Component {
constructor(props) {
super(props);
@@ -88,41 +101,26 @@ export default class LicenseSettings extends React.Component {
let licenseKey;
if (global.window.mm_license.IsLicensed === 'true') {
edition = 'Mattermost Enterprise Edition. Designed for enterprise-scale communication.';
edition = (
<FormattedMessage
id='admin.license.enterpriseEdition'
defaultMessage='Mattermost Enterprise Edition. Designed for enterprise-scale communication.'
/>
);
licenseType = (
<div>
<p>
{'This compiled release of Mattermost platform is provided under a '}
<a
href='http://mattermost.com'
target='_blank'
>
{'commercial license'}
</a>
{' from Mattermost, Inc. based on your subscription level and is subject to the '}
<a
href={global.window.mm_config.TermsOfServiceLink}
target='_blank'
>
{'Terms of Service.'}
</a>
</p>
<p>{'Your subscription details are as follows:'}</p>
{'Name: ' + global.window.mm_license.Name}
<br/>
{'Company or organization name: ' + global.window.mm_license.Company}
<br/>
{'Number of users: ' + global.window.mm_license.Users}
<br/>
{`License issued: ${Utils.displayDate(parseInt(global.window.mm_license.IssuedAt, 10))} ${Utils.displayTime(parseInt(global.window.mm_license.IssuedAt, 10), true)}`}
<br/>
{'Start date of license: ' + Utils.displayDate(parseInt(global.window.mm_license.StartsAt, 10))}
<br/>
{'Expiry date of license: ' + Utils.displayDate(parseInt(global.window.mm_license.ExpiresAt, 10))}
<br/>
{'LDAP: ' + global.window.mm_license.LDAP}
<br/>
</div>
<FormattedHTMLMessage
id='admin.license.entrepriseType'
defaultMessage='<div><p>This compiled release of Mattermost platform is provided under a <a href="http://mattermost.com" target="_blank">commercial license</a>
from Mattermost, Inc. based on your subscription level and is subject to the <a href="{terms}" target="_blank">Terms of Service.</a></p>
<p>Your subscription details are as follows:</p>
Name: {name}<br />
Company or organization name: {company}<br/>
Number of users: {users}<br/>
License issued: {issued}<br/>
Start date of license: {start}<br/>
Expiry date of license: {expires}<br/>
LDAP: {ldap}<br/></div>'
/>
);
licenseKey = (
@@ -131,32 +129,39 @@ export default class LicenseSettings extends React.Component {
className='btn btn-danger'
onClick={this.handleRemove}
id='remove-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Removing License...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.removing)}
>
{'Remove Enterprise License and Downgrade Server'}
<FormattedMessage
id='admin.license.keyRemove'
defaultMessage='Remove Enterprise License and Downgrade Server'
/>
</button>
<br/>
<br/>
<p className='help-text'>
{'If youre migrating servers you may need to remove your license key from this server in order to install it on a new server. To start, '}
<a
href='http://mattermost.com'
target='_blank'
>
{'disable all Enterprise Edition features on this server'}
</a>
{'. This will enable the ability to remove the license key and downgrade this server from Enterprise Edition to Team Edition.'}
<FormattedHTMLMessage
id='admin.licence.keyMigration'
defaultMessage='If youre migrating servers you may need to remove your license key from this server in order to install it on a new server. To start,
<a href="http://mattermost.com" target="_blank">disable all Enterprise Edition features on this server</a>.
This will enable the ability to remove the license key and downgrade this server from Enterprise Edition to Team Edition.'
/>
</p>
</div>
);
} else {
edition = 'Mattermost Team Edition. Designed for teams from 5 to 50 users.';
edition = (
<FormattedMessage
id='admin.license.teamEdition'
defaultMessage='Mattermost Team Edition. Designed for teams from 5 to 50 users.'
/>
);
licenseType = (
<span>
<p>{'This compiled release of Mattermost platform is offered under an MIT license.'}</p>
<p>{'See MIT-COMPILED-LICENSE.txt in your root install directory for details. See NOTICES.txt for information about open source software used in this system.'}</p>
</span>
<FormattedHTMLMessage
id='admin.license.teamType'
defaultMessage='<span><p>This compiled release of Mattermost platform is offered under an MIT license.</p>
<p>See MIT-COMPILED-LICENSE.txt in your root install directory for details. See NOTICES.txt for information about open source software used in this system.</p></span>'
/>
);
licenseKey = (
@@ -173,23 +178,23 @@ export default class LicenseSettings extends React.Component {
disabled={!this.state.fileSelected}
onClick={this.handleSubmit}
id='upload-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Uploading License...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.uploading)}
>
{'Upload'}
<FormattedMessage
id='admin.license.upload'
defaultMessage='Upload'
/>
</button>
<br/>
<br/>
<br/>
{serverError}
<p className='help-text'>
{'Upload a license key for Mattermost Enterprise Edition to upgrade this server. '}
<a
href='http://mattermost.com'
target='_blank'
>
{'Visit us online'}
</a>
{' to learn more about the benefits of Enterprise Edition or to purchase a key.'}
<FormattedHTMLMessage
id='admin.license.uploadDesc'
defaultMessage='Upload a license key for Mattermost Enterprise Edition to upgrade this server. <a href="http://mattermost.com" target="_blank">Visit us online</a>
to learn more about the benefits of Enterprise Edition or to purchase a key.'
/>
</p>
</div>
);
@@ -197,7 +202,12 @@ export default class LicenseSettings extends React.Component {
return (
<div className='wrapper--fixed'>
<h3>{'Edition and License'}</h3>
<h3>
<FormattedMessage
id='admin.license.title'
defaultMessage='Edition and License'
/>
</h3>
<form
className='form-horizontal'
role='form'
@@ -206,7 +216,10 @@ export default class LicenseSettings extends React.Component {
<label
className='control-label col-sm-4'
>
{'Edition: '}
<FormattedMessage
id='admin.license.edition'
defaultMessage='Edition: '
/>
</label>
<div className='col-sm-8'>
{edition}
@@ -216,7 +229,10 @@ export default class LicenseSettings extends React.Component {
<label
className='control-label col-sm-4'
>
{'License: '}
<FormattedMessage
id='admin.license.type'
defaultMessage='License: '
/>
</label>
<div className='col-sm-8'>
{licenseType}
@@ -226,7 +242,10 @@ export default class LicenseSettings extends React.Component {
<label
className='control-label col-sm-4'
>
{'License Key: '}
<FormattedMessage
id='admin.license.key'
defaultMessage='License Key: '
/>
</label>
{licenseKey}
</div>
@@ -235,3 +254,9 @@ export default class LicenseSettings extends React.Component {
);
}
}
LicenseSettings.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(LicenseSettings);

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

@@ -4,7 +4,24 @@
import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
export default class LogSettings extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
locationPlaceholder: {
id: 'admin.log.locationPlaceholder',
defaultMessage: 'Enter your file location'
},
formatPlaceholder: {
id: 'admin.log.formatPlaceholder',
defaultMessage: 'Enter your file format'
},
saving: {
id: 'admin.log.saving',
defaultMessage: 'Saving Config...'
}
});
class LogSettings extends React.Component {
constructor(props) {
super(props);
@@ -78,6 +95,7 @@ export default class LogSettings extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
var serverError = '';
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
@@ -90,7 +108,12 @@ export default class LogSettings extends React.Component {
return (
<div className='wrapper--fixed'>
<h3>{'Log Settings'}</h3>
<h3>
<FormattedMessage
id='admin.log.logSettings'
defaultMessage='Log Settings'
/>
</h3>
<form
className='form-horizontal'
role='form'
@@ -101,7 +124,10 @@ export default class LogSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='consoleEnable'
>
{'Log To The Console: '}
<FormattedMessage
id='admin.log.consoleTitle'
defaultMessage='Log To The Console: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -113,7 +139,10 @@ export default class LogSettings extends React.Component {
defaultChecked={this.props.config.LogSettings.EnableConsole}
onChange={this.handleChange.bind(this, 'console_true')}
/>
{'true'}
<FormattedMessage
id='admin.log.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -123,9 +152,17 @@ export default class LogSettings extends React.Component {
defaultChecked={!this.props.config.LogSettings.EnableConsole}
onChange={this.handleChange.bind(this, 'console_false')}
/>
{'false'}
<FormattedMessage
id='admin.log.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'Typically set to false in production. Developers may set this field to true to output log messages to console based on the console level option. If true, server writes messages to the standard output stream (stdout).'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.log.consoleDescription'
defaultMessage='Typically set to false in production. Developers may set this field to true to output log messages to console based on the console level option. If true, server writes messages to the standard output stream (stdout).'
/>
</p>
</div>
</div>
@@ -134,7 +171,10 @@ export default class LogSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='consoleLevel'
>
{'Console Log Level:'}
<FormattedMessage
id='admin.log.levelTitle'
defaultMessage='Console Log Level:'
/>
</label>
<div className='col-sm-8'>
<select
@@ -149,7 +189,12 @@ export default class LogSettings extends React.Component {
<option value='INFO'>{'INFO'}</option>
<option value='ERROR'>{'ERROR'}</option>
</select>
<p className='help-text'>{'This setting determines the level of detail at which log events are written to the console. ERROR: Outputs only error messages. INFO: Outputs error messages and information around startup and initialization. DEBUG: Prints high detail for developers working on debugging issues.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.log.levelDescription'
defaultMessage='This setting determines the level of detail at which log events are written to the console. ERROR: Outputs only error messages. INFO: Outputs error messages and information around startup and initialization. DEBUG: Prints high detail for developers working on debugging issues.'
/>
</p>
</div>
</div>
@@ -157,7 +202,10 @@ export default class LogSettings extends React.Component {
<label
className='control-label col-sm-4'
>
{'Log To File: '}
<FormattedMessage
id='admin.log.fileTitle'
defaultMessage='Log To File: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -169,7 +217,10 @@ export default class LogSettings extends React.Component {
defaultChecked={this.props.config.LogSettings.EnableFile}
onChange={this.handleChange.bind(this, 'file_true')}
/>
{'true'}
<FormattedMessage
id='admin.log.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -179,9 +230,17 @@ export default class LogSettings extends React.Component {
defaultChecked={!this.props.config.LogSettings.EnableFile}
onChange={this.handleChange.bind(this, 'file_false')}
/>
{'false'}
<FormattedMessage
id='admin.log.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'Typically set to true in production. When true, log files are written to the log file specified in file location field below.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.log.fileDescription'
defaultMessage='Typically set to true in production. When true, log files are written to the log file specified in file location field below.'
/>
</p>
</div>
</div>
@@ -190,7 +249,10 @@ export default class LogSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='fileLevel'
>
{'File Log Level:'}
<FormattedMessage
id='admin.log.fileLevelTitle'
defaultMessage='File Log Level:'
/>
</label>
<div className='col-sm-8'>
<select
@@ -205,7 +267,12 @@ export default class LogSettings extends React.Component {
<option value='INFO'>{'INFO'}</option>
<option value='ERROR'>{'ERROR'}</option>
</select>
<p className='help-text'>{'This setting determines the level of detail at which log events are written to the log file. ERROR: Outputs only error messages. INFO: Outputs error messages and information around startup and initialization. DEBUG: Prints high detail for developers working on debugging issues.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.log.fileLevelDescription'
defaultMessage='This setting determines the level of detail at which log events are written to the log file. ERROR: Outputs only error messages. INFO: Outputs error messages and information around startup and initialization. DEBUG: Prints high detail for developers working on debugging issues.'
/>
</p>
</div>
</div>
@@ -214,7 +281,10 @@ export default class LogSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='fileLocation'
>
{'File Location:'}
<FormattedMessage
id='admin.log.locationTitle'
defaultMessage='File Location:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -222,12 +292,17 @@ export default class LogSettings extends React.Component {
className='form-control'
id='fileLocation'
ref='fileLocation'
placeholder='Enter your file location'
placeholder={formatMessage(holders.locationPlaceholder)}
defaultValue={this.props.config.LogSettings.FileLocation}
onChange={this.handleChange}
disabled={!this.state.fileEnable}
/>
<p className='help-text'>{'File to which log files are written. If blank, will be set to ./logs/mattermost, which writes logs to mattermost.log. Log rotation is enabled and every 10,000 lines of log information is written to new files stored in the same directory, for example mattermost.2015-09-23.001, mattermost.2015-09-23.002, and so forth.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.log.locationDescription'
defaultMessage='File to which log files are written. If blank, will be set to ./logs/mattermost, which writes logs to mattermost.log. Log rotation is enabled and every 10,000 lines of log information is written to new files stored in the same directory, for example mattermost.2015-09-23.001, mattermost.2015-09-23.002, and so forth.'
/>
</p>
</div>
</div>
@@ -236,7 +311,10 @@ export default class LogSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='fileFormat'
>
{'File Format:'}
<FormattedMessage
id='admin.log.formatTitle'
defaultMessage='File Format:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -244,25 +322,58 @@ export default class LogSettings extends React.Component {
className='form-control'
id='fileFormat'
ref='fileFormat'
placeholder='Enter your file format'
placeholder={formatMessage(holders.formatPlaceholder)}
defaultValue={this.props.config.LogSettings.FileFormat}
onChange={this.handleChange}
disabled={!this.state.fileEnable}
/>
<div className='help-text'>
{'Format of log message output. If blank will be set to "[%D %T] [%L] %M", where:'}
<FormattedMessage
id='admin.log.formatDescription'
defaultMessage='Format of log message output. If blank will be set to "[%D %T] [%L] %M", where:'
/>
<div className='help-text'>
<table
className='table table-bordered'
cellPadding='5'
>
<tbody>
<tr><td className='help-text'>{'%T'}</td><td className='help-text'>{'Time (15:04:05 MST)'}</td></tr>
<tr><td className='help-text'>{'%D'}</td><td className='help-text'>{'Date (2006/01/02)'}</td></tr>
<tr><td className='help-text'>{'%d'}</td><td className='help-text'>{'Date (01/02/06)'}</td></tr>
<tr><td className='help-text'>{'%L'}</td><td className='help-text'>{'Level (DEBG, INFO, EROR)'}</td></tr>
<tr><td className='help-text'>{'%S'}</td><td className='help-text'>{'Source'}</td></tr>
<tr><td className='help-text'>{'%M'}</td><td className='help-text'>{'Message'}</td></tr>
<tr><td className='help-text'>{'%T'}</td><td className='help-text'>
<FormattedMessage
id='admin.log.formatTime'
defaultMessage='Time (15:04:05 MST)'
/>
</td></tr>
<tr><td className='help-text'>{'%D'}</td><td className='help-text'>
<FormattedMessage
id='admin.log.formatDateLong'
defaultMessage='Date (2006/01/02)'
/>
</td></tr>
<tr><td className='help-text'>{'%d'}</td><td className='help-text'>
<FormattedMessage
id='admin.log.formatDateShort'
defaultMessage='Date (01/02/06)'
/>
</td></tr>
<tr><td className='help-text'>{'%L'}</td><td className='help-text'>
<FormattedMessage
id='admin.log.formatLevel'
defaultMessage='Level (DEBG, INFO, EROR)'
/>
</td></tr>
<tr><td className='help-text'>{'%S'}</td><td className='help-text'>
<FormattedMessage
id='admin.log.formatSource'
defaultMessage='Source'
/>
</td></tr>
<tr><td className='help-text'>{'%M'}</td><td className='help-text'>
<FormattedMessage
id='admin.log.formatMessage'
defaultMessage='Message'
/>
</td></tr>
</tbody>
</table>
</div>
@@ -279,9 +390,12 @@ export default class LogSettings extends React.Component {
className={saveClass}
onClick={this.handleSubmit}
id='save-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Saving Config...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
>
{'Save'}
<FormattedMessage
id='admin.log.save'
defaultMessage='Save'
/>
</button>
</div>
</div>
@@ -293,5 +407,8 @@ export default class LogSettings extends React.Component {
}
LogSettings.propTypes = {
intl: intlShape.isRequired,
config: React.PropTypes.object
};
export default injectIntl(LogSettings);

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

@@ -5,6 +5,8 @@ import AdminStore from '../../stores/admin_store.jsx';
import LoadingScreen from '../loading_screen.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import {FormattedMessage} from 'mm-intl';
export default class Logs extends React.Component {
constructor(props) {
super(props);
@@ -73,13 +75,21 @@ export default class Logs extends React.Component {
return (
<div className='panel'>
<h3>{'Server Logs'}</h3>
<h3>
<FormattedMessage
id='admin.logs.title'
defaultMessage='Server Logs'
/>
</h3>
<button
type='submit'
className='btn btn-primary'
onClick={this.reload}
>
{'Reload'}
<FormattedMessage
id='admin.logs.reload'
defaultMessage='Reload'
/>
</button>
<div className='log__panel'>
{content}

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

@@ -4,7 +4,16 @@
import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
export default class PrivacySettings extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
saving: {
id: 'admin.privacy.saving',
defaultMessage: 'Saving Config...'
}
});
class PrivacySettings extends React.Component {
constructor(props) {
super(props);
@@ -64,7 +73,12 @@ export default class PrivacySettings extends React.Component {
return (
<div className='wrapper--fixed'>
<h3>{'Privacy Settings'}</h3>
<h3>
<FormattedMessage
id='admin.privacy.title'
defaultMessage='Privacy Settings'
/>
</h3>
<form
className='form-horizontal'
role='form'
@@ -75,7 +89,10 @@ export default class PrivacySettings extends React.Component {
className='control-label col-sm-4'
htmlFor='ShowEmailAddress'
>
{'Show Email Address: '}
<FormattedMessage
id='admin.privacy.showEmailTitle'
defaultMessage='Show Email Address: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -87,7 +104,10 @@ export default class PrivacySettings extends React.Component {
defaultChecked={this.props.config.PrivacySettings.ShowEmailAddress}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.privacy.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -97,9 +117,17 @@ export default class PrivacySettings extends React.Component {
defaultChecked={!this.props.config.PrivacySettings.ShowEmailAddress}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.privacy.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When false, hides email address of users from other users in the user interface, including team owners and team administrators. Used when system is set up for managing teams where some users choose to keep their contact information private.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.privacy.showEmailDescription'
defaultMessage='When false, hides email address of users from other users in the user interface, including team owners and team administrators. Used when system is set up for managing teams where some users choose to keep their contact information private.'
/>
</p>
</div>
</div>
@@ -108,7 +136,10 @@ export default class PrivacySettings extends React.Component {
className='control-label col-sm-4'
htmlFor='ShowFullName'
>
{'Show Full Name: '}
<FormattedMessage
id='admin.privacy.showFullNameTitle'
defaultMessage='Show Full Name: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -120,7 +151,10 @@ export default class PrivacySettings extends React.Component {
defaultChecked={this.props.config.PrivacySettings.ShowFullName}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.privacy.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -130,9 +164,17 @@ export default class PrivacySettings extends React.Component {
defaultChecked={!this.props.config.PrivacySettings.ShowFullName}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.privacy.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When false, hides full name of users from other users, including team owners and team administrators. Username is shown in place of full name.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.privacy.showFullNameDescription'
defaultMessage='When false, hides full name of users from other users, including team owners and team administrators. Username is shown in place of full name.'
/>
</p>
</div>
</div>
@@ -145,9 +187,12 @@ export default class PrivacySettings extends React.Component {
className={saveClass}
onClick={this.handleSubmit}
id='save-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Saving Config...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.saving)}
>
{'Save'}
<FormattedMessage
id='admin.privacy.save'
defaultMessage='Save'
/>
</button>
</div>
</div>
@@ -159,5 +204,8 @@ export default class PrivacySettings extends React.Component {
}
PrivacySettings.propTypes = {
intl: intlShape.isRequired,
config: React.PropTypes.object
};
export default injectIntl(PrivacySettings);

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

@@ -4,7 +4,28 @@
import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
export default class RateSettings extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
queriesExample: {
id: 'admin.rate.queriesExample',
defaultMessage: 'Ex "10"'
},
memoryExample: {
id: 'admin.rate.memoryExample',
defaultMessage: 'Ex "10000"'
},
httpHeaderExample: {
id: 'admin.rate.httpHeaderExample',
defaultMessage: 'Ex "X-Real-IP", "X-Forwarded-For"'
},
saving: {
id: 'admin.rate.saving',
defaultMessage: 'Saving Config...'
}
});
class RateSettings extends React.Component {
constructor(props) {
super(props);
@@ -85,6 +106,7 @@ export default class RateSettings extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
var serverError = '';
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
@@ -100,12 +122,27 @@ export default class RateSettings extends React.Component {
<div className='banner'>
<div className='banner__content'>
<h4 className='banner__heading'>{'Note:'}</h4>
<p>{'Changing properties in this section will require a server restart before taking effect.'}</p>
<h4 className='banner__heading'>
<FormattedMessage
id='admin.rate.noteTitle'
defaultMessage='Note:'
/>
</h4>
<p>
<FormattedMessage
id='admin.rate.noteDescription'
defaultMessage='Changing properties in this section will require a server restart before taking effect.'
/>
</p>
</div>
</div>
<h3>{'Rate Limit Settings'}</h3>
<h3>
<FormattedMessage
id='admin.rate.title'
defaultMessage='Rate Limit Settings'
/>
</h3>
<form
className='form-horizontal'
role='form'
@@ -116,7 +153,10 @@ export default class RateSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='EnableRateLimiter'
>
{'Enable Rate Limiter: '}
<FormattedMessage
id='admin.rate.enableLimiterTitle'
defaultMessage='Enable Rate Limiter: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -128,7 +168,10 @@ export default class RateSettings extends React.Component {
defaultChecked={this.props.config.RateLimitSettings.EnableRateLimiter}
onChange={this.handleChange.bind(this, 'EnableRateLimiterTrue')}
/>
{'true'}
<FormattedMessage
id='admin.rate.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -138,9 +181,17 @@ export default class RateSettings extends React.Component {
defaultChecked={!this.props.config.RateLimitSettings.EnableRateLimiter}
onChange={this.handleChange.bind(this, 'EnableRateLimiterFalse')}
/>
{'false'}
<FormattedMessage
id='admin.rate.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When true, APIs are throttled at rates specified below.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.rate.enableLimiterDescription'
defaultMessage='When true, APIs are throttled at rates specified below.'
/>
</p>
</div>
</div>
@@ -149,7 +200,10 @@ export default class RateSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='PerSec'
>
{'Number Of Queries Per Second:'}
<FormattedMessage
id='admin.rate.queriesTitle'
defaultMessage='Number Of Queries Per Second:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -157,12 +211,17 @@ export default class RateSettings extends React.Component {
className='form-control'
id='PerSec'
ref='PerSec'
placeholder='Ex "10"'
placeholder={formatMessage(holders.queriesExample)}
defaultValue={this.props.config.RateLimitSettings.PerSec}
onChange={this.handleChange}
disabled={!this.state.EnableRateLimiter}
/>
<p className='help-text'>{'Throttles API at this number of requests per second.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.rate.queriesDescription'
defaultMessage='Throttles API at this number of requests per second.'
/>
</p>
</div>
</div>
@@ -171,7 +230,10 @@ export default class RateSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='MemoryStoreSize'
>
{'Memory Store Size:'}
<FormattedMessage
id='admin.rate.memoryTitle'
defaultMessage='Memory Store Size:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -179,12 +241,17 @@ export default class RateSettings extends React.Component {
className='form-control'
id='MemoryStoreSize'
ref='MemoryStoreSize'
placeholder='Ex "10000"'
placeholder={formatMessage(holders.memoryExample)}
defaultValue={this.props.config.RateLimitSettings.MemoryStoreSize}
onChange={this.handleChange}
disabled={!this.state.EnableRateLimiter}
/>
<p className='help-text'>{'Maximum number of users sessions connected to the system as determined by "Vary By Remote Address" and "Vary By Header" settings below.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.rate.memoryDescription'
defaultMessage='Maximum number of users sessions connected to the system as determined by "Vary By Remote Address" and "Vary By Header" settings below.'
/>
</p>
</div>
</div>
@@ -193,7 +260,10 @@ export default class RateSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='VaryByRemoteAddr'
>
{'Vary By Remote Address: '}
<FormattedMessage
id='admin.rate.remoteTitle'
defaultMessage='Vary By Remote Address: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -206,7 +276,10 @@ export default class RateSettings extends React.Component {
onChange={this.handleChange.bind(this, 'VaryByRemoteAddrTrue')}
disabled={!this.state.EnableRateLimiter}
/>
{'true'}
<FormattedMessage
id='admin.rate.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -217,9 +290,17 @@ export default class RateSettings extends React.Component {
onChange={this.handleChange.bind(this, 'VaryByRemoteAddrFalse')}
disabled={!this.state.EnableRateLimiter}
/>
{'false'}
<FormattedMessage
id='admin.rate.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When true, rate limit API access by IP address.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.rate.remoteDescription'
defaultMessage='When true, rate limit API access by IP address.'
/>
</p>
</div>
</div>
@@ -228,7 +309,10 @@ export default class RateSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='VaryByHeader'
>
{'Vary By HTTP Header:'}
<FormattedMessage
id='admin.rate.httpHeaderTitle'
defaultMessage='Vary By HTTP Header:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -236,12 +320,17 @@ export default class RateSettings extends React.Component {
className='form-control'
id='VaryByHeader'
ref='VaryByHeader'
placeholder='Ex "X-Real-IP", "X-Forwarded-For"'
placeholder={formatMessage(holders.httpHeaderExample)}
defaultValue={this.props.config.RateLimitSettings.VaryByHeader}
onChange={this.handleChange}
disabled={!this.state.EnableRateLimiter || this.state.VaryByRemoteAddr}
/>
<p className='help-text'>{'When filled in, vary rate limiting by HTTP header field specified (e.g. when configuring NGINX set to "X-Real-IP", when configuring AmazonELB set to "X-Forwarded-For").'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.rate.httpHeaderDescription'
defaultMessage='When filled in, vary rate limiting by HTTP header field specified (e.g. when configuring NGINX set to "X-Real-IP", when configuring AmazonELB set to "X-Forwarded-For").'
/>
</p>
</div>
</div>
@@ -254,9 +343,12 @@ export default class RateSettings extends React.Component {
className={saveClass}
onClick={this.handleSubmit}
id='save-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Saving Config...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
>
{'Save'}
<FormattedMessage
id='admin.rate.save'
defaultMessage='Save'
/>
</button>
</div>
</div>
@@ -268,5 +360,8 @@ export default class RateSettings extends React.Component {
}
RateSettings.propTypes = {
intl: intlShape.isRequired,
config: React.PropTypes.object
};
export default injectIntl(RateSettings);

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

@@ -5,7 +5,16 @@ import * as Client from '../../utils/client.jsx';
import Constants from '../../utils/constants.jsx';
var Modal = ReactBootstrap.Modal;
export default class ResetPasswordModal extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
var holders = defineMessages({
submit: {
id: 'admin.reset_password.submit',
defaultMessage: 'Please enter at least {chars} characters.'
}
});
class ResetPasswordModal extends React.Component {
constructor(props) {
super(props);
@@ -22,7 +31,7 @@ export default class ResetPasswordModal extends React.Component {
var password = ReactDOM.findDOMNode(this.refs.password).value;
if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
this.setState({serverError: 'Please enter at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters.'});
this.setState({serverError: this.props.intl.formatMessage(holders.submit, {chars: Constants.MIN_PASSWORD_LENGTH})});
return;
}
@@ -67,7 +76,12 @@ export default class ResetPasswordModal extends React.Component {
onHide={this.doCancel}
>
<Modal.Header closeButton={true}>
<Modal.Title>{'Reset Password'}</Modal.Title>
<Modal.Title>
<FormattedMessage
id='admin.reset_password.title'
defaultMessage='Reset Password'
/>
</Modal.Title>
</Modal.Header>
<form
role='form'
@@ -82,7 +96,10 @@ export default class ResetPasswordModal extends React.Component {
title='New Password'
className='input-group-addon'
>
{'New Password'}
<FormattedMessage
id='admin.reset_password.newPassword'
defaultMessage='New Password'
/>
</span>
<input
type='password'
@@ -103,7 +120,10 @@ export default class ResetPasswordModal extends React.Component {
className='btn btn-default'
onClick={this.doCancel}
>
{'Close'}
<FormattedMessage
id='admin.reset_password.close'
defaultMessage='Close'
/>
</button>
<button
onClick={this.doSubmit}
@@ -111,7 +131,10 @@ export default class ResetPasswordModal extends React.Component {
className='btn btn-primary'
tabIndex='2'
>
{'Select'}
<FormattedMessage
id='admin.reset_password.select'
defaultMessage='Select'
/>
</button>
</Modal.Footer>
</form>
@@ -125,9 +148,12 @@ ResetPasswordModal.defaultProps = {
};
ResetPasswordModal.propTypes = {
intl: intlShape.isRequired,
user: React.PropTypes.object,
team: React.PropTypes.object,
show: React.PropTypes.bool.isRequired,
onModalSubmit: React.PropTypes.func,
onModalDismissed: React.PropTypes.func
};
export default injectIntl(ResetPasswordModal);

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

@@ -1,6 +1,8 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {FormattedMessage} from 'mm-intl';
var Modal = ReactBootstrap.Modal;
export default class SelectTeamModal extends React.Component {
@@ -45,7 +47,12 @@ export default class SelectTeamModal extends React.Component {
onHide={this.doCancel}
>
<Modal.Header closeButton={true}>
<Modal.Title>{'Select Team'}</Modal.Title>
<Modal.Title>
<FormattedMessage
id='admin.select_team.selectTeam'
defaultMessage='Select Team'
/>
</Modal.Title>
</Modal.Header>
<form
role='form'
@@ -70,7 +77,10 @@ export default class SelectTeamModal extends React.Component {
className='btn btn-default'
onClick={this.doCancel}
>
{'Close'}
<FormattedMessage
id='admin.select_team.close'
defaultMessage='Close'
/>
</button>
<button
onClick={this.doSubmit}
@@ -78,7 +88,10 @@ export default class SelectTeamModal extends React.Component {
className='btn btn-primary'
tabIndex='2'
>
{'Select'}
<FormattedMessage
id='admin.select_team.select'
defaultMessage='Select'
/>
</button>
</Modal.Footer>
</form>
@@ -96,4 +109,4 @@ SelectTeamModal.propTypes = {
show: React.PropTypes.bool.isRequired,
onModalSubmit: React.PropTypes.func,
onModalDismissed: React.PropTypes.func
};
};

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

@@ -4,11 +4,40 @@
import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const DefaultSessionLength = 30;
const DefaultMaximumLoginAttempts = 10;
const DefaultSessionCacheInMinutes = 10;
export default class ServiceSettings extends React.Component {
var holders = defineMessages({
listenExample: {
id: 'admin.service.listenExample',
defaultMessage: 'Ex ":8065"'
},
attemptExample: {
id: 'admin.service.attemptExample',
defaultMessage: 'Ex "10"'
},
segmentExample: {
id: 'admin.service.segmentExample',
defaultMessage: 'Ex "g3fgGOXJAQ43QV7rAh6iwQCkV4cA1Gs"'
},
googleExample: {
id: 'admin.service.googleExample',
defaultMessage: 'Ex "7rAh6iwQCkV4cA1Gsg3fgGOXJAQ43QV"'
},
sessionDaysEx: {
id: 'admin.service.sessionDaysEx',
defaultMessage: 'Ex "30"'
},
saving: {
id: 'admin.service.saving',
defaultMessage: 'Saving Config...'
}
});
class ServiceSettings extends React.Component {
constructor(props) {
super(props);
@@ -120,6 +149,7 @@ export default class ServiceSettings extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
var serverError = '';
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
@@ -133,7 +163,12 @@ export default class ServiceSettings extends React.Component {
return (
<div className='wrapper--fixed'>
<h3>{'Service Settings'}</h3>
<h3>
<FormattedMessage
id='admin.service.title'
defaultMessage='Service Settings'
/>
</h3>
<form
className='form-horizontal'
role='form'
@@ -144,7 +179,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='ListenAddress'
>
{'Listen Address:'}
<FormattedMessage
id='admin.service.listenAddress'
defaultMessage='Listen Address:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -152,11 +190,16 @@ export default class ServiceSettings extends React.Component {
className='form-control'
id='ListenAddress'
ref='ListenAddress'
placeholder='Ex ":8065"'
placeholder={formatMessage(holders.listenExample)}
defaultValue={this.props.config.ServiceSettings.ListenAddress}
onChange={this.handleChange}
/>
<p className='help-text'>{'The address to which to bind and listen. Entering ":8065" will bind to all interfaces or you can choose one like "127.0.0.1:8065". Changing this will require a server restart before taking effect.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.listenDescription'
defaultMessage='The address to which to bind and listen. Entering ":8065" will bind to all interfaces or you can choose one like "127.0.0.1:8065". Changing this will require a server restart before taking effect.'
/>
</p>
</div>
</div>
@@ -165,7 +208,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='MaximumLoginAttempts'
>
{'Maximum Login Attempts:'}
<FormattedMessage
id='admin.service.attemptTitle'
defaultMessage='Maximum Login Attempts:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -173,11 +219,16 @@ export default class ServiceSettings extends React.Component {
className='form-control'
id='MaximumLoginAttempts'
ref='MaximumLoginAttempts'
placeholder='Ex "10"'
placeholder={formatMessage(holders.attemptExample)}
defaultValue={this.props.config.ServiceSettings.MaximumLoginAttempts}
onChange={this.handleChange}
/>
<p className='help-text'>{'Login attempts allowed before user is locked out and required to reset password via email.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.attemptDescription'
defaultMessage='Login attempts allowed before user is locked out and required to reset password via email.'
/>
</p>
</div>
</div>
@@ -186,7 +237,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='SegmentDeveloperKey'
>
{'Segment Developer Key:'}
<FormattedMessage
id='admin.service.segmentTitle'
defaultMessage='Segment Developer Key:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -194,11 +248,16 @@ export default class ServiceSettings extends React.Component {
className='form-control'
id='SegmentDeveloperKey'
ref='SegmentDeveloperKey'
placeholder='Ex "g3fgGOXJAQ43QV7rAh6iwQCkV4cA1Gs"'
placeholder={formatMessage(holders.segmentExample)}
defaultValue={this.props.config.ServiceSettings.SegmentDeveloperKey}
onChange={this.handleChange}
/>
<p className='help-text'>{'For users running a SaaS services, sign up for a key at Segment.com to track metrics.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.segmentDescription'
defaultMessage='For users running a SaaS services, sign up for a key at Segment.com to track metrics.'
/>
</p>
</div>
</div>
@@ -207,7 +266,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='GoogleDeveloperKey'
>
{'Google Developer Key:'}
<FormattedMessage
id='admin.service.googleTitle'
defaultMessage='Google Developer Key:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -215,19 +277,17 @@ export default class ServiceSettings extends React.Component {
className='form-control'
id='GoogleDeveloperKey'
ref='GoogleDeveloperKey'
placeholder='Ex "7rAh6iwQCkV4cA1Gsg3fgGOXJAQ43QV"'
placeholder={formatMessage(holders.googleExample)}
defaultValue={this.props.config.ServiceSettings.GoogleDeveloperKey}
onChange={this.handleChange}
/>
<p className='help-text'>
{'Set this key to enable embedding of YouTube video previews based on hyperlinks appearing in messages or comments. Instructions to obtain a key available at '}
<a
href='https://www.youtube.com/watch?v=Im69kzhpR3I'
target='_blank'
>
{'https://www.youtube.com/watch?v=Im69kzhpR3I'}
</a>
{'. Leaving the field blank disables the automatic generation of YouTube video previews from links.'}
<FormattedHTMLMessage
id='admin.service.googleDescription'
defaultMessage='Set this key to enable embedding of YouTube video previews based on hyperlinks appearing in messages or comments. Instructions to obtain a key available at
<a href="https://www.youtube.com/watch?v=Im69kzhpR3I" target="_blank">https://www.youtube.com/watch?v=Im69kzhpR3I</a>.
Leaving the field blank disables the automatic generation of YouTube video previews from links.'
/>
</p>
</div>
</div>
@@ -237,7 +297,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='EnableIncomingWebhooks'
>
{'Enable Incoming Webhooks: '}
<FormattedMessage
id='admin.service.webhooksTitle'
defaultMessage='Enable Incoming Webhooks: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -249,7 +312,10 @@ export default class ServiceSettings extends React.Component {
defaultChecked={this.props.config.ServiceSettings.EnableIncomingWebhooks}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.service.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -259,9 +325,17 @@ export default class ServiceSettings extends React.Component {
defaultChecked={!this.props.config.ServiceSettings.EnableIncomingWebhooks}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.service.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When true, incoming webhooks will be allowed. To help combat phishing attacks, all posts from webhooks will be labelled by a BOT tag.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.webhooksDescription'
defaultMessage='When true, incoming webhooks will be allowed. To help combat phishing attacks, all posts from webhooks will be labelled by a BOT tag.'
/>
</p>
</div>
</div>
@@ -270,7 +344,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='EnableOutgoingWebhooks'
>
{'Enable Outgoing Webhooks: '}
<FormattedMessage
id='admin.service.outWebhooksTitle'
defaultMessage='Enable Outgoing Webhooks: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -282,7 +359,10 @@ export default class ServiceSettings extends React.Component {
defaultChecked={this.props.config.ServiceSettings.EnableOutgoingWebhooks}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.service.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -292,9 +372,17 @@ export default class ServiceSettings extends React.Component {
defaultChecked={!this.props.config.ServiceSettings.EnableOutgoingWebhooks}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.service.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When true, outgoing webhooks will be allowed.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.outWebhooksDesc'
defaultMessage='When true, outgoing webhooks will be allowed.'
/>
</p>
</div>
</div>
@@ -303,7 +391,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='EnablePostUsernameOverride'
>
{'Enable Overriding Usernames from Webhooks: '}
<FormattedMessage
id='admin.service.overrideTitle'
defaultMessage='Enable Overriding Usernames from Webhooks: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -315,7 +406,10 @@ export default class ServiceSettings extends React.Component {
defaultChecked={this.props.config.ServiceSettings.EnablePostUsernameOverride}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.service.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -325,9 +419,17 @@ export default class ServiceSettings extends React.Component {
defaultChecked={!this.props.config.ServiceSettings.EnablePostUsernameOverride}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.service.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When true, webhooks will be allowed to change the username they are posting as. Note, combined with allowing icon overriding, this could open users up to phishing attacks.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.overrideDescription'
defaultMessage='When true, webhooks will be allowed to change the username they are posting as. Note, combined with allowing icon overriding, this could open users up to phishing attacks.'
/>
</p>
</div>
</div>
@@ -336,7 +438,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='EnablePostIconOverride'
>
{'Enable Overriding Icon from Webhooks: '}
<FormattedMessage
id='admin.service.iconTitle'
defaultMessage='Enable Overriding Icon from Webhooks: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -348,7 +453,10 @@ export default class ServiceSettings extends React.Component {
defaultChecked={this.props.config.ServiceSettings.EnablePostIconOverride}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.service.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -358,9 +466,17 @@ export default class ServiceSettings extends React.Component {
defaultChecked={!this.props.config.ServiceSettings.EnablePostIconOverride}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.service.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When true, webhooks will be allowed to change the icon they post with. Note, combined with allowing username overriding, this could open users up to phishing attacks.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.iconDescription'
defaultMessage='When true, webhooks will be allowed to change the icon they post with. Note, combined with allowing username overriding, this could open users up to phishing attacks.'
/>
</p>
</div>
</div>
@@ -369,7 +485,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='EnableTesting'
>
{'Enable Testing: '}
<FormattedMessage
id='admin.service.testingTitle'
defaultMessage='Enable Testing: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -381,7 +500,10 @@ export default class ServiceSettings extends React.Component {
defaultChecked={this.props.config.ServiceSettings.EnableTesting}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.service.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -391,9 +513,17 @@ export default class ServiceSettings extends React.Component {
defaultChecked={!this.props.config.ServiceSettings.EnableTesting}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.service.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'(Developer Option) When true, /loadtest slash command is enabled to load test accounts and test data. Changing this will require a server restart before taking effect.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.testingDescription'
defaultMessage='(Developer Option) When true, /loadtest slash command is enabled to load test accounts and test data. Changing this will require a server restart before taking effect.'
/>
</p>
</div>
</div>
@@ -402,7 +532,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='EnableDeveloper'
>
{'Enable Developer Mode: '}
<FormattedMessage
id='admin.service.developerTitle'
defaultMessage='Enable Developer Mode: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -414,7 +547,10 @@ export default class ServiceSettings extends React.Component {
defaultChecked={this.props.config.ServiceSettings.EnableDeveloper}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.service.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -424,9 +560,17 @@ export default class ServiceSettings extends React.Component {
defaultChecked={!this.props.config.ServiceSettings.EnableDeveloper}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.service.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'(Developer Option) When true, extra information around errors will be displayed in the UI.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.developerDesc'
defaultMessage='(Developer Option) When true, extra information around errors will be displayed in the UI.'
/>
</p>
</div>
</div>
@@ -435,7 +579,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='EnableSecurityFixAlert'
>
{'Enable Security Alerts: '}
<FormattedMessage
id='admin.service.securityTitle'
defaultMessage='Enable Security Alerts: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -447,7 +594,10 @@ export default class ServiceSettings extends React.Component {
defaultChecked={this.props.config.ServiceSettings.EnableSecurityFixAlert}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.service.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -457,9 +607,17 @@ export default class ServiceSettings extends React.Component {
defaultChecked={!this.props.config.ServiceSettings.EnableSecurityFixAlert}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.service.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When true, System Administrators are notified by email if a relevant security fix alert has been announced in the last 12 hours. Requires email to be enabled.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.securityDesc'
defaultMessage='When true, System Administrators are notified by email if a relevant security fix alert has been announced in the last 12 hours. Requires email to be enabled.'
/>
</p>
</div>
</div>
@@ -468,7 +626,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='SessionLengthWebInDays'
>
{'Session Length for Web in Days:'}
<FormattedMessage
id='admin.service.webSessionDays'
defaultMessage='Session Length for Web in Days:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -476,11 +637,16 @@ export default class ServiceSettings extends React.Component {
className='form-control'
id='SessionLengthWebInDays'
ref='SessionLengthWebInDays'
placeholder='Ex "30"'
placeholder={formatMessage(holders.sessionDaysEx)}
defaultValue={this.props.config.ServiceSettings.SessionLengthWebInDays}
onChange={this.handleChange}
/>
<p className='help-text'>{'The web session will expire after the number of days specified and will require a user to login again.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.webSessionDaysDesc'
defaultMessage='The web session will expire after the number of days specified and will require a user to login again.'
/>
</p>
</div>
</div>
@@ -489,7 +655,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='SessionLengthMobileInDays'
>
{'Session Length for Mobile Device in Days:'}
<FormattedMessage
id='admin.service.mobileSessionDays'
defaultMessage='Session Length for Mobile Device in Days:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -497,11 +666,16 @@ export default class ServiceSettings extends React.Component {
className='form-control'
id='SessionLengthMobileInDays'
ref='SessionLengthMobileInDays'
placeholder='Ex "30"'
placeholder={formatMessage(holders.sessionDaysEx)}
defaultValue={this.props.config.ServiceSettings.SessionLengthMobileInDays}
onChange={this.handleChange}
/>
<p className='help-text'>{'The native mobile session will expire after the number of days specified and will require a user to login again.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.mobileSessionDaysDesc'
defaultMessage='The native mobile session will expire after the number of days specified and will require a user to login again.'
/>
</p>
</div>
</div>
@@ -510,7 +684,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='SessionLengthSSOInDays'
>
{'Session Length for SSO in Days:'}
<FormattedMessage
id='admin.service.ssoSessionDays'
defaultMessage='Session Length for SSO in Days:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -518,11 +695,16 @@ export default class ServiceSettings extends React.Component {
className='form-control'
id='SessionLengthSSOInDays'
ref='SessionLengthSSOInDays'
placeholder='Ex "30"'
placeholder={formatMessage(holders.sessionDaysEx)}
defaultValue={this.props.config.ServiceSettings.SessionLengthSSOInDays}
onChange={this.handleChange}
/>
<p className='help-text'>{'The SSO session will expire after the number of days specified and will require a user to login again.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.ssoSessionDaysDesc'
defaultMessage='The SSO session will expire after the number of days specified and will require a user to login again.'
/>
</p>
</div>
</div>
@@ -531,7 +713,10 @@ export default class ServiceSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='SessionCacheInMinutes'
>
{'Session Cache in Minutes:'}
<FormattedMessage
id='admin.service.sessionCache'
defaultMessage='Session Cache in Minutes:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -539,11 +724,16 @@ export default class ServiceSettings extends React.Component {
className='form-control'
id='SessionCacheInMinutes'
ref='SessionCacheInMinutes'
placeholder='Ex "30"'
placeholder={formatMessage(holders.sessionDaysEx)}
defaultValue={this.props.config.ServiceSettings.SessionCacheInMinutes}
onChange={this.handleChange}
/>
<p className='help-text'>{'The number of minutes to cache a session in memory.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.service.sessionCacheDesc'
defaultMessage='The number of minutes to cache a session in memory.'
/>
</p>
</div>
</div>
@@ -556,9 +746,12 @@ export default class ServiceSettings extends React.Component {
className={saveClass}
onClick={this.handleSubmit}
id='save-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Saving Config...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
>
{'Save'}
<FormattedMessage
id='admin.service.save'
defaultMessage='Save'
/>
</button>
</div>
</div>
@@ -603,5 +796,8 @@ export default class ServiceSettings extends React.Component {
// </div>
ServiceSettings.propTypes = {
intl: intlShape.isRequired,
config: React.PropTypes.object
};
export default injectIntl(ServiceSettings);

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

@@ -5,7 +5,32 @@ import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import crypto from 'crypto';
export default class SqlSettings extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
warning: {
id: 'admin.sql.warning',
defaultMessage: 'Warning: re-generating this salt may cause some columns in the database to return empty results.'
},
maxConnectionsExample: {
id: 'admin.sql.maxConnectionsExample',
defaultMessage: 'Ex "10"'
},
maxOpenExample: {
id: 'admin.sql.maxOpenExample',
defaultMessage: 'Ex "10"'
},
keyExample: {
id: 'admin.sql.keyExample',
defaultMessage: 'Ex "gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6"'
},
saving: {
id: 'admin.sql.saving',
defaultMessage: 'Saving Config...'
}
});
class SqlSettings extends React.Component {
constructor(props) {
super(props);
@@ -74,7 +99,7 @@ export default class SqlSettings extends React.Component {
handleGenerate(e) {
e.preventDefault();
var cfm = global.window.confirm('Warning: re-generating this salt may cause some columns in the database to return empty results.');
var cfm = global.window.confirm(this.props.intl.formatMessage(holders.warning));
if (cfm === false) {
return;
}
@@ -85,6 +110,7 @@ export default class SqlSettings extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
var serverError = '';
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
@@ -111,12 +137,27 @@ export default class SqlSettings extends React.Component {
<div className='banner'>
<div className='banner__content'>
<h4 className='banner__heading'>{'Note:'}</h4>
<p>{'Changing properties in this section will require a server restart before taking effect.'}</p>
<h4 className='banner__heading'>
<FormattedMessage
id='admin.sql.noteTitle'
defaultMessage='Note:'
/>
</h4>
<p>
<FormattedMessage
id='admin.sql.noteDescription'
defaultMessage='Changing properties in this section will require a server restart before taking effect.'
/>
</p>
</div>
</div>
<h3>{'SQL Settings'}</h3>
<h3>
<FormattedMessage
id='admin.sql.title'
defaultMessage='SQL Settings'
/>
</h3>
<form
className='form-horizontal'
role='form'
@@ -127,7 +168,10 @@ export default class SqlSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='DriverName'
>
{'Driver Name:'}
<FormattedMessage
id='admin.sql.driverName'
defaultMessage='Driver Name:'
/>
</label>
<div className='col-sm-8'>
<p className='help-text'>{this.props.config.SqlSettings.DriverName}</p>
@@ -139,7 +183,10 @@ export default class SqlSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='DataSource'
>
{'Data Source:'}
<FormattedMessage
id='admin.sql.dataSource'
defaultMessage='Data Source:'
/>
</label>
<div className='col-sm-8'>
<p className='help-text'>{dataSource}</p>
@@ -151,7 +198,10 @@ export default class SqlSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='DataSourceReplicas'
>
{'Data Source Replicas:'}
<FormattedMessage
id='admin.sql.replicas'
defaultMessage='Data Source Replicas:'
/>
</label>
<div className='col-sm-8'>
<p className='help-text'>{dataSourceReplicas}</p>
@@ -163,7 +213,10 @@ export default class SqlSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='MaxIdleConns'
>
{'Maximum Idle Connections:'}
<FormattedMessage
id='admin.sql.maxConnectionsTitle'
defaultMessage='Maximum Idle Connections:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -171,11 +224,16 @@ export default class SqlSettings extends React.Component {
className='form-control'
id='MaxIdleConns'
ref='MaxIdleConns'
placeholder='Ex "10"'
placeholder={formatMessage(holders.maxConnectionsExample)}
defaultValue={this.props.config.SqlSettings.MaxIdleConns}
onChange={this.handleChange}
/>
<p className='help-text'>{'Maximum number of idle connections held open to the database.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.sql.maxConnectionsDescription'
defaultMessage='Maximum number of idle connections held open to the database.'
/>
</p>
</div>
</div>
@@ -184,7 +242,10 @@ export default class SqlSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='MaxOpenConns'
>
{'Maximum Open Connections:'}
<FormattedMessage
id='admin.sql.maxOpenTitle'
defaultMessage='Maximum Open Connections:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -192,11 +253,16 @@ export default class SqlSettings extends React.Component {
className='form-control'
id='MaxOpenConns'
ref='MaxOpenConns'
placeholder='Ex "10"'
placeholder={formatMessage(holders.maxOpenExample)}
defaultValue={this.props.config.SqlSettings.MaxOpenConns}
onChange={this.handleChange}
/>
<p className='help-text'>{'Maximum number of open connections held open to the database.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.sql.maxOpenDescription'
defaultMessage='Maximum number of open connections held open to the database.'
/>
</p>
</div>
</div>
@@ -205,7 +271,10 @@ export default class SqlSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='AtRestEncryptKey'
>
{'At Rest Encrypt Key:'}
<FormattedMessage
id='admin.sql.keyTitle'
defaultMessage='At Rest Encrypt Key:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -213,17 +282,25 @@ export default class SqlSettings extends React.Component {
className='form-control'
id='AtRestEncryptKey'
ref='AtRestEncryptKey'
placeholder='Ex "gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6"'
placeholder={formatMessage(holders.keyExample)}
defaultValue={this.props.config.SqlSettings.AtRestEncryptKey}
onChange={this.handleChange}
/>
<p className='help-text'>{'32-character salt available to encrypt and decrypt sensitive fields in database.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.sql.keyDescription'
defaultMessage='32-character salt available to encrypt and decrypt sensitive fields in database.'
/>
</p>
<div className='help-text'>
<button
className='btn btn-default'
onClick={this.handleGenerate}
>
{'Re-Generate'}
<FormattedMessage
id='admin.sql.regenerate'
defaultMessage='Re-Generate'
/>
</button>
</div>
</div>
@@ -234,7 +311,10 @@ export default class SqlSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='Trace'
>
{'Trace: '}
<FormattedMessage
id='admin.sql.traceTitle'
defaultMessage='Trace: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -246,7 +326,10 @@ export default class SqlSettings extends React.Component {
defaultChecked={this.props.config.SqlSettings.Trace}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.sql.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -256,9 +339,17 @@ export default class SqlSettings extends React.Component {
defaultChecked={!this.props.config.SqlSettings.Trace}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.sql.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'(Development Mode) When true, executing SQL statements are written to the log.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.sql.traceDescription'
defaultMessage='(Development Mode) When true, executing SQL statements are written to the log.'
/>
</p>
</div>
</div>
@@ -271,9 +362,12 @@ export default class SqlSettings extends React.Component {
className={saveClass}
onClick={this.handleSubmit}
id='save-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Saving Config...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
>
{'Save'}
<FormattedMessage
id='admin.sql.save'
defaultMessage='Save'
/>
</button>
</div>
</div>
@@ -285,5 +379,8 @@ export default class SqlSettings extends React.Component {
}
SqlSettings.propTypes = {
intl: intlShape.isRequired,
config: React.PropTypes.object
};
export default injectIntl(SqlSettings);

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

@@ -4,7 +4,24 @@
import Analytics from './analytics.jsx';
import * as Client from '../../utils/client.jsx';
export default class SystemAnalytics extends React.Component {
import {injectIntl, intlShape, defineMessages} from 'mm-intl';
const labels = defineMessages({
totalPosts: {
id: 'admin.system_analytics.totalPosts',
defaultMessage: 'Total Posts'
},
activeUsers: {
id: 'admin.system_analytics.activeUsers',
defaultMessage: 'Active Users With Posts'
},
title: {
id: 'admin.system_analytics.title',
defaultMessage: 'the System'
}
});
class SystemAnalytics extends React.Component {
constructor(props) {
super(props);
@@ -29,6 +46,7 @@ export default class SystemAnalytics extends React.Component {
}
getData() { // should be moved to an action creator eventually
const {formatMessage} = this.props.intl;
Client.getSystemAnalytics(
'standard',
(data) => {
@@ -63,7 +81,7 @@ export default class SystemAnalytics extends React.Component {
var chartData = {
labels: [],
datasets: [{
label: 'Total Posts',
label: formatMessage(labels.totalPosts),
fillColor: 'rgba(151,187,205,0.2)',
strokeColor: 'rgba(151,187,205,1)',
pointColor: 'rgba(151,187,205,1)',
@@ -97,7 +115,7 @@ export default class SystemAnalytics extends React.Component {
var chartData = {
labels: [],
datasets: [{
label: 'Active Users With Posts',
label: formatMessage(labels.activeUsers),
fillColor: 'rgba(151,187,205,0.2)',
strokeColor: 'rgba(151,187,205,1)',
pointColor: 'rgba(151,187,205,1)',
@@ -142,7 +160,7 @@ export default class SystemAnalytics extends React.Component {
return (
<div>
<Analytics
title={'the System'}
title={this.props.intl.formatMessage(labels.title)}
channelOpenCount={this.state.channel_open_count}
channelPrivateCount={this.state.channel_private_count}
postCount={this.state.post_count}
@@ -157,5 +175,8 @@ export default class SystemAnalytics extends React.Component {
}
SystemAnalytics.propTypes = {
intl: intlShape.isRequired,
team: React.PropTypes.object
};
export default injectIntl(SystemAnalytics);

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

@@ -4,7 +4,20 @@
import Analytics from './analytics.jsx';
import * as Client from '../../utils/client.jsx';
export default class TeamAnalytics extends React.Component {
import {injectIntl, intlShape, defineMessages} from 'mm-intl';
const labels = defineMessages({
totalPosts: {
id: 'admin.team_analytics.totalPosts',
defaultMessage: 'Total Posts'
},
activeUsers: {
id: 'admin.team_analytics.activeUsers',
defaultMessage: 'Active Users With Posts'
}
});
class TeamAnalytics extends React.Component {
constructor(props) {
super(props);
@@ -29,6 +42,7 @@ export default class TeamAnalytics extends React.Component {
}
getData(teamId) { // should be moved to an action creator eventually
const {formatMessage} = this.props.intl;
Client.getTeamAnalytics(
teamId,
'standard',
@@ -65,7 +79,7 @@ export default class TeamAnalytics extends React.Component {
var chartData = {
labels: [],
datasets: [{
label: 'Total Posts',
label: formatMessage(labels.totalPosts),
fillColor: 'rgba(151,187,205,0.2)',
strokeColor: 'rgba(151,187,205,1)',
pointColor: 'rgba(151,187,205,1)',
@@ -100,7 +114,7 @@ export default class TeamAnalytics extends React.Component {
var chartData = {
labels: [],
datasets: [{
label: 'Active Users With Posts',
label: formatMessage(labels.activeUsers),
fillColor: 'rgba(151,187,205,0.2)',
strokeColor: 'rgba(151,187,205,1)',
pointColor: 'rgba(151,187,205,1)',
@@ -231,5 +245,8 @@ export default class TeamAnalytics extends React.Component {
}
TeamAnalytics.propTypes = {
intl: intlShape.isRequired,
team: React.PropTypes.object
};
export default injectIntl(TeamAnalytics);

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

@@ -4,7 +4,28 @@
import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
export default class TeamSettings extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
siteNameExample: {
id: 'admin.team.siteNameExample',
defaultMessage: 'Ex "Mattermost"'
},
maxUsersExample: {
id: 'admin.team.maxUsersExample',
defaultMessage: 'Ex "25"'
},
restrictExample: {
id: 'admin.team.restrictExample',
defaultMessage: 'Ex "corp.mattermost.com, mattermost.org"'
},
saving: {
id: 'admin.team.saving',
defaultMessage: 'Saving Config...'
}
});
class TeamSettings extends React.Component {
constructor(props) {
super(props);
@@ -62,6 +83,7 @@ export default class TeamSettings extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
var serverError = '';
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
@@ -75,7 +97,12 @@ export default class TeamSettings extends React.Component {
return (
<div className='wrapper--fixed'>
<h3>{'Team Settings'}</h3>
<h3>
<FormattedMessage
id='admin.team.title'
defaultMessage='Team Settings'
/>
</h3>
<form
className='form-horizontal'
role='form'
@@ -86,7 +113,10 @@ export default class TeamSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='SiteName'
>
{'Site Name:'}
<FormattedMessage
id='admin.team.siteNameTitle'
defaultMessage='Site Name:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -94,11 +124,16 @@ export default class TeamSettings extends React.Component {
className='form-control'
id='SiteName'
ref='SiteName'
placeholder='Ex "Mattermost"'
placeholder={formatMessage(holders.siteNameExample)}
defaultValue={this.props.config.TeamSettings.SiteName}
onChange={this.handleChange}
/>
<p className='help-text'>{'Name of service shown in login screens and UI.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.team.siteNameDescription'
defaultMessage='Name of service shown in login screens and UI.'
/>
</p>
</div>
</div>
@@ -107,7 +142,10 @@ export default class TeamSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='MaxUsersPerTeam'
>
{'Max Users Per Team:'}
<FormattedMessage
id='admin.team.maxUsersTitle'
defaultMessage='Max Users Per Team:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -115,11 +153,16 @@ export default class TeamSettings extends React.Component {
className='form-control'
id='MaxUsersPerTeam'
ref='MaxUsersPerTeam'
placeholder='Ex "25"'
placeholder={formatMessage(holders.maxUsersExample)}
defaultValue={this.props.config.TeamSettings.MaxUsersPerTeam}
onChange={this.handleChange}
/>
<p className='help-text'>{'Maximum total number of users per team, including both active and inactive users.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.team.maxUsersDescription'
defaultMessage='Maximum total number of users per team, including both active and inactive users.'
/>
</p>
</div>
</div>
@@ -128,7 +171,10 @@ export default class TeamSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='EnableTeamCreation'
>
{'Enable Team Creation: '}
<FormattedMessage
id='admin.team.teamCreationTitle'
defaultMessage='Enable Team Creation: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -140,7 +186,10 @@ export default class TeamSettings extends React.Component {
defaultChecked={this.props.config.TeamSettings.EnableTeamCreation}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.team.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -150,9 +199,17 @@ export default class TeamSettings extends React.Component {
defaultChecked={!this.props.config.TeamSettings.EnableTeamCreation}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.team.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When false, the ability to create teams is disabled. The create team button displays error when pressed.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.team.teamCreationDescription'
defaultMessage='When false, the ability to create teams is disabled. The create team button displays error when pressed.'
/>
</p>
</div>
</div>
@@ -161,7 +218,10 @@ export default class TeamSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='EnableUserCreation'
>
{'Enable User Creation: '}
<FormattedMessage
id='admin.team.userCreationTitle'
defaultMessage='Enable User Creation: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -173,7 +233,10 @@ export default class TeamSettings extends React.Component {
defaultChecked={this.props.config.TeamSettings.EnableUserCreation}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.team.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -183,9 +246,17 @@ export default class TeamSettings extends React.Component {
defaultChecked={!this.props.config.TeamSettings.EnableUserCreation}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.team.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When false, the ability to create accounts is disabled. The create account button displays error when pressed.'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.team.userCreationDescription'
defaultMessage='When false, the ability to create accounts is disabled. The create account button displays error when pressed.'
/>
</p>
</div>
</div>
@@ -194,7 +265,10 @@ export default class TeamSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='RestrictCreationToDomains'
>
{'Restrict Creation To Domains:'}
<FormattedMessage
id='admin.team.restrictTitle'
defaultMessage='Restrict Creation To Domains:'
/>
</label>
<div className='col-sm-8'>
<input
@@ -202,11 +276,16 @@ export default class TeamSettings extends React.Component {
className='form-control'
id='RestrictCreationToDomains'
ref='RestrictCreationToDomains'
placeholder='Ex "corp.mattermost.com, mattermost.org"'
placeholder={formatMessage(holders.restrictExample)}
defaultValue={this.props.config.TeamSettings.RestrictCreationToDomains}
onChange={this.handleChange}
/>
<p className='help-text'>{'Teams and user accounts can only be created from a specific domain (e.g. "mattermost.org") or list of comma-separated domains (e.g. "corp.mattermost.com, mattermost.org").'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.team.restrictDescription'
defaultMessage='Teams and user accounts can only be created from a specific domain (e.g. "mattermost.org") or list of comma-separated domains (e.g. "corp.mattermost.com, mattermost.org").'
/>
</p>
</div>
</div>
@@ -215,7 +294,10 @@ export default class TeamSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='RestrictTeamNames'
>
{'Restrict Team Names: '}
<FormattedMessage
id='admin.team.restrictNameTitle'
defaultMessage='Restrict Team Names: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -227,7 +309,10 @@ export default class TeamSettings extends React.Component {
defaultChecked={this.props.config.TeamSettings.RestrictTeamNames}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.team.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -237,9 +322,17 @@ export default class TeamSettings extends React.Component {
defaultChecked={!this.props.config.TeamSettings.RestrictTeamNames}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.team.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When true, You cannot create a team name with reserved words like www, admin, support, test, channel, etc'}</p>
<p className='help-text'>
<FormattedMessage
id='admin.team.restrictNameDesc'
defaultMessage='When true, You cannot create a team name with reserved words like www, admin, support, test, channel, etc'
/>
</p>
</div>
</div>
@@ -248,7 +341,10 @@ export default class TeamSettings extends React.Component {
className='control-label col-sm-4'
htmlFor='EnableTeamListing'
>
{'Enable Team Directory: '}
<FormattedMessage
id='admin.team.dirTitle'
defaultMessage='Enable Team Directory: '
/>
</label>
<div className='col-sm-8'>
<label className='radio-inline'>
@@ -260,7 +356,10 @@ export default class TeamSettings extends React.Component {
defaultChecked={this.props.config.TeamSettings.EnableTeamListing}
onChange={this.handleChange}
/>
{'true'}
<FormattedMessage
id='admin.team.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -270,9 +369,17 @@ export default class TeamSettings extends React.Component {
defaultChecked={!this.props.config.TeamSettings.EnableTeamListing}
onChange={this.handleChange}
/>
{'false'}
<FormattedMessage
id='admin.team.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>{'When true, teams that are configured to show in team directory will show on main page inplace of creating a new team.'}</p>
<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>
@@ -285,9 +392,12 @@ export default class TeamSettings extends React.Component {
className={saveClass}
onClick={this.handleSubmit}
id='save-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Saving Config...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
>
{'Save'}
<FormattedMessage
id='admin.team.save'
defaultMessage='Save'
/>
</button>
</div>
</div>
@@ -299,5 +409,8 @@ export default class TeamSettings extends React.Component {
}
TeamSettings.propTypes = {
intl: intlShape.isRequired,
config: React.PropTypes.object
};
export default injectIntl(TeamSettings);

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

@@ -6,6 +6,8 @@ import LoadingScreen from '../loading_screen.jsx';
import UserItem from './user_item.jsx';
import ResetPasswordModal from './reset_password_modal.jsx';
import {FormattedMessage} from 'mm-intl';
export default class UserList extends React.Component {
constructor(props) {
super(props);
@@ -122,7 +124,15 @@ export default class UserList extends React.Component {
if (this.state.users == null) {
return (
<div className='wrapper--fixed'>
<h3>{'Users for ' + this.props.team.name}</h3>
<h3>
<FormattedMessage
id='admin.userList.title'
defaultMessage='Users for {team}'
values={{
team: this.props.team.name
}}
/>
</h3>
{serverError}
<LoadingScreen />
</div>
@@ -141,7 +151,16 @@ export default class UserList extends React.Component {
return (
<div className='wrapper--fixed'>
<h3>{'Users for ' + this.props.team.name + ' (' + this.state.users.length + ')'}</h3>
<h3>
<FormattedMessage
id='admin.userList.title2'
defaultMessage='Users for {team} ({count})'
values={{
team: this.props.team.name,
count: this.state.users.length
}}
/>
</h3>
{serverError}
<form
className='form-horizontal'

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

@@ -3,6 +3,30 @@
import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
import UserStore from '../../stores/user_store.jsx';
import ConfirmModal from '../confirm_modal.jsx';
import TeamStore from '../../stores/team_store.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
var holders = defineMessages({
confirmDemoteRoleTitle: {
id: 'admin.user_item.confirmDemoteRoleTitle',
defaultMessage: 'Confirm demotion from System Admin role'
},
confirmDemotion: {
id: 'admin.user_item.confirmDemotion',
defaultMessage: 'Confirm Demotion'
},
confirmDemoteDescription: {
id: 'admin.user_item.confirmDemoteDescription',
defaultMessage: 'If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you\'ll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command.'
},
confirmDemotionCmd: {
id: 'admin.user_item.confirmDemotionCmd',
defaultMessage: 'platform -assign_role -team_name="yourteam" -email="name@yourcompany.com" -role="system_admin"'
}
});
export default class UserItem extends React.Component {
constructor(props) {
@@ -14,25 +38,38 @@ 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.handleDemote = this.handleDemote.bind(this);
this.handleDemoteSubmit = this.handleDemoteSubmit.bind(this);
this.handleDemoteCancel = this.handleDemoteCancel.bind(this);
this.state = {};
this.state = {
serverError: null,
showDemoteModal: false,
user: null,
role: null
};
}
handleMakeMember(e) {
e.preventDefault();
const data = {
user_id: this.props.user.id,
new_roles: ''
};
const me = UserStore.getCurrentUser();
if (this.props.user.id === me.id) {
this.handleDemote(this.props.user, '');
} else {
const data = {
user_id: this.props.user.id,
new_roles: ''
};
Client.updateRoles(data,
() => {
this.props.refreshProfiles();
},
(err) => {
this.setState({serverError: err.message});
}
);
Client.updateRoles(data,
() => {
this.props.refreshProfiles();
},
(err) => {
this.setState({serverError: err.message});
}
);
}
}
handleMakeActive(e) {
@@ -61,19 +98,24 @@ export default class UserItem extends React.Component {
handleMakeAdmin(e) {
e.preventDefault();
const data = {
user_id: this.props.user.id,
new_roles: 'admin'
};
const me = UserStore.getCurrentUser();
if (this.props.user.id === me.id) {
this.handleDemote(this.props.user, 'admin');
} else {
const data = {
user_id: this.props.user.id,
new_roles: 'admin'
};
Client.updateRoles(data,
() => {
this.props.refreshProfiles();
},
(err) => {
this.setState({serverError: err.message});
}
);
Client.updateRoles(data,
() => {
this.props.refreshProfiles();
},
(err) => {
this.setState({serverError: err.message});
}
);
}
}
handleMakeSystemAdmin(e) {
@@ -98,6 +140,54 @@ export default class UserItem extends React.Component {
this.props.doPasswordReset(this.props.user);
}
handleDemote(user, role) {
this.setState({
serverError: this.state.serverError,
showDemoteModal: true,
user,
role
});
}
handleDemoteCancel() {
this.setState({
serverError: null,
showDemoteModal: false,
user: null,
role: null
});
}
handleDemoteSubmit() {
const data = {
user_id: this.props.user.id,
new_roles: this.state.role
};
Client.updateRoles(data,
() => {
this.setState({
serverError: null,
showDemoteModal: false,
user: null,
role: null
});
const teamUrl = TeamStore.getCurrentTeamUrl();
if (teamUrl) {
window.location.href = teamUrl;
} else {
window.location.href = '/';
}
},
(err) => {
this.setState({
serverError: err.message
});
}
);
}
render() {
let serverError = null;
if (this.state.serverError) {
@@ -109,12 +199,27 @@ export default class UserItem extends React.Component {
}
const user = this.props.user;
let currentRoles = 'Member';
let currentRoles = (
<FormattedMessage
id='admin.user_item.member'
defaultMessage='Member'
/>
);
if (user.roles.length > 0) {
if (Utils.isSystemAdmin(user.roles)) {
currentRoles = 'System Admin';
currentRoles = (
<FormattedMessage
id='admin.user_item.sysAdmin'
defaultMessage='System Admin'
/>
);
} else if (Utils.isAdmin(user.roles)) {
currentRoles = 'Team Admin';
currentRoles = (
<FormattedMessage
id='admin.user_item.teamAdmin'
defaultMessage='Team Admin'
/>
);
} else {
currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
}
@@ -128,7 +233,12 @@ export default class UserItem extends React.Component {
let showMakeNotActive = user.roles !== 'system_admin';
if (user.delete_at > 0) {
currentRoles = 'Inactive';
currentRoles = (
<FormattedMessage
id='admin.user_item.inactive'
defaultMessage='Inactive'
/>
);
showMakeMember = false;
showMakeAdmin = false;
showMakeSystemAdmin = false;
@@ -145,7 +255,10 @@ export default class UserItem extends React.Component {
href='#'
onClick={this.handleMakeSystemAdmin}
>
{'Make System Admin'}
<FormattedMessage
id='admin.user_item.makeSysAdmin'
defaultMessage='Make System Admin'
/>
</a>
</li>
);
@@ -160,7 +273,10 @@ export default class UserItem extends React.Component {
href='#'
onClick={this.handleMakeAdmin}
>
{'Make Team Admin'}
<FormattedMessage
id='admin.user_item.makeTeamAdmin'
defaultMessage='Make Team Admin'
/>
</a>
</li>
);
@@ -175,7 +291,10 @@ export default class UserItem extends React.Component {
href='#'
onClick={this.handleMakeMember}
>
{'Make Member'}
<FormattedMessage
id='admin.user_item.makeMember'
defaultMessage='Make Member'
/>
</a>
</li>
);
@@ -190,7 +309,10 @@ export default class UserItem extends React.Component {
href='#'
onClick={this.handleMakeActive}
>
{'Make Active'}
<FormattedMessage
id='admin.user_item.makeActive'
defaultMessage='Make Active'
/>
</a>
</li>
);
@@ -205,11 +327,29 @@ export default class UserItem extends React.Component {
href='#'
onClick={this.handleMakeNotActive}
>
{'Make Inactive'}
<FormattedMessage
id='admin.user_item.makeInactive'
defaultMessage='Make Inactive'
/>
</a>
</li>
);
}
const me = UserStore.getCurrentUser();
const {formatMessage} = this.props.intl;
let makeDemoteModal = null;
if (this.props.user.id === me.id) {
makeDemoteModal = (
<ConfirmModal
show={this.state.showDemoteModal}
title={formatMessage(holders.confirmDemoteRoleTitle)}
message={[formatMessage(holders.confirmDemoteDescription), React.createElement('br'), React.createElement('br'), formatMessage(holders.confirmDemotionCmd), serverError]}
confirm_button={formatMessage(holders.confirmDemotion)}
onConfirm={this.handleDemoteSubmit}
onCancel={this.handleDemoteCancel}
/>
);
}
return (
<tr>
@@ -248,11 +388,15 @@ export default class UserItem extends React.Component {
href='#'
onClick={this.handleResetPassword}
>
{'Reset Password'}
<FormattedMessage
id='admin.user_item.resetPwd'
defaultMessage='Reset Password'
/>
</a>
</li>
</ul>
</div>
{makeDemoteModal}
{serverError}
</td>
</tr>
@@ -261,7 +405,10 @@ export default class UserItem extends React.Component {
}
UserItem.propTypes = {
intl: intlShape.isRequired,
user: React.PropTypes.object.isRequired,
refreshProfiles: React.PropTypes.func.isRequired,
doPasswordReset: React.PropTypes.func.isRequired
};
export default injectIntl(UserItem);

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

@@ -3,6 +3,8 @@
import * as Client from '../utils/client.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
export default class Authorize extends React.Component {
constructor(props) {
super(props);
@@ -33,28 +35,67 @@ export default class Authorize extends React.Component {
}
render() {
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>
<br/>
<br/>
<label>{'Allow '}{this.props.appName}{' access?'}</label>
<br/>
<button
type='submit'
className='btn authorize-btn'
onClick={this.handleDeny}
>
{'Deny'}
</button>
<button
type='submit'
className='btn btn-primary authorize-btn'
onClick={this.handleAllow}
>
{'Allow'}
</button>
<div className='container-fluid'>
<div className='oauth-prompt'>
<div className='prompt__heading'>
<div className='prompt__app-icon'>
<img
src='/static/images/icon50x50.png'
width='50'
height='50'
alt=''
/>
</div>
<div className='text'>
<FormattedMessage
id='authorize.title'
defaultMessage='An application would like to connect to your {teamName} account'
values={{
teamName: this.props.teamName
}}
/>
</div>
</div>
<p>
<FormattedHTMLMessage
id='authorize.app'
defaultMessage='The app <strong>{appName}</strong> would like the ability to access and modify your basic information.'
values={{
appName: this.props.appName
}}
/>
</p>
<h2 className='prompt__allow'>
<FormattedHTMLMessage
id='authorize.access'
defaultMessage='Allow <strong>{appName}</strong> access?'
values={{
appName: this.props.appName
}}
/>
</h2>
<div className='prompt__buttons'>
<button
type='submit'
className='btn authorize-btn'
onClick={this.handleDeny}
>
<FormattedMessage
id='authorize.deny'
defaultMessage='Deny'
/>
</button>
<button
type='submit'
className='btn btn-primary authorize-btn'
onClick={this.handleAllow}
>
<FormattedMessage
id='authorize.allow'
defaultMessage='Allow'
/>
</button>
</div>
</div>
</div>
);

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

@@ -66,11 +66,9 @@ export default class CenterPanel extends React.Component {
createPost = (
<div
id='archive-link-home'
onClick={handleClick}
>
<a
href=''
onClick={handleClick}
>
<a href=''>
{'You are viewing the Archives. Click here to jump to recent messages. '}
{<i className='fa fa-arrow-down'></i>}
</a>

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

@@ -4,6 +4,8 @@
var Modal = ReactBootstrap.Modal;
import * as Utils from '../utils/utils.jsx';
import {FormattedMessage} from 'mm-intl';
export default class ChangeUrlModal extends React.Component {
constructor(props) {
super(props);
@@ -39,21 +41,58 @@ export default class ChangeUrlModal extends React.Component {
getURLError(url) {
let error = []; //eslint-disable-line prefer-const
if (url.length < 2) {
error.push(<span key='error1'>{'Must be longer than two characters'}<br/></span>);
error.push(
<span key='error1'>
<FormattedMessage
id='change_url.longer'
defaultMessage='Must be longer than two characters'
/>
<br/>
</span>
);
}
if (url.charAt(0) === '-' || url.charAt(0) === '_') {
error.push(<span key='error2'>{'Must start with a letter or number'}<br/></span>);
error.push(
<span key='error2'>
<FormattedMessage
id='change_url.startWithLetter'
defaultMessage='Must start with a letter or number'
/>
<br/>
</span>
);
}
if (url.length > 1 && (url.charAt(url.length - 1) === '-' || url.charAt(url.length - 1) === '_')) {
error.push(<span key='error3'>{'Must end with a letter or number'}<br/></span>);
error.push(
<span key='error3'>
<FormattedMessage
id='change_url.endWithLetter'
defaultMessage='Must end with a letter or number'
/>
<br/>
</span>);
}
if (url.indexOf('__') > -1) {
error.push(<span key='error4'>{'Can not contain two underscores in a row.'}<br/></span>);
error.push(
<span key='error4'>
<FormattedMessage
id='change_url.noUnderscore'
defaultMessage='Can not contain two underscores in a row.'
/>
<br/>
</span>);
}
// In case of error we don't detect
if (error.length === 0) {
error.push(<span key='errorlast'>{'Invalid URL'}<br/></span>);
error.push(
<span key='errorlast'>
<FormattedMessage
id='change_url.invalidUrl'
defaultMessage='Invalid URL'
/>
<br/>
</span>);
}
return error;
}
@@ -137,7 +176,10 @@ export default class ChangeUrlModal extends React.Component {
className='btn btn-default'
onClick={this.doCancel}
>
{'Close'}
<FormattedMessage
id='change_url.close'
defaultMessage='Close'
/>
</button>
<button
onClick={this.doSubmit}

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

@@ -4,6 +4,8 @@
import EmailToSSO from './email_to_sso.jsx';
import SSOToEmail from './sso_to_email.jsx';
import {FormattedMessage} from 'mm-intl';
export default class ClaimAccount extends React.Component {
constructor(props) {
super(props);
@@ -13,7 +15,14 @@ export default class ClaimAccount extends React.Component {
render() {
let content;
if (this.props.email === '') {
content = <p>{'No email specified.'}</p>;
content = (
<p>
<FormattedMessage
id='claim.account.noEmail'
defaultMessage='No email specified'
/>
</p>
);
} else if (this.props.currentType === '' && this.props.newType !== '') {
content = (
<EmailToSSO

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

@@ -4,7 +4,20 @@
import * as Utils from '../../utils/utils.jsx';
import * as Client from '../../utils/client.jsx';
export default class EmailToSSO extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
pwdError: {
id: 'claim.email_to_sso.pwdError',
defaultMessage: 'Please enter your password.'
},
pwd: {
id: 'claim.email_to_sso.pwd',
defaultMessage: 'Password'
}
});
class EmailToSSO extends React.Component {
constructor(props) {
super(props);
@@ -18,7 +31,7 @@ export default class EmailToSSO extends React.Component {
var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
if (!password) {
state.error = 'Please enter your password.';
state.error = this.props.intl.formatMessage(holders.pwdError);
this.setState(state);
return;
}
@@ -59,17 +72,42 @@ export default class EmailToSSO extends React.Component {
return (
<div className='col-sm-12'>
<div className='signup-team__container'>
<h3>{'Switch Email/Password Account to ' + uiType}</h3>
<h3>
<FormattedMessage
id='claim.email_to_sso.title'
defaultMessage='Switch Email/Password Account to {uiType}'
values={{
uiType: uiType
}}
/>
</h3>
<form onSubmit={this.submit}>
<p>{'Upon claiming your account, you will only be able to login with ' + Utils.toTitleCase(this.props.type) + ' SSO.'}</p>
<p>{'Enter the password for your ' + this.props.teamDisplayName + ' ' + global.window.mm_config.SiteName + ' account.'}</p>
<p>
<FormattedMessage
id='claim.email_to_sso.ssoType'
defaultMessage='Upon claiming your account, you will only be able to login with {type} SSO'
values={{
type: Utils.toTitleCase(this.props.type)
}}
/>
</p>
<p>
<FormattedMessage
id='claim.email_to_sso.enterPwd'
defaultMessage='Enter the password for your {team} {site} account'
values={{
team: this.props.teamDisplayName,
site: global.window.mm_config.SiteName
}}
/>
</p>
<div className={formClass}>
<input
type='password'
className='form-control'
name='password'
ref='password'
placeholder='Password'
placeholder={this.props.intl.formatMessage(holders.pwd)}
spellCheck='false'
/>
</div>
@@ -78,7 +116,13 @@ export default class EmailToSSO extends React.Component {
type='submit'
className='btn btn-primary'
>
{'Switch account to ' + uiType}
<FormattedMessage
id='claim.email_to_sso.switchTo'
defaultMessage='Switch account to {uiType}'
values={{
uiType: uiType
}}
/>
</button>
</form>
</div>
@@ -90,8 +134,11 @@ export default class EmailToSSO extends React.Component {
EmailToSSO.defaultProps = {
};
EmailToSSO.propTypes = {
intl: intlShape.isRequired,
type: React.PropTypes.string.isRequired,
email: React.PropTypes.string.isRequired,
teamName: React.PropTypes.string.isRequired,
teamDisplayName: React.PropTypes.string.isRequired
};
export default injectIntl(EmailToSSO);

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

@@ -4,7 +4,28 @@
import * as Utils from '../../utils/utils.jsx';
import * as Client from '../../utils/client.jsx';
export default class SSOToEmail extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
enterPwd: {
id: 'claim.sso_to_email.enterPwd',
defaultMessage: 'Please enter a password.'
},
pwdNotMatch: {
id: 'claim.sso_to_email.pwdNotMatch',
defaultMessage: 'Password do not match.'
},
newPwd: {
id: 'claim.sso_to_email.newPwd',
defaultMessage: 'New Password'
},
confirm: {
id: 'claim.sso_to_email.confirm',
defaultMessage: 'Confirm Password'
}
});
class SSOToEmail extends React.Component {
constructor(props) {
super(props);
@@ -13,19 +34,20 @@ export default class SSOToEmail extends React.Component {
this.state = {};
}
submit(e) {
const {formatMessage} = this.props.intl;
e.preventDefault();
const state = {};
const password = ReactDOM.findDOMNode(this.refs.password).value.trim();
if (!password) {
state.error = 'Please enter a password.';
state.error = formatMessage(holders.enterPwd);
this.setState(state);
return;
}
const confirmPassword = ReactDOM.findDOMNode(this.refs.passwordconfirm).value.trim();
if (!confirmPassword || password !== confirmPassword) {
state.error = 'Passwords do not match.';
state.error = formatMessage(holders.pwdNotMatch);
this.setState(state);
return;
}
@@ -50,6 +72,7 @@ export default class SSOToEmail extends React.Component {
);
}
render() {
const {formatMessage} = this.props.intl;
var error = null;
if (this.state.error) {
error = <div className='form-group has-error'><label className='control-label'>{this.state.error}</label></div>;
@@ -65,17 +88,39 @@ export default class SSOToEmail extends React.Component {
return (
<div className='col-sm-12'>
<div className='signup-team__container'>
<h3>{'Switch ' + uiType + ' Account to Email'}</h3>
<h3>
<FormattedMessage
id='claim.sso_to_email.title'
defaultMessage='Switch {type} Account to Email'
values={{
type: uiType
}}
/>
</h3>
<form onSubmit={this.submit}>
<p>{'Upon changing your account type, you will only be able to login with your email and password.'}</p>
<p>{'Enter a new password for your ' + this.props.teamDisplayName + ' ' + global.window.mm_config.SiteName + ' account.'}</p>
<p>
<FormattedMessage
id='claim.sso_to_email.description'
defaultMessage='Upon changing your account type, you will only be able to login with your email and password.'
/>
</p>
<p>
<FormattedMessage
id='claim.sso_to_email_newPwd'
defaultMessage='Enter a new password for your {team} {site} account'
values={{
team: this.props.teamDisplayName,
site: global.window.mm_config.SiteName
}}
/>
</p>
<div className={formClass}>
<input
type='password'
className='form-control'
name='password'
ref='password'
placeholder='New Password'
placeholder={formatMessage(holders.newPwd)}
spellCheck='false'
/>
</div>
@@ -85,7 +130,7 @@ export default class SSOToEmail extends React.Component {
className='form-control'
name='passwordconfirm'
ref='passwordconfirm'
placeholder='Confirm Password'
placeholder={formatMessage(holders.confirm)}
spellCheck='false'
/>
</div>
@@ -94,7 +139,13 @@ export default class SSOToEmail extends React.Component {
type='submit'
className='btn btn-primary'
>
{'Switch ' + uiType + ' account to email and password'}
<FormattedMessage
id='claim.sso_to_email.switchTo'
defaultMessage='Switch {type} to email and password'
values={{
type: uiType
}}
/>
</button>
</form>
</div>
@@ -106,8 +157,11 @@ export default class SSOToEmail extends React.Component {
SSOToEmail.defaultProps = {
};
SSOToEmail.propTypes = {
intl: intlShape.isRequired,
currentType: React.PropTypes.string.isRequired,
email: React.PropTypes.string.isRequired,
teamName: React.PropTypes.string.isRequired,
teamDisplayName: React.PropTypes.string.isRequired
};
export default injectIntl(SSOToEmail);

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

@@ -1,6 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {FormattedMessage} from 'mm-intl';
const Modal = ReactBootstrap.Modal;
export default class ConfirmModal extends React.Component {
@@ -33,7 +34,10 @@ export default class ConfirmModal extends React.Component {
className='btn btn-default'
onClick={this.props.onCancel}
>
{'Cancel'}
<FormattedMessage
id='confirm_modal.cancel'
defaultMessage='Cancel'
/>
</button>
<button
type='button'

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

@@ -16,10 +16,32 @@ import FilePreview from './file_preview.jsx';
import * as Utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const ActionTypes = Constants.ActionTypes;
const KeyCodes = Constants.KeyCodes;
export default class CreateComment extends React.Component {
const holders = defineMessages({
commentLength: {
id: 'create_comment.commentLength',
defaultMessage: 'Comment length must be less than {max} characters.'
},
comment: {
id: 'create_comment.comment',
defaultMessage: 'Add Comment'
},
addComment: {
id: 'create_comment.addComment',
defaultMessage: 'Add a comment...'
},
commentTitle: {
id: 'create_comment.commentTitle',
defaultMessage: 'Comment'
}
});
class CreateComment extends React.Component {
constructor(props) {
super(props);
@@ -93,7 +115,7 @@ export default class CreateComment extends React.Component {
}
if (post.message.length > Constants.CHARACTER_LIMIT) {
this.setState({postError: `Comment length must be less than ${Constants.CHARACTER_LIMIT} characters.`});
this.setState({postError: this.props.intl.formatMessage(holders.commentLength, {max: Constants.CHARACTER_LIMIT})});
return;
}
@@ -189,7 +211,7 @@ export default class CreateComment extends React.Component {
AppDispatcher.handleViewAction({
type: ActionTypes.RECIEVED_EDIT_POST,
refocusId: '#reply_textbox',
title: 'Comment',
title: this.props.intl.formatMessage(holders.commentTitle),
message: lastPost.message,
postId: lastPost.id,
channelId: lastPost.channel_id,
@@ -305,14 +327,23 @@ export default class CreateComment extends React.Component {
let uploadsInProgressText = null;
if (this.state.uploadsInProgress.length > 0) {
uploadsInProgressText = (
<span
className='pull-right post-right-comments-upload-in-progress'
>
{this.state.uploadsInProgress.length === 1 ? 'File uploading' : 'Files uploading'}
<span className='pull-right post-right-comments-upload-in-progress'>
{this.state.uploadsInProgress.length === 1 ? (
<FormattedMessage
id='create_comment.file'
defaultMessage='File uploading'
/>
) : (
<FormattedMessage
id='create_comment.files'
defaultMessage='Files uploading'
/>
)}
</span>
);
}
const {formatMessage} = this.props.intl;
return (
<form onSubmit={this.handleSubmit}>
<div className='post-create'>
@@ -326,7 +357,7 @@ export default class CreateComment extends React.Component {
onKeyPress={this.commentMsgKeyPress}
onKeyDown={this.handleKeyDown}
messageText={this.state.messageText}
createMessage='Add a comment...'
createMessage={formatMessage(holders.addComment)}
initialText=''
supportsCommands={false}
id='reply_textbox'
@@ -351,7 +382,7 @@ export default class CreateComment extends React.Component {
<input
type='button'
className='btn btn-primary comment-btn pull-right'
value='Add Comment'
value={formatMessage(holders.comment)}
onClick={this.handleSubmit}
/>
{uploadsInProgressText}
@@ -366,6 +397,9 @@ export default class CreateComment extends React.Component {
}
CreateComment.propTypes = {
intl: intlShape.isRequired,
channelId: React.PropTypes.string.isRequired,
rootId: React.PropTypes.string.isRequired
};
export default injectIntl(CreateComment);

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

@@ -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);

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

@@ -1,6 +1,8 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
export default class EmailVerify extends React.Component {
constructor(props) {
super(props);
@@ -19,21 +21,61 @@ export default class EmailVerify extends React.Component {
var resend = '';
var resendConfirm = '';
if (this.props.isVerified === 'true') {
title = global.window.mm_config.SiteName + ' Email Verified';
body = <p>Your email has been verified! Click <a href={this.props.teamURL + '?email=' + this.props.userEmail}>here</a> to log in.</p>;
title = (
<FormattedMessage
id='email_verify.verified'
defaultMessage='{siteName} Email Verified'
values={{
siteName: global.window.mm_config.SiteName
}}
/>
);
body = (
<FormattedHTMLMessage
id='email_verify.verifiedBody'
defaultMessage='<p>Your email has been verified! Click <a href={url}>here</a> to log in.</p>'
values={{
url: this.props.teamURL + '?email=' + this.props.userEmail
}}
/>
);
} else {
title = global.window.mm_config.SiteName + ': You are almost done';
body = <p>Please verify your email address. Check your inbox for an email.</p>;
title = (
<FormattedMessage
id='email_verify.almost'
defaultMessage='{siteName}: You are almost done'
values={{
siteName: global.window.mm_config.SiteName
}}
/>
);
body = (
<p>
<FormattedMessage
id='email_verify.notVerifiedBody'
defaultMessage='Please verify your email address. Check your inbox for an email.'
/>
</p>
);
resend = (
<button
onClick={this.handleResend}
className='btn btn-primary'
>
Resend Email
<FormattedMessage
id='email_verify.resend'
defaultMessage='Resend Email'
/>
</button>
);
if (this.props.resendSuccess) {
resendConfirm = <div><br /><p className='alert alert-success'><i className='fa fa-check'></i>{' Verification email sent.'}</p></div>;
resendConfirm = (
<div><br /><p className='alert alert-success'><i className='fa fa-check'></i>
<FormattedMessage
id='email_verify.sent'
defaultMessage=' Verification email sent.'
/>
</p></div>);
}
}

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

@@ -3,6 +3,16 @@
import ErrorStore from '../stores/error_store.jsx';
// import mm-intl is required for the tool to be able to extract the messages
import {defineMessages} from 'mm-intl';
var messages = defineMessages({
preview: {
id: 'error_bar.preview_mode',
defaultMessage: 'Preview Mode: Email notifications have not been configured'
}
});
export default class ErrorBar extends React.Component {
constructor() {
super();
@@ -13,6 +23,12 @@ export default class ErrorBar extends React.Component {
this.state = ErrorStore.getLastError();
}
static propTypes() {
return {
intl: ReactIntl.intlShape.isRequired
};
}
isValidError(s) {
if (!s) {
return false;
@@ -41,6 +57,13 @@ export default class ErrorBar extends React.Component {
return false;
}
componentWillMount() {
if (global.window.mm_config.SendEmailNotifications === 'false') {
ErrorStore.storeLastError({message: this.props.intl.formatMessage(messages.preview)});
this.onErrorChange();
}
}
componentDidMount() {
ErrorStore.addChangeListener(this.onErrorChange);
}
@@ -64,6 +87,7 @@ export default class ErrorBar extends React.Component {
e.preventDefault();
}
ErrorStore.clearLastError();
this.setState({message: null});
}
@@ -86,3 +110,5 @@ export default class ErrorBar extends React.Component {
);
}
}
export default ReactIntl.injectIntl(ErrorBar);

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

@@ -6,7 +6,28 @@ import Constants from '../utils/constants.jsx';
import ChannelStore from '../stores/channel_store.jsx';
import * as Utils from '../utils/utils.jsx';
export default class FileUpload extends React.Component {
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
const holders = defineMessages({
limited: {
id: 'file_upload.limited',
defaultMessage: 'Uploads limited to {count} files maximum. Please use additional posts for more files.'
},
filesAbove: {
id: 'file_upload.filesAbove',
defaultMessage: 'Files above {max}MB could not be uploaded: {filenames}'
},
fileAbove: {
id: 'file_upload.fileAbove',
defaultMessage: 'File above {max}MB could not be uploaded: {filename}'
},
pasted: {
id: 'file_upload.pasted',
defaultMessage: 'Image Pasted at '
}
});
class FileUpload extends React.Component {
constructor(props) {
super(props);
@@ -74,14 +95,15 @@ export default class FileUpload extends React.Component {
numUploads += 1;
}
const {formatMessage} = this.props.intl;
if (files.length > uploadsRemaining) {
this.props.onUploadError(`Uploads limited to ${Constants.MAX_UPLOAD_FILES} files maximum. Please use additional posts for more files.`);
this.props.onUploadError(formatMessage(holders.limited, {count: Constants.MAX_UPLOAD_FILES}));
} else if (tooLargeFiles.length > 1) {
var tooLargeFilenames = tooLargeFiles.map((file) => file.name).join(', ');
this.props.onUploadError(`Files above ${Constants.MAX_FILE_SIZE / 1000000}MB could not be uploaded: ${tooLargeFilenames}`);
this.props.onUploadError(formatMessage(holders.filesAbove, {max: (Constants.MAX_FILE_SIZE / 1000000), files: tooLargeFilenames}));
} else if (tooLargeFiles.length > 0) {
this.props.onUploadError(`File above ${Constants.MAX_FILE_SIZE / 1000000}MB could not be uploaded: ${tooLargeFiles[0].name}`);
this.props.onUploadError(formatMessage(holders.fileAbove, {max: (Constants.MAX_FILE_SIZE / 1000000), file: tooLargeFiles[0].name}));
}
}
@@ -106,6 +128,7 @@ export default class FileUpload extends React.Component {
componentDidMount() {
var inputDiv = ReactDOM.findDOMNode(this.refs.input);
var self = this;
const {formatMessage} = this.props.intl;
if (this.props.postType === 'post') {
$('.row.main').dragster({
@@ -184,7 +207,7 @@ export default class FileUpload extends React.Component {
var numToUpload = Math.min(Constants.MAX_UPLOAD_FILES - self.props.getFileCount(ChannelStore.getCurrentId()), numItems);
if (numItems > numToUpload) {
self.props.onUploadError('Uploads limited to ' + Constants.MAX_UPLOAD_FILES + ' files maximum. Please use additional posts for more files.');
self.props.onUploadError(formatMessage(holders.limited, {count: Constants.MAX_UPLOAD_FILES}));
}
for (var i = 0; i < items.length && i < numToUpload; i++) {
@@ -218,7 +241,7 @@ export default class FileUpload extends React.Component {
min = String(d.getMinutes());
}
var name = 'Image Pasted at ' + d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate() + ' ' + hour + '-' + min + '.' + ext;
var name = formatMessage(holders.pasted) + d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate() + ' ' + hour + '-' + min + '.' + ext;
formData.append('files', file, name);
formData.append('client_ids', clientId);
@@ -296,6 +319,7 @@ export default class FileUpload extends React.Component {
}
FileUpload.propTypes = {
intl: intlShape.isRequired,
onUploadError: React.PropTypes.func,
getFileCount: React.PropTypes.func,
onFileUpload: React.PropTypes.func,
@@ -304,3 +328,5 @@ FileUpload.propTypes = {
channelId: React.PropTypes.string,
postType: React.PropTypes.string
};
export default injectIntl(FileUpload);

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

@@ -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 FileUploadOverlay extends React.Component {
render() {
var overlayClass = 'file-overlay hidden';
@@ -19,7 +21,12 @@ export default class FileUploadOverlay extends React.Component {
src='/static/images/filesOverlay.png'
alt='Files'
/>
<span><i className='fa fa-upload'></i>{'Drop a file to upload it.'}</span>
<span><i className='fa fa-upload'></i>
<FormattedMessage
id='upload_overlay.info'
defaultMessage='Drop a file to upload it.'
/>
</span>
<img
className='overlay__logo'
src='/static/images/logoWhite.png'

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

@@ -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);

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

@@ -1,6 +1,8 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {FormattedMessage} from 'mm-intl';
const Modal = ReactBootstrap.Modal;
export default class GetLinkModal extends React.Component {
@@ -59,14 +61,25 @@ export default class GetLinkModal extends React.Component {
className='btn btn-primary pull-left'
onClick={this.copyLink}
>
{'Copy Link'}
<FormattedMessage
id='get_link.copy'
defaultMessage='Copy Link'
/>
</button>
);
}
var copyLinkConfirm = null;
if (this.state.copiedLink) {
copyLinkConfirm = <p className='alert alert-success copy-link-confirm'><i className='fa fa-check'></i>{' Link copied to clipboard.'}</p>;
copyLinkConfirm = (
<p className='alert alert-success copy-link-confirm'>
<i className='fa fa-check'></i>
<FormattedMessage
id='get_link.clipboard'
defaultMessage=' Link copied to clipboard.'
/>
</p>
);
}
return (
@@ -92,7 +105,10 @@ export default class GetLinkModal extends React.Component {
className='btn btn-default'
onClick={this.onHide}
>
{'Close'}
<FormattedMessage
id='get_link.close'
defaultMessage='Close'
/>
</button>
{copyLink}
{copyLinkConfirm}

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

@@ -6,7 +6,20 @@ import GetLinkModal from './get_link_modal.jsx';
import ModalStore from '../stores/modal_store.jsx';
import TeamStore from '../stores/team_store.jsx';
export default class GetTeamInviteLinkModal extends React.Component {
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
const holders = defineMessages({
title: {
id: 'get_team_invite_link_modal.title',
defaultMessage: 'Team Invite Link'
},
help: {
id: 'get_team_invite_link_modal.help',
defaultMessage: 'Send teammates the link below for them to sign-up to this team site.'
}
});
class GetTeamInviteLinkModal extends React.Component {
constructor(props) {
super(props);
@@ -32,14 +45,22 @@ export default class GetTeamInviteLinkModal extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
return (
<GetLinkModal
show={this.state.show}
onHide={() => this.setState({show: false})}
title='Team Invite Link'
helpText='Send teammates the link below for them to sign-up to this team site.'
title={formatMessage(holders.title)}
helpText={formatMessage(holders.help)}
link={TeamStore.getCurrentInviteLink()}
/>
);
}
}
GetTeamInviteLinkModal.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(GetTeamInviteLinkModal);

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

@@ -12,9 +12,38 @@ import ChannelStore from '../stores/channel_store.jsx';
import TeamStore from '../stores/team_store.jsx';
import ConfirmModal from './confirm_modal.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const Modal = ReactBootstrap.Modal;
export default class InviteMemberModal extends React.Component {
const holders = defineMessages({
emailError: {
id: 'invite_member.emailError',
defaultMessage: 'Please enter a valid email address'
},
firstname: {
id: 'invite_member.firstname',
defaultMessage: 'First name'
},
lastname: {
id: 'invite_member.lastname',
defaultMessage: 'Last name'
},
modalTitle: {
id: 'invite_member.modalTitle',
defaultMessage: 'Discard Invitations?'
},
modalMessage: {
id: 'invite_member.modalMessage',
defaultMessage: 'You have unsent invitations, are you sure you want to discard them?'
},
modalButton: {
id: 'invite_member.modalButton',
defaultMessage: 'Yes, Discard'
}
});
class InviteMemberModal extends React.Component {
constructor(props) {
super(props);
@@ -72,7 +101,7 @@ export default class InviteMemberModal extends React.Component {
var invite = {};
invite.email = ReactDOM.findDOMNode(this.refs['email' + index]).value.trim();
if (!invite.email || !utils.isEmail(invite.email)) {
emailErrors[index] = 'Please enter a valid email address';
emailErrors[index] = this.props.intl.formatMessage(holders.emailError);
valid = false;
} else {
emailErrors[index] = '';
@@ -103,7 +132,7 @@ export default class InviteMemberModal extends React.Component {
this.setState({isSendingEmails: false});
},
(err) => {
if (err.message === 'This person is already on your team') {
if (err.id === 'api.team.invite_members.already.app_error') {
emailErrors[err.detailed_error] = err.message;
this.setState({emailErrors: emailErrors});
} else {
@@ -199,6 +228,7 @@ export default class InviteMemberModal extends React.Component {
render() {
var currentUser = UserStore.getCurrentUser();
const {formatMessage} = this.props.intl;
if (currentUser != null) {
var inviteSections = [];
@@ -252,7 +282,7 @@ export default class InviteMemberModal extends React.Component {
type='text'
className='form-control'
ref={'first_name' + index}
placeholder='First name'
placeholder={formatMessage(holders.firstname)}
maxLength='64'
disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
spellCheck='false'
@@ -266,7 +296,7 @@ export default class InviteMemberModal extends React.Component {
type='text'
className='form-control'
ref={'last_name' + index}
placeholder='Last name'
placeholder={formatMessage(holders.lastname)}
maxLength='64'
disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
spellCheck='false'
@@ -318,20 +348,48 @@ export default class InviteMemberModal extends React.Component {
type='button'
className='btn btn-default'
onClick={this.addInviteFields}
>{'Add another'}</button>
>
<FormattedMessage
id='invite_member.addAnother'
defaultMessage='Add another'
/>
</button>
<br/>
<br/>
<span>{'People invited automatically join the '}<strong>{defaultChannelName}</strong>{' channel.'}</span>
<span>
<FormattedHTMLMessage
id='invite_member.autoJoin'
defaultMessage='People invited automatically join the <strong>{channel}</strong> channel.'
values={{
channel: defaultChannelName
}}
/>
</span>
</div>
);
var sendButtonLabel = 'Send Invitation';
var sendButtonLabel = (
<FormattedMessage
id='invite_member.send'
defaultMessage='Send Invitation'
/>
);
if (this.state.isSendingEmails) {
sendButtonLabel = (
<span><i className='fa fa-spinner fa-spin' />{' Sending'}</span>
<span><i className='fa fa-spinner fa-spin' />
<FormattedMessage
id='invite_member.sending'
defaultMessage=' Sending'
/>
</span>
);
} else if (this.state.inviteIds.length > 1) {
sendButtonLabel = 'Send Invitations';
sendButtonLabel = (
<FormattedMessage
id='invite_member.send2'
defaultMessage='Send Invitations'
/>
);
}
sendButton = (
@@ -352,27 +410,46 @@ export default class InviteMemberModal extends React.Component {
href='#'
onClick={this.showGetTeamInviteLinkModal}
>
{'Team Invite Link'}
<FormattedMessage
id='invite_member.inviteLink'
defaultMessage='Team Invite Link'
/>
</a>
);
teamInviteLink = (
<p>
{'You can also invite people using the '}{link}{'.'}
<FormattedMessage
id='invite_member.teamInviteLink'
defaultMessage='You can also invite people using the {link}.'
values={{
link: (link)
}}
/>
</p>
);
}
content = (
<div>
<p>{'Email is currently disabled for your team, and email invitations cannot be sent. Contact your system administrator to enable email and email invitations.'}</p>
<p>
<FormattedMessage
id='invite_member.content'
defaultMessage='Email is currently disabled for your team, and email invitations cannot be sent. Contact your system administrator to enable email and email invitations.'
/>
</p>
{teamInviteLink}
</div>
);
} else {
content = (
<div>
<p>{'User creation has been disabled for your team. Please ask your team administrator for details.'}</p>
<p>
<FormattedMessage
id='invite_member.disabled'
defaultMessage='User creation has been disabled for your team. Please ask your team administrator for details.'
/>
</p>
</div>
);
}
@@ -387,7 +464,12 @@ export default class InviteMemberModal extends React.Component {
backdrop={this.state.isSendingEmails ? 'static' : true}
>
<Modal.Header closeButton={!this.state.isSendingEmails}>
<Modal.Title>{'Invite New Member'}</Modal.Title>
<Modal.Title>
<FormattedMessage
id='invite_member.newMember'
defaultMessage='Invite New Member'
/>
</Modal.Title>
</Modal.Header>
<Modal.Body ref='modalBody'>
<form role='form'>
@@ -402,15 +484,18 @@ export default class InviteMemberModal extends React.Component {
onClick={this.handleHide.bind(this, true)}
disabled={this.state.isSendingEmails}
>
{'Cancel'}
<FormattedMessage
id='invite_member.cancel'
defaultMessage='Cancel'
/>
</button>
{sendButton}
</Modal.Footer>
</Modal>
<ConfirmModal
title='Discard Invitations?'
message='You have unsent invitations, are you sure you want to discard them?'
confirm_button='Yes, Discard'
title={formatMessage(holders.modalTitle)}
message={formatMessage(holders.modalMessage)}
confirm_button={formatMessage(holders.modalButton)}
show={this.state.showConfirmModal}
onConfirm={this.handleHide.bind(this, false)}
onCancel={() => this.setState({showConfirmModal: false})}
@@ -424,4 +509,7 @@ export default class InviteMemberModal extends React.Component {
}
InviteMemberModal.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(InviteMemberModal);

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

@@ -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 LoadingScreen extends React.Component {
constructor(props) {
super(props);
@@ -13,7 +15,12 @@ export default class LoadingScreen extends React.Component {
style={{position: this.props.position}}
>
<div className='loading__content'>
<h3>Loading</h3>
<h3>
<FormattedMessage
id='loading_screen.loading'
defaultMessage='Loading'
/>
</h3>
<div className='round round-1'></div>
<div className='round round-2'></div>
<div className='round round-3'></div>

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

@@ -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>
@@ -115,14 +149,17 @@ export default class Login extends React.Component {
}
let teamSignUp = null;
if (global.window.mm_config.EnableTeamCreation === 'true') {
if (global.window.mm_config.EnableTeamCreation === 'true' && !Utils.isMobileApp()) {
teamSignUp = (
<div className='margin--extra'>
<a
href='/'
className='signup-team-login'
>
{'Create a new team'}
<FormattedMessage
id='login.createTeam'
defaultMessage='Create a new team'
/>
</a>
</div>
);
@@ -137,25 +174,45 @@ export default class Login extends React.Component {
);
}
let findTeams = null;
if (!Utils.isMobileApp()) {
findTeams = (
<div className='form-group margin--extra form-group--small'>
<span>
<a href='/find_team'>
<FormattedMessage
id='login.find'
defaultMessage='Find your other teams'
/>
</a></span>
</div>
);
}
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}
{ldapLogin}
{userSignUp}
<div className='form-group margin--extra form-group--small'>
<span>
<a href='/find_team'>
<FormattedMessage
id='login.find_teams'
defaultMessage='Find your other teams'
/>
</a></span>
</div>
{findTeams}
{forgotPassword}
{teamSignUp}
</div>

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

@@ -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);

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

@@ -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);

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

@@ -6,6 +6,8 @@ import * as Client from '../utils/client.jsx';
import * as AsyncClient from '../utils/async_client.jsx';
import * as Utils from '../utils/utils.jsx';
import {FormattedMessage} from 'mm-intl';
export default class MemberListTeamItem extends React.Component {
constructor(props) {
super(props);
@@ -78,14 +80,29 @@ export default class MemberListTeamItem extends React.Component {
}
const user = this.props.user;
let currentRoles = 'Member';
let currentRoles = (
<FormattedMessage
id='member_team_item.member'
defaultMessage='Member'
/>
);
const timestamp = UserStore.getCurrentUser().update_at;
if (user.roles.length > 0) {
if (Utils.isSystemAdmin(user.roles)) {
currentRoles = 'System Admin';
currentRoles = (
<FormattedMessage
id='member_team_item.systemAdmin'
defaultMessage='System Admin'
/>
);
} else if (Utils.isAdmin(user.roles)) {
currentRoles = 'Team Admin';
currentRoles = (
<FormattedMessage
id='member_team_item.teamAdmin'
defaultMessage='Team Admin'
/>
);
} else {
currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
}
@@ -98,7 +115,12 @@ export default class MemberListTeamItem extends React.Component {
let showMakeNotActive = user.roles !== 'system_admin';
if (user.delete_at > 0) {
currentRoles = 'Inactive';
currentRoles = (
<FormattedMessage
id='member_team_item.inactive'
defaultMessage='Inactive'
/>
);
showMakeMember = false;
showMakeAdmin = false;
showMakeActive = true;
@@ -114,7 +136,10 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeAdmin}
>
{'Make Team Admin'}
<FormattedMessage
id='member_team_item.makeAdmin'
defaultMessage='Make Team Admin'
/>
</a>
</li>
);
@@ -129,7 +154,10 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeMember}
>
{'Make Member'}
<FormattedMessage
id='member_team_item.makeMember'
defaultMessage='Make Member'
/>
</a>
</li>
);
@@ -144,7 +172,10 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeActive}
>
{'Make Active'}
<FormattedMessage
id='member_team_item.makeActive'
defaultMessage='Make Active'
/>
</a>
</li>
);
@@ -159,7 +190,10 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeNotActive}
>
{'Make Inactive'}
<FormattedMessage
id='member_team_item.makeInactive'
defaultMessage='Make Inactive'
/>
</a>
</li>
);

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

@@ -8,6 +8,8 @@ import ChannelStore from '../stores/channel_store.jsx';
import LoadingScreen from './loading_screen.jsx';
import NewChannelFlow from './new_channel_flow.jsx';
import {FormattedMessage} from 'mm-intl';
function getStateFromStores() {
return {
channels: ChannelStore.getMoreAll(),
@@ -100,7 +102,10 @@ export default class MoreChannels extends React.Component {
onClick={self.handleJoin.bind(self, channel, index)}
className='btn btn-primary'
>
Join
<FormattedMessage
id='more_channels.join'
defaultMessage='Join'
/>
</button>
);
}
@@ -123,8 +128,18 @@ export default class MoreChannels extends React.Component {
} else {
moreChannels = (
<div className='no-channel-message'>
<p className='primary-message'>No more channels to join</p>
<p className='secondary-message'>Click 'Create New Channel' to make a new one</p>
<p className='primary-message'>
<FormattedMessage
id='more_channels.noMore'
defaultMessage='No more channels to join'
/>
</p>
<p className='secondary-message'>
<FormattedMessage
id='more_channels.createClick'
defaultMessage="Click 'Create New Channel' to make a new one"
/>
</p>
</div>
);
}
@@ -148,15 +163,28 @@ export default class MoreChannels extends React.Component {
data-dismiss='modal'
>
<span aria-hidden='true'>{'×'}</span>
<span className='sr-only'>{'Close'}</span>
<span className='sr-only'>
<FormattedMessage
id='more_channels.close'
defaultMessage='Close'
/>
</span>
</button>
<h4 className='modal-title'>{'More Channels'}</h4>
<h4 className='modal-title'>
<FormattedMessage
id='more_channels.title'
defaultMessage='More Channels'
/>
</h4>
<button
type='button'
className='btn btn-primary channel-create-btn'
onClick={this.handleNewChannel}
>
{'Create New Channel'}
<FormattedMessage
id='more_channels.create'
defaultMessage='Create New Channel'
/>
</button>
<NewChannelFlow
show={this.state.showNewChannelModal}
@@ -174,7 +202,10 @@ export default class MoreChannels extends React.Component {
className='btn btn-default'
data-dismiss='modal'
>
{'Close'}
<FormattedMessage
id='more_channels.close'
defaultMessage='Close'
/>
</button>
</div>
</div>

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

@@ -5,7 +5,20 @@ const Modal = ReactBootstrap.Modal;
import UserStore from '../stores/user_store.jsx';
import * as Utils from '../utils/utils.jsx';
export default class MoreDirectChannels extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
member: {
id: 'more_direct_channels.member',
defaultMessage: 'Member'
},
search: {
id: 'more_direct_channels.search',
defaultMessage: 'Search members'
}
});
class MoreDirectChannels extends React.Component {
constructor(props) {
super(props);
@@ -148,7 +161,10 @@ export default class MoreDirectChannels extends React.Component {
className='btn btn-primary btn-message'
onClick={this.handleShowDirectChannel.bind(this, user)}
>
{'Message'}
<FormattedMessage
id='more_direct_channels.message'
defaultMessage='Message'
/>
</button>
);
}
@@ -180,6 +196,7 @@ export default class MoreDirectChannels extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
if (!this.props.show) {
return null;
}
@@ -199,19 +216,44 @@ export default class MoreDirectChannels extends React.Component {
const userEntries = users.map(this.createRowForUser);
if (userEntries.length === 0) {
userEntries.push(<tr key='no-users-found'><td>{'No users found :('}</td></tr>);
userEntries.push(
<tr key='no-users-found'><td>
<FormattedMessage
id='more_direct_channels.notFound'
defaultMessage='No users found :('
/>
</td></tr>);
}
let memberString = 'Member';
let memberString = formatMessage(holders.member);
if (users.length !== 1) {
memberString += 's';
}
let count;
if (users.length === this.state.users.length) {
count = `${users.length} ${memberString}`;
count = (
<FormattedMessage
id='more_direct_channels.count'
defaultMessage='{count} {member}'
values={{
count: users.length,
member: memberString
}}
/>
);
} else {
count = `${users.length} ${memberString} of ${this.state.users.length} Total`;
count = (
<FormattedMessage
id='more_direct_channels.countTotal'
defaultMessage='{count} {member} of {total} Total'
values={{
count: users.length,
member: memberString,
total: this.state.users.length
}}
/>
);
}
return (
@@ -221,7 +263,12 @@ export default class MoreDirectChannels extends React.Component {
onHide={this.handleHide}
>
<Modal.Header closeButton={true}>
<Modal.Title>{'Direct Messages'}</Modal.Title>
<Modal.Title>
<FormattedMessage
id='more_direct_channels.title'
defaultMessage='Direct Messages'
/>
</Modal.Title>
</Modal.Header>
<Modal.Body ref='modalBody'>
<div className='filter-row'>
@@ -229,7 +276,7 @@ export default class MoreDirectChannels extends React.Component {
<input
ref='filter'
className='form-control filter-textbox'
placeholder='Search members'
placeholder={formatMessage(holders.search)}
onInput={this.handleFilterChange}
/>
</div>
@@ -254,7 +301,10 @@ export default class MoreDirectChannels extends React.Component {
className='btn btn-default'
onClick={this.handleHide}
>
{'Close'}
<FormattedMessage
id='more_direct_channels.close'
defaultMessage='Close'
/>
</button>
</Modal.Footer>
</Modal>
@@ -263,6 +313,9 @@ export default class MoreDirectChannels extends React.Component {
}
MoreDirectChannels.propTypes = {
intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
onModalDismissed: React.PropTypes.func
};
export default injectIntl(MoreDirectChannels);

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

@@ -5,9 +5,19 @@ import SocketStore from '../stores/socket_store.jsx';
import UserStore from '../stores/user_store.jsx';
import Constants from '../utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const SocketEvents = Constants.SocketEvents;
export default class MsgTyping extends React.Component {
const holders = defineMessages({
someone: {
id: 'msg_typing.someone',
defaultMessage: 'Someone'
}
});
class MsgTyping extends React.Component {
constructor(props) {
super(props);
@@ -25,9 +35,17 @@ export default class MsgTyping extends React.Component {
SocketStore.addChangeListener(this.onChange);
}
componentWillReceiveProps(newProps) {
if (this.props.channelId !== newProps.channelId) {
this.updateTypingText();
componentWillReceiveProps(nextProps) {
if (this.props.channelId !== nextProps.channelId) {
for (const u in this.typingUsers) {
if (!this.typingUsers.hasOwnProperty(u)) {
continue;
}
clearTimeout(this.typingUsers[u]);
}
this.typingUsers = {};
this.setState({text: ''});
}
}
@@ -36,10 +54,10 @@ export default class MsgTyping extends React.Component {
}
onChange(msg) {
let username = 'Someone';
let username = this.props.intl.formatMessage(holders.someone);
if (msg.action === SocketEvents.TYPING &&
this.props.channelId === msg.channel_id &&
this.props.parentId === msg.props.parent_id) {
this.props.channelId === msg.channel_id &&
this.props.parentId === msg.props.parent_id) {
if (UserStore.hasProfile(msg.user_id)) {
username = UserStore.getProfile(msg.user_id).username;
}
@@ -72,11 +90,28 @@ export default class MsgTyping extends React.Component {
text = '';
break;
case 1:
text = users[0] + ' is typing...';
text = (
<FormattedMessage
id='msg_typing.isTyping'
defaultMessage='{user} is typing...'
values={{
user: users[0]
}}
/>
);
break;
default: {
const last = users.pop();
text = users.join(', ') + ' and ' + last + ' are typing...';
text = (
<FormattedMessage
id='msg_typing.areTyping'
defaultMessage='{users} and {last} are typing...'
vaues={{
users: users.join(', '),
last: last
}}
/>
);
break;
}
}
@@ -92,6 +127,9 @@ export default class MsgTyping extends React.Component {
}
MsgTyping.propTypes = {
intl: intlShape.isRequired,
channelId: React.PropTypes.string,
parentId: React.PropTypes.string
};
export default injectIntl(MsgTyping);

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

@@ -392,10 +392,14 @@ export default class Navbar extends React.Component {
} else if (channel.type === 'D') {
isDirect = true;
if (this.state.users.length > 1) {
let p;
if (this.state.users[0].id === currentId) {
channelTitle = UserStore.getProfile(this.state.users[1].id).username;
p = UserStore.getProfile(this.state.users[1].id);
} else {
channelTitle = UserStore.getProfile(this.state.users[0].id).username;
p = UserStore.getProfile(this.state.users[0].id);
}
if (p != null) {
channelTitle = p.username;
}
}
}

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

@@ -14,6 +14,8 @@ import UserSettingsModal from './user_settings/user_settings_modal.jsx';
import Constants from '../utils/constants.jsx';
import {FormattedMessage} from 'mm-intl';
function getStateFromStores() {
const teams = [];
const teamsObject = UserStore.getTeams();
@@ -97,7 +99,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={EventHelpers.showInviteMemberModal}
>
{'Invite New Member'}
<FormattedMessage
id='navbar_dropdown.inviteMember'
defaultMessage='Invite New Member'
/>
</a>
</li>
);
@@ -109,7 +114,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={EventHelpers.showGetTeamInviteLinkModal}
>
{'Get Team Invite Link'}
<FormattedMessage
id='navbar_dropdown.teamLink'
defaultMessage='Get Team Invite Link'
/>
</a>
</li>
);
@@ -120,7 +128,10 @@ export default class NavbarDropdown extends React.Component {
manageLink = (
<li>
<ToggleModalButton dialogType={TeamMembersModal}>
{'Manage Members'}
<FormattedMessage
id='navbar_dropdown.manageMembers'
defaultMessage='Manage Members'
/>
</ToggleModalButton>
</li>
);
@@ -134,7 +145,10 @@ export default class NavbarDropdown extends React.Component {
data-toggle='modal'
data-target='#team_settings'
>
{'Team Settings'}
<FormattedMessage
id='navbar_dropdown.teamSettings'
defaultMessage='Team Settings'
/>
</a>
</li>
);
@@ -146,7 +160,10 @@ export default class NavbarDropdown extends React.Component {
<a
href={'/admin_console?' + Utils.getSessionIndex()}
>
{'System Console'}
<FormattedMessage
id='navbar_dropdown.console'
defaultMessage='System Console'
/>
</a>
</li>
);
@@ -165,7 +182,16 @@ export default class NavbarDropdown extends React.Component {
this.state.teams.forEach((team) => {
if (team.name !== this.props.teamName) {
teams.push(<li key={team.name}><a href={Utils.getWindowLocationOrigin() + '/' + team.name}>{'Switch to ' + team.display_name}</a></li>);
teams.push(
<li key={team.name}><a href={Utils.getWindowLocationOrigin() + '/' + team.name}>
<FormattedMessage
id='navbar_dropdown.switchTeam'
defaultMessage='Switch to {team}'
values={{
team: team.display_name
}}
/>
</a></li>);
}
});
}
@@ -178,7 +204,10 @@ export default class NavbarDropdown extends React.Component {
target='_blank'
href={Utils.getWindowLocationOrigin() + '/signup_team'}
>
{'Create a New Team'}
<FormattedMessage
id='navbar_dropdown.create'
defaultMessage='Create a New Team'
/>
</a>
</li>
);
@@ -192,7 +221,10 @@ export default class NavbarDropdown extends React.Component {
target='_blank'
href={global.window.mm_config.HelpLink}
>
{'Help'}
<FormattedMessage
id='navbar_dropdown.help'
defaultMessage='Help'
/>
</a>
</li>
);
@@ -206,7 +238,10 @@ export default class NavbarDropdown extends React.Component {
target='_blank'
href={global.window.mm_config.ReportAProblemLink}
>
{'Report a Problem'}
<FormattedMessage
id='navbar_dropdown.report'
defaultMessage='Report a Problem'
/>
</a>
</li>
);
@@ -239,7 +274,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={() => this.setState({showUserSettingsModal: true})}
>
{'Account Settings'}
<FormattedMessage
id='navbar_dropdown.accountSettings'
defaultMessage='Account Settings'
/>
</a>
</li>
{inviteLink}
@@ -249,7 +287,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={this.handleLogoutClick}
>
{'Logout'}
<FormattedMessage
id='navbar_dropdown.logout'
defaultMessage='Logout'
/>
</a>
</li>
{adminDivider}
@@ -265,7 +306,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={this.handleAboutModal}
>
{'About Mattermost'}
<FormattedMessage
id='navbar_dropdown.about'
defaultMessage='About Mattermost'
/>
</a>
</li>
<UserSettingsModal

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

@@ -9,11 +9,47 @@ import UserStore from '../stores/user_store.jsx';
import NewChannelModal from './new_channel_modal.jsx';
import ChangeURLModal from './change_url_modal.jsx';
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
const SHOW_NEW_CHANNEL = 1;
const SHOW_EDIT_URL = 2;
const SHOW_EDIT_URL_THEN_COMPLETE = 3;
const messages = defineMessages({
invalidName: {
id: 'channel_flow.invalidName',
defaultMessage: 'Invalid Channel Name'
},
alreadyExist: {
id: 'channel_flow.alreadyExist',
defaultMessage: 'A channel with that URL already exists'
},
channel: {
id: 'channel_flow.channel',
defaultMessage: 'Channel'
},
group: {
id: 'channel_flow.group',
defaultMessage: 'Group'
},
change: {
id: 'channel_flow.changeUrlTitle',
defaultMessage: 'Change {term} URL'
},
set: {
id: 'channel_flow.set_url_title',
defaultMessage: 'Set {term} URL'
},
create: {
id: 'channel_flow.create',
defaultMessage: 'Create {term}'
},
changeUrlDescription: {
id: 'channel_flow.changeUrlDescription',
defaultMessage: 'Some characters are not allowed in URLs and may be removed.'
}
});
export default class NewChannelFlow extends React.Component {
class NewChannelFlow extends React.Component {
constructor(props) {
super(props);
@@ -51,9 +87,10 @@ export default class NewChannelFlow extends React.Component {
doSubmit() {
var channel = {};
const {formatMessage} = this.props.intl;
channel.display_name = this.state.channelDisplayName;
if (!channel.display_name) {
this.setState({serverError: 'Invalid Channel Name'});
this.setState({serverError: formatMessage(messages.invalidName)});
return;
}
@@ -75,11 +112,11 @@ export default class NewChannelFlow extends React.Component {
Utils.switchChannel(data);
},
(err) => {
if (err.message === 'Name must be 2 or more lowercase alphanumeric characters') {
if (err.id === 'model.channel.is_valid.2_or_more.app_error') {
this.setState({flowState: SHOW_EDIT_URL_THEN_COMPLETE});
}
if (err.message === 'A channel with that handle already exists') {
this.setState({serverError: 'A channel with that URL already exists'});
if (err.id === 'store.sql_channel.update.exists.app_error') {
this.setState({serverError: formatMessage(messages.alreadyExist)});
return;
}
this.setState({serverError: err.message});
@@ -130,27 +167,29 @@ export default class NewChannelFlow extends React.Component {
let changeURLSubmitButtonText = '';
let channelTerm = '';
const {formatMessage} = this.props.intl;
// Only listen to flow state if we are being shown
if (this.props.show) {
switch (this.state.flowState) {
case SHOW_NEW_CHANNEL:
if (this.state.channelType === 'O') {
showChannelModal = true;
channelTerm = 'Channel';
channelTerm = formatMessage(messages.channel);
} else {
showGroupModal = true;
channelTerm = 'Group';
channelTerm = formatMessage(messages.group);
}
break;
case SHOW_EDIT_URL:
showChangeURLModal = true;
changeURLTitle = 'Change ' + channelTerm + ' URL';
changeURLSubmitButtonText = 'Change ' + channelTerm + ' URL';
changeURLTitle = formatMessage(messages.change, {term: channelTerm});
changeURLSubmitButtonText = formatMessage(messages.change, {term: channelTerm});
break;
case SHOW_EDIT_URL_THEN_COMPLETE:
showChangeURLModal = true;
changeURLTitle = 'Set ' + channelTerm + ' URL';
changeURLSubmitButtonText = 'Create ' + channelTerm;
changeURLTitle = formatMessage(messages.set, {term: channelTerm});
changeURLSubmitButtonText = formatMessage(messages.create, {term: channelTerm});
break;
}
}
@@ -181,7 +220,7 @@ export default class NewChannelFlow extends React.Component {
<ChangeURLModal
show={showChangeURLModal}
title={changeURLTitle}
description={'Some characters are not allowed in URLs and may be removed.'}
description={formatMessage(messages.changeUrlDescription)}
urlLabel={channelTerm + ' URL'}
submitButtonText={changeURLSubmitButtonText}
currentURL={this.state.channelName}
@@ -200,7 +239,10 @@ NewChannelFlow.defaultProps = {
};
NewChannelFlow.propTypes = {
intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
channelType: React.PropTypes.string.isRequired,
onModalDismissed: React.PropTypes.func.isRequired
};
export default injectIntl(NewChannelFlow);

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

@@ -2,9 +2,19 @@
// See License.txt for license information.
import * as Utils from '../utils/utils.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
var Modal = ReactBootstrap.Modal;
export default class NewChannelModal extends React.Component {
const holders = defineMessages({
nameEx: {
id: 'channel_modal.nameEx',
defaultMessage: 'E.g.: "Bugs", "Marketing", "办公室恋情"'
}
});
class NewChannelModal extends React.Component {
constructor(props) {
super(props);
@@ -22,12 +32,17 @@ export default class NewChannelModal extends React.Component {
});
}
}
componentDidMount() {
if (Utils.isBrowserIE()) {
$('body').addClass('browser--IE');
}
}
handleSubmit(e) {
e.preventDefault();
const displayName = ReactDOM.findDOMNode(this.refs.display_name).value.trim();
if (displayName.length < 1) {
this.setState({displayNameError: 'This field is required'});
this.setState({displayNameError: true});
return;
}
@@ -46,7 +61,15 @@ export default class NewChannelModal extends React.Component {
var displayNameClass = 'form-group';
if (this.state.displayNameError) {
displayNameError = <p className='input__help error'>{this.state.displayNameError}</p>;
displayNameError = (
<p className='input__help error'>
<FormattedMessage
id='channel_modal.displayNameError'
defaultMessage='This field is required'
/>
{this.state.displayNameError}
</p>
);
displayNameClass += ' has-error';
}
@@ -58,29 +81,51 @@ export default class NewChannelModal extends React.Component {
var channelSwitchText = '';
switch (this.props.channelType) {
case 'P':
channelTerm = 'Group';
channelTerm = (
<FormattedMessage
id='channel_modal.group'
defaultMessage='Group'
/>
);
channelSwitchText = (
<div className='modal-intro'>
{'Create a new private group with restricted membership. '}
<FormattedMessage
id='channel_modal.privateGroup1'
defaultMessage='Create a new private group with restricted membership. '
/>
<a
href='#'
onClick={this.props.onTypeSwitched}
>
{'Create a public channel'}
<FormattedMessage
id='channel_modal.publicChannel1'
defaultMessage='Create a public channel'
/>
</a>
</div>
);
break;
case 'O':
channelTerm = 'Channel';
channelTerm = (
<FormattedMessage
id='channel_modal.channel'
defaultMessage='Channel'
/>
);
channelSwitchText = (
<div className='modal-intro'>
{'Create a new public channel anyone can join. '}
<FormattedMessage
id='channel_modal.publicChannel2'
defaultMessage='Create a new public channel anyone can join. '
/>
<a
href='#'
onClick={this.props.onTypeSwitched}
>
{'Create a private group'}
<FormattedMessage
id='channel_modal.privateGroup2'
defaultMessage='Create a private group'
/>
</a>
</div>
);
@@ -97,7 +142,13 @@ export default class NewChannelModal extends React.Component {
onHide={this.props.onModalDismissed}
>
<Modal.Header closeButton={true}>
<Modal.Title>{'New ' + channelTerm}</Modal.Title>
<Modal.Title>
<FormattedMessage
id='channel_modal.modalTitle'
defaultMessage='New '
/>
{channelTerm}
</Modal.Title>
</Modal.Header>
<form
role='form'
@@ -108,14 +159,19 @@ export default class NewChannelModal extends React.Component {
{channelSwitchText}
</div>
<div className={displayNameClass}>
<label className='col-sm-3 form__label control-label'>{'Name'}</label>
<label className='col-sm-3 form__label control-label'>
<FormattedMessage
id='channel_modal.name'
defaultMessage='Name'
/>
</label>
<div className='col-sm-9'>
<input
onChange={this.handleChange}
type='text'
ref='display_name'
className='form-control'
placeholder='E.g.: "Bugs", "Marketing", "办公室恋情"'
placeholder={this.props.intl.formatMessage(holders.nameEx)}
maxLength='22'
value={this.props.channelData.displayName}
autoFocus={true}
@@ -128,7 +184,10 @@ export default class NewChannelModal extends React.Component {
href='#'
onClick={this.props.onChangeURLPressed}
>
{'Edit'}
<FormattedMessage
id='channel_modal.edit'
defaultMessage='Edit'
/>
</a>
{')'}
</p>
@@ -136,22 +195,38 @@ export default class NewChannelModal extends React.Component {
</div>
<div className='form-group less'>
<div className='col-sm-3'>
<label className='form__label control-label'>{'Purpose'}</label>
<label className='form__label light'>{'(optional)'}</label>
<label className='form__label control-label'>
<FormattedMessage
id='channel_modal.purpose'
defaultMessage='Purpose'
/>
</label>
<label className='form__label light'>
<FormattedMessage
id='channel_modal.optional'
defaultMessage='(optional)'
/>
</label>
</div>
<div className='col-sm-9'>
<textarea
className='form-control no-resize'
ref='channel_purpose'
rows='4'
placeholder='Purpose'
placeholder={this.props.intl.formatMessage({id: 'channel_modal.purpose'})}
maxLength='128'
value={this.props.channelData.purpose}
onChange={this.handleChange}
tabIndex='2'
/>
<p className='input__help'>
{`Describe how this ${channelTerm} should be used.`}
<FormattedMessage
id='channel_modal.descriptionHelp'
defaultMessage='Describe how this {term} should be used.'
values={{
term: (channelTerm)
}}
/>
</p>
{serverError}
</div>
@@ -163,7 +238,10 @@ export default class NewChannelModal extends React.Component {
className='btn btn-default'
onClick={this.props.onModalDismissed}
>
{'Cancel'}
<FormattedMessage
id='channel_modal.cancel'
defaultMessage='Cancel'
/>
</button>
<button
onClick={this.handleSubmit}
@@ -171,7 +249,11 @@ export default class NewChannelModal extends React.Component {
className='btn btn-primary'
tabIndex='3'
>
{'Create New ' + channelTerm}
<FormattedMessage
id='channel_modal.createNew'
defaultMessage='Create New '
/>
{channelTerm}
</button>
</Modal.Footer>
</form>
@@ -187,6 +269,7 @@ NewChannelModal.defaultProps = {
serverError: ''
};
NewChannelModal.propTypes = {
intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
channelType: React.PropTypes.string.isRequired,
channelData: React.PropTypes.object.isRequired,
@@ -197,3 +280,5 @@ NewChannelModal.propTypes = {
onChangeURLPressed: React.PropTypes.func.isRequired,
onDataChanged: React.PropTypes.func.isRequired
};
export default injectIntl(NewChannelModal);

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

@@ -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);

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

@@ -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);

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

@@ -44,19 +44,19 @@ export default class PostInfo extends React.Component {
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>
);
<li
key='replyLink'
role='presentation'
>
<a
className='link__reply theme'
href='#'
onClick={this.props.handleCommentClick}
>
{'Reply'}
</a>
</li>
);
}
dropdownContents.push(
@@ -173,7 +173,7 @@ export default class PostInfo extends React.Component {
>
<span
className='comment-icon'
dangerouslySetInnerHTML={{__html: Constants.COMMENT_ICON}}
dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
/>
{commentCountText}
</a>

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

@@ -7,9 +7,22 @@ import ModalStore from '../stores/modal_store.jsx';
const Modal = ReactBootstrap.Modal;
import Constants from '../utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const ActionTypes = Constants.ActionTypes;
export default class RegisterAppModal extends React.Component {
const holders = defineMessages({
required: {
id: 'register_app.required',
defaultMessage: 'Required'
},
optional: {
id: 'register_app.optional',
defaultMessage: 'Optional'
}
});
class RegisterAppModal extends React.Component {
constructor() {
super();
@@ -60,7 +73,7 @@ export default class RegisterAppModal extends React.Component {
var name = this.refs.name.value;
if (!name || name.length === 0) {
state.nameError = 'Application name must be filled in.';
state.nameError = true;
this.setState(state);
return;
}
@@ -69,7 +82,7 @@ export default class RegisterAppModal extends React.Component {
var homepage = this.refs.homepage.value;
if (!homepage || homepage.length === 0) {
state.homepageError = 'Homepage must be filled in.';
state.homepageError = true;
this.setState(state);
return;
}
@@ -81,7 +94,7 @@ export default class RegisterAppModal extends React.Component {
var rawCallbacks = this.refs.callback.value.trim();
if (!rawCallbacks || rawCallbacks.length === 0) {
state.callbackError = 'At least one callback URL must be filled in.';
state.callbackError = true;
this.setState(state);
return;
}
@@ -112,17 +125,45 @@ export default class RegisterAppModal extends React.Component {
this.setState({saved: this.refs.save.checked});
}
render() {
const {formatMessage} = this.props.intl;
var nameError;
if (this.state.nameError) {
nameError = <div className='form-group has-error'><label className='control-label'>{this.state.nameError}</label></div>;
nameError = (
<div className='form-group has-error'>
<label className='control-label'>
<FormattedMessage
id='register_app.nameError'
defaultMessage='Application name must be filled in.'
/>
</label>
</div>
);
}
var homepageError;
if (this.state.homepageError) {
homepageError = <div className='form-group has-error'><label className='control-label'>{this.state.homepageError}</label></div>;
homepageError = (
<div className='form-group has-error'>
<label className='control-label'>
<FormattedMessage
id='register_app.homepageError'
defaultMessage='Homepage must be filled in.'
/>
</label>
</div>
);
}
var callbackError;
if (this.state.callbackError) {
callbackError = <div className='form-group has-error'><label className='control-label'>{this.state.callbackError}</label></div>;
callbackError = (
<div className='form-group has-error'>
<label className='control-label'>
<FormattedMessage
id='register_app.callbackError'
defaultMessage='At least one callback URL must be filled in.'
/>
</label>
</div>
);
}
var serverError;
if (this.state.serverError) {
@@ -135,50 +176,75 @@ export default class RegisterAppModal extends React.Component {
body = (
<div className='settings-modal'>
<div className='form-horizontal user-settings'>
<h4 className='padding-bottom x3'>{'Register a New Application'}</h4>
<h4 className='padding-bottom x3'>
<FormattedMessage
id='register_app.title'
defaultMessage='Register a New Application'
/>
</h4>
<div className='row'>
<label className='col-sm-4 control-label'>{'Application Name'}</label>
<label className='col-sm-4 control-label'>
<FormattedMessage
id='register_app.name'
defaultMessage='Application Name'
/>
</label>
<div className='col-sm-7'>
<input
ref='name'
className='form-control'
type='text'
placeholder='Required'
placeholder={formatMessage(holders.required)}
/>
{nameError}
</div>
</div>
<div className='row padding-top x2'>
<label className='col-sm-4 control-label'>{'Homepage URL'}</label>
<label className='col-sm-4 control-label'>
<FormattedMessage
id='register_app.homepage'
defaultMessage='Homepage URL'
/>
</label>
<div className='col-sm-7'>
<input
ref='homepage'
className='form-control'
type='text'
placeholder='Required'
placeholder={formatMessage(holders.required)}
/>
{homepageError}
</div>
</div>
<div className='row padding-top x2'>
<label className='col-sm-4 control-label'>{'Description'}</label>
<label className='col-sm-4 control-label'>
<FormattedMessage
id='register_app.description'
defaultMessage='Description'
/>
</label>
<div className='col-sm-7'>
<input
ref='desc'
className='form-control'
type='text'
placeholder='Optional'
placeholder={formatMessage(holders.optional)}
/>
</div>
</div>
<div className='row padding-top padding-bottom x2'>
<label className='col-sm-4 control-label'>{'Callback URL'}</label>
<label className='col-sm-4 control-label'>
<FormattedMessage
id='register_app.callback'
defaultMessage='Callback URL'
/>
</label>
<div className='col-sm-7'>
<textarea
ref='callback'
className='form-control'
type='text'
placeholder='Required'
placeholder={formatMessage(holders.required)}
rows='5'
/>
{callbackError}
@@ -196,7 +262,10 @@ export default class RegisterAppModal extends React.Component {
className='btn btn-default'
onClick={() => this.updateShow(false)}
>
{'Cancel'}
<FormattedMessage
id='register_app.cancel'
defaultMessage='Cancel'
/>
</button>
<button
onClick={this.handleSubmit}
@@ -204,7 +273,10 @@ export default class RegisterAppModal extends React.Component {
className='btn btn-primary'
tabIndex='3'
>
{'Register'}
<FormattedMessage
id='register_app.register'
defaultMessage='Register'
/>
</button>
</div>
);
@@ -216,10 +288,20 @@ export default class RegisterAppModal extends React.Component {
body = (
<div className='form-horizontal user-settings'>
<h4 className='padding-bottom x3'>{'Your Application Credentials'}</h4>
<h4 className='padding-bottom x3'>
<FormattedMessage
id='register_app.credentialsTitle'
defaultMessage='Your Application Credentials'
/>
</h4>
<br/>
<div className='row'>
<label className='col-sm-4 control-label'>{'Client ID'}</label>
<label className='col-sm-4 control-label'>
<FormattedMessage
id='register_app.clientId'
defaultMessage='Client ID'
/>
</label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -231,7 +313,11 @@ export default class RegisterAppModal extends React.Component {
</div>
<br/>
<div className='row padding-top x2'>
<label className='col-sm-4 control-label'>{'Client Secret'}</label>
<label className='col-sm-4 control-label'>
<FormattedMessage
id='register_app.clientSecret'
defaultMessage='Client Secret'
/></label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -243,7 +329,12 @@ export default class RegisterAppModal extends React.Component {
</div>
<br/>
<br/>
<strong>{'Save these somewhere SAFE and SECURE. Treat your Client ID as your app\'s username and your Client Secret as the app\'s password.'}</strong>
<strong>
<FormattedMessage
id='register_app.credentialsDescription'
defaultMessage="Save these somewhere SAFE and SECURE. Treat your Client ID as your app's username and your Client Secret as the app's password."
/>
</strong>
<br/>
<br/>
<div className='checkbox'>
@@ -254,7 +345,10 @@ export default class RegisterAppModal extends React.Component {
checked={this.state.saved}
onChange={this.save}
/>
{'I have saved both my Client Id and Client Secret somewhere safe'}
<FormattedMessage
id='register_app.credentialsSave'
defaultMessage='I have saved both my Client Id and Client Secret somewhere safe'
/>
</label>
</div>
</div>
@@ -269,7 +363,10 @@ export default class RegisterAppModal extends React.Component {
this.updateShow(false);
}}
>
{'Close'}
<FormattedMessage
id='register_app.close'
defaultMessage='Close'
/>
</a>
);
}
@@ -281,7 +378,12 @@ export default class RegisterAppModal extends React.Component {
onHide={() => this.updateShow(false)}
>
<Modal.Header closeButton={true}>
<Modal.Title>{'Developer Applications'}</Modal.Title>
<Modal.Title>
<FormattedMessage
id='register_app.dev'
defaultMessage='Developer Applications'
/>
</Modal.Title>
</Modal.Header>
<form
role='form'
@@ -300,3 +402,8 @@ export default class RegisterAppModal extends React.Component {
}
}
RegisterAppModal.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(RegisterAppModal);

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

@@ -16,7 +16,16 @@ import * as TextFormatting from '../utils/text_formatting.jsx';
import twemoji from 'twemoji';
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
export default class RhsComment extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedDate} from 'mm-intl';
const holders = defineMessages({
comment: {
id: 'rhs_comment.comment',
defaultMessage: 'Comment'
}
});
class RhsComment extends React.Component {
constructor(props) {
super(props);
@@ -95,12 +104,15 @@ export default class RhsComment extends React.Component {
data-toggle='modal'
data-target='#edit_post'
data-refocusid='#reply_textbox'
data-title='Comment'
data-title={this.props.intl.formatMessage(holders.comment)}
data-message={post.message}
data-postid={post.id}
data-channelid={post.channel_id}
>
{'Edit'}
<FormattedMessage
id='rhs_comment.edit'
defaultMessage='Edit'
/>
</a>
</li>
);
@@ -117,7 +129,10 @@ export default class RhsComment extends React.Component {
role='menuitem'
onClick={() => EventHelpers.showDeletePostModal(post, 0)}
>
{'Delete'}
<FormattedMessage
id='rhs_comment.del'
defaultMessage='Delete'
/>
</a>
</li>
);
@@ -165,7 +180,10 @@ export default class RhsComment extends React.Component {
href='#'
onClick={this.retryComment}
>
{'Retry'}
<FormattedMessage
id='rhs_comment.retry'
defaultMessage='Retry'
/>
</a>
);
} else if (post.state === Constants.POST_LOADING) {
@@ -208,7 +226,15 @@ export default class RhsComment extends React.Component {
</li>
<li className='col'>
<time className='post__time'>
{Utils.displayCommentDateTime(post.create_at)}
<FormattedDate
value={post.create_at}
day='numeric'
month='long'
year='numeric'
hour12={true}
hour='2-digit'
minute='2-digit'
/>
</time>
</li>
<li className='col col__reply'>
@@ -237,5 +263,8 @@ RhsComment.defaultProps = {
post: null
};
RhsComment.propTypes = {
intl: intlShape.isRequired,
post: React.PropTypes.object
};
export default injectIntl(RhsComment);

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

@@ -3,6 +3,9 @@
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import Constants from '../utils/constants.jsx';
import {FormattedMessage} from 'mm-intl';
const ActionTypes = Constants.ActionTypes;
export default class RhsHeaderPost extends React.Component {
@@ -58,7 +61,13 @@ export default class RhsHeaderPost extends React.Component {
return (
<div className='sidebar--right__header'>
<span className='sidebar--right__title'>{back}Message Details</span>
<span className='sidebar--right__title'>
{back}
<FormattedMessage
id='rhs_header.details'
defaultMessage='Message Details'
/>
</span>
<button
type='button'
className='sidebar--right__close'

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

@@ -14,6 +14,8 @@ import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import Constants from '../utils/constants.jsx';
import {FormattedMessage, FormattedDate} from 'mm-intl';
export default class RhsRootPost extends React.Component {
constructor(props) {
super(props);
@@ -68,7 +70,12 @@ export default class RhsRootPost extends React.Component {
var channelName;
if (channel) {
if (channel.type === 'D') {
channelName = 'Direct Message';
channelName = (
<FormattedMessage
id='rhs_root.direct'
defaultMessage='Direct Message'
/>
);
} else {
channelName = channel.display_name;
}
@@ -93,7 +100,10 @@ export default class RhsRootPost extends React.Component {
data-postid={post.id}
data-channelid={post.channel_id}
>
{'Edit'}
<FormattedMessage
id='rhs_root.edit'
defaultMessage='Edit'
/>
</a>
</li>
);
@@ -110,7 +120,10 @@ export default class RhsRootPost extends React.Component {
role='menuitem'
onClick={() => EventHelpers.showDeletePostModal(post, this.props.commentCount)}
>
{'Delete'}
<FormattedMessage
id='rhs_root.del'
defaultMessage='Delete'
/>
</a>
</li>
);
@@ -205,7 +218,15 @@ export default class RhsRootPost extends React.Component {
{botIndicator}
<li className='col'>
<time className='post__time'>
{utils.displayCommentDateTime(post.create_at)}
<FormattedDate
value={post.create_at}
day='numeric'
month='long'
year='numeric'
hour12={true}
hour='2-digit'
minute='2-digit'
/>
</time>
</li>
<li className='col col__reply'>

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

@@ -11,10 +11,20 @@ import SearchSuggestionList from './suggestion/search_suggestion_list.jsx';
import SearchUserProvider from './suggestion/search_user_provider.jsx';
import * as utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
var ActionTypes = Constants.ActionTypes;
var Popover = ReactBootstrap.Popover;
export default class SearchBar extends React.Component {
const holders = defineMessages({
search: {
id: 'search_bar.search',
defaultMessage: 'Search'
}
});
class SearchBar extends React.Component {
constructor() {
super();
this.mounted = false;
@@ -147,7 +157,10 @@ export default class SearchBar extends React.Component {
className='search__clear'
onClick={this.clearFocus}
>
{'Cancel'}
<FormattedMessage
id='search_bar.cancel'
defaultMessage='Cancel'
/>
</span>
<form
role='form'
@@ -160,7 +173,7 @@ export default class SearchBar extends React.Component {
<SuggestionBox
ref='search'
className='form-control search-bar'
placeholder='Search'
placeholder={this.props.intl.formatMessage(holders.search)}
value={this.state.searchTerm}
onFocus={this.handleUserFocus}
onBlur={this.handleUserBlur}
@@ -174,18 +187,20 @@ export default class SearchBar extends React.Component {
placement='bottom'
className={helpClass}
>
<h4>{'Search Options'}</h4>
<ul>
<li>
<span>{'Use '}</span><b>{'"quotation marks"'}</b><span>{' to search for phrases'}</span>
</li>
<li>
<span>{'Use '}</span><b>{'from:'}</b><span>{' to find posts from specific users and '}</span><b>{'in:'}</b><span>{' to find posts in specific channels'}</span>
</li>
</ul>
<FormattedHTMLMessage
id='search_bar.usage'
defaultMessage='<h4>Search Options</h4><ul><li><span>Use </span><b>"quotation marks"</b><span> to search for phrases</span></li><li><span>Use </span><b>from:</b><span> to find posts from specific users and </span><b>in:</b><span> to find posts in specific channels</span></li></ul>'
/>
</Popover>
</form>
</div>
);
}
}
SearchBar.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(SearchBar);

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

@@ -8,6 +8,8 @@ import * as Utils from '../utils/utils.jsx';
import SearchResultsHeader from './search_results_header.jsx';
import SearchResultsItem from './search_results_item.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
function getStateFromStores() {
return {results: SearchStore.getSearchResults()};
}
@@ -83,25 +85,29 @@ export default class SearchResults extends React.Component {
if (!searchTerm && noResults) {
ctls = (
<div className='sidebar--right__subheader'>
<ul>
<li>
{'Use '}<b>{'"quotation marks"'}</b>{' to search for phrases'}
</li>
<li>
{'Use '}<b>{'from:'}</b>{' to find posts from specific users and '}<b>{'in:'}</b>{' to find posts in specific channels'}
</li>
</ul>
<FormattedHTMLMessage
id='search_results.usage'
defaultMessage='<ul><li>Use <b>"quotation marks"</b> to search for phrases</li><li>Use <b>from:</b> to find posts from specific users and <b>in:</b> to find posts in specific channels</li></ul>'
/>
</div>
);
} else if (noResults) {
ctls =
(
<div className='sidebar--right__subheader'>
<h4>{'NO RESULTS'}</h4>
<ul>
<li>{'If you\'re searching a partial phrase (ex. searching "rea", looking for "reach" or "reaction"), append a * to your search term'}</li>
<li>{'Due to the volume of results, two letter searches and common words like "this", "a" and "is" won\'t appear in search results'}</li>
</ul>
<h4>
<FormattedMessage
id='search_results.noResults'
defaultMessage='NO RESULTS'
/>
</h4>
<FormattedHTMLMessage
id='search_results.because'
defaultMessage='<ul>
<li>If you&#39;re searching a partial phrase (ex. searching "rea", looking for "reach" or "reaction"), append a * to your search term</li>
<li>Due to the volume of results, two letter searches and common words like "this", "a" and "is" won&#39;t appear in search results</li>
</ul>'
/>
</div>
);
} else {

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

@@ -3,6 +3,9 @@
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import Constants from '../utils/constants.jsx';
import {FormattedMessage} from 'mm-intl';
var ActionTypes = Constants.ActionTypes;
export default class SearchResultsHeader extends React.Component {
@@ -34,10 +37,20 @@ export default class SearchResultsHeader extends React.Component {
}
render() {
var title = 'Search Results';
var title = (
<FormattedMessage
id='search_header.results'
defaultMessage='Search Results'
/>
);
if (this.props.isMentionSearch) {
title = 'Recent Mentions';
title = (
<FormattedMessage
id='search_header.title2'
defaultMessage='Recent Mentions'
/>
);
}
return (

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

@@ -10,6 +10,8 @@ import * as TextFormatting from '../utils/text_formatting.jsx';
import Constants from '../utils/constants.jsx';
import {FormattedMessage, FormattedDate} from 'mm-intl';
export default class SearchResultsItem extends React.Component {
constructor(props) {
super(props);
@@ -42,7 +44,12 @@ export default class SearchResultsItem extends React.Component {
if (channel) {
channelName = channel.display_name;
if (channel.type === 'D') {
channelName = 'Direct Message';
channelName = (
<FormattedMessage
id='search_item.direct'
defaultMessage='Direct Message'
/>
);
}
}
@@ -69,7 +76,15 @@ export default class SearchResultsItem extends React.Component {
<li className='col__name'><strong><UserProfile userId={this.props.post.user_id} /></strong></li>
<li className='col'>
<time className='search-item-time'>
{utils.displayDate(this.props.post.create_at) + ' ' + utils.displayTime(this.props.post.create_at)}
<FormattedDate
value={this.props.post.create_at}
day='numeric'
month='long'
year='numeric'
hour12={true}
hour='2-digit'
minute='2-digit'
/>
</time>
</li>
<li>
@@ -78,7 +93,10 @@ export default class SearchResultsItem extends React.Component {
className='search-item__jump'
onClick={this.handleClick}
>
{'Jump'}
<FormattedMessage
id='search_item.jump'
defaultMessage='Jump'
/>
</a>
</li>
<li>
@@ -89,7 +107,7 @@ export default class SearchResultsItem extends React.Component {
>
<span
className='comment-icon'
dangerouslySetInnerHTML={{__html: Constants.COMMENT_ICON}}
dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
/>
</a>
</li>

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

@@ -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 SettingItemMax extends React.Component {
render() {
var clientError = null;
@@ -26,7 +28,10 @@ export default class SettingItemMax extends React.Component {
href='#'
onClick={this.props.submit}
>
Save
<FormattedMessage
id='setting_item_max.save'
defaultMessage='Save'
/>
</a>
);
}
@@ -60,7 +65,10 @@ export default class SettingItemMax extends React.Component {
href='#'
onClick={this.props.updateSection}
>
Cancel
<FormattedMessage
id='setting_item_max.cancel'
defaultMessage='Cancel'
/>
</a>
</li>
</ul>

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

@@ -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 SettingItemMin extends React.Component {
render() {
let editButton = null;
@@ -13,7 +15,10 @@ export default class SettingItemMin extends React.Component {
onClick={this.props.updateSection}
>
<i className='fa fa-pencil'/>
{'Edit'}
<FormattedMessage
id='setting_item_min.edit'
defaultMessage='Edit'
/>
</a>
</li>
);

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

@@ -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 SettingPicture extends React.Component {
constructor(props) {
super(props);
@@ -76,10 +78,24 @@ export default class SettingPicture extends React.Component {
<a
className={confirmButtonClass}
onClick={this.props.submit}
>Save</a>
>
<FormattedMessage
id='setting_picture.save'
defaultMessage='Save'
/>
</a>
);
}
var helpText = 'Upload a profile picture in either JPG or PNG format, at least ' + global.window.mm_config.ProfileWidth + 'px in width and ' + global.window.mm_config.ProfileHeight + 'px height.';
var helpText = (
<FormattedMessage
id='setting_picture.help'
defaultMessage='Upload a profile picture in either JPG or PNG format, at least {width}px in width and {height}px height.'
values={{
width: global.window.mm_config.ProfileWidth,
height: global.window.mm_config.ProfileHeight
}}
/>
);
var self = this;
return (
@@ -97,7 +113,10 @@ export default class SettingPicture extends React.Component {
{serverError}
{clientError}
<span className='btn btn-sm btn-primary btn-file sel-btn'>
Select
<FormattedMessage
id='setting_picture.select'
defaultMessage='Select'
/>
<input
ref='input'
accept='.jpg,.png,.bmp'
@@ -110,7 +129,12 @@ export default class SettingPicture extends React.Component {
className='btn btn-sm theme'
href='#'
onClick={self.props.updateSection}
>Cancel</a>
>
<FormattedMessage
id='setting_picture.cancel'
defaultMessage='Cancel'
/>
</a>
</li>
</ul>
</li>

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

@@ -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 SettingsUpload extends React.Component {
constructor(props) {
super(props);
@@ -41,7 +43,7 @@ export default class SettingsUpload extends React.Component {
if (inputnode.files && inputnode.files[0]) {
this.props.submit(inputnode.files[0]);
} else {
this.setState({clientError: 'No file selected.'});
this.setState({clientError: true});
}
}
@@ -49,7 +51,12 @@ export default class SettingsUpload extends React.Component {
let clientError = null;
if (this.state.clientError) {
clientError = (
<div className='file-status'>{this.state.clientError}</div>
<div className='file-status'>
<FormattedMessage
id='setting_upload.noFile'
defaultMessage='No file selected.'
/>
</div>
);
}
let serverError = null;
@@ -75,7 +82,10 @@ export default class SettingsUpload extends React.Component {
<ul className='setting-list'>
<li className='setting-list-item'>
<span className='btn btn-sm btn-primary btn-file sel-btn'>
{'Select file'}
<FormattedMessage
id='setting_upload.select'
defaultMessage='Select file'
/>
<input
ref='uploadinput'
accept={this.props.fileTypesAccepted}
@@ -87,7 +97,10 @@ export default class SettingsUpload extends React.Component {
className={submitButtonClass}
onClick={this.doSubmit}
>
{'Import'}
<FormattedMessage
id='setting_upload.import'
defaultMessage='Import'
/>
</a>
{fileNameText}
{serverError}

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

@@ -17,6 +17,9 @@ import * as Client from '../utils/client.jsx';
import * as Utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const Preferences = Constants.Preferences;
const TutorialSteps = Constants.TutorialSteps;
@@ -182,7 +185,10 @@ export default class Sidebar extends React.Component {
let currentChannelName = channel.display_name;
if (channel.type === 'D') {
currentChannelName = Utils.getDirectTeammate(channel.id).username;
const teammate = Utils.getDirectTeammate(channel.id);
if (teammate != null) {
currentChannelName = teammate.username;
}
}
const unread = this.getTotalUnreadCount();
@@ -275,34 +281,33 @@ export default class Sidebar extends React.Component {
screens.push(
<div>
<h4>{'Channels'}</h4>
<p><strong>{'Channels'}</strong>{' organize conversations across different topics. Theyre open to everyone on your team. To send private communications use '}<strong>{'Direct Messages'}</strong>{' for a single person or '}<strong>{'Private Groups'}</strong>{' for multiple people.'}
</p>
<FormattedHTMLMessage
id='sidebar.tutorialScreen1'
defaultMessage='<h4>Channels</h4><p><strong>Channels</strong> organize conversations across different topics. Theyre open to everyone on your team. To send private communications use <strong>Direct Messages</strong> for a single person or <strong>Private Groups</strong> for multiple people.</p>'
/>
</div>
);
screens.push(
<div>
<h4>{'"Town Square" and "Off-Topic" channels'}</h4>
<p>{'Here are two public channels to start:'}</p>
<p>
<strong>{'Town Square'}</strong>{' is a place for team-wide communication. Everyone in your team is a member of this channel.'}
</p>
<p>
<strong>{'Off-Topic'}</strong>{' is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.'}
</p>
<FormattedHTMLMessage
id='sidebar.tutorialScreen2'
defaultMessage='<h4>"Town Square" and "Off-Topic" channels</h4>
<p>Here are two public channels to start:</p>
<p><strong>Town Square</strong> is a place for team-wide communication. Everyone in your team is a member of this channel.</p>
<p><strong>Off-Topic</strong> is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.</p>'
/>
</div>
);
screens.push(
<div>
<h4>{'Creating and Joining Channels'}</h4>
<p>
{'Click '}<strong>{'"More..."'}</strong>{' to create a new channel or join an existing one.'}
</p>
<p>
{'You can also create a new channel or private group by clicking the '}<strong>{'"+" symbol'}</strong>{' next to the channel or private group header.'}
</p>
<FormattedHTMLMessage
id='sidebar.tutorialScreen3'
defaultMessage='<h4>Creating and Joining Channels</h4>
<p>Click <strong>"More..."</strong> to create a new channel or join an existing one.</p>
<p>You can also create a new channel or private group by clicking the <strong>"+" symbol</strong> next to the channel or private group header.</p>'
/>
</div>
);
@@ -437,7 +442,12 @@ export default class Sidebar extends React.Component {
let closeButton = null;
const removeTooltip = (
<Tooltip id='remove-dm-tooltip'>{'Remove from list'}</Tooltip>
<Tooltip id='remove-dm-tooltip'>
<FormattedMessage
id='sidebar.removeList'
defaultMessage='Remove from list'
/>
</Tooltip>
);
if (handleClose && !badge) {
closeButton = (
@@ -525,7 +535,13 @@ export default class Sidebar extends React.Component {
href='#'
onClick={this.showMoreDirectChannelsModal}
>
{'More (' + this.state.hiddenDirectChannelCount + ')'}
<FormattedMessage
id='sidebar.more'
defaultMessage='More ({count})'
values={{
count: this.state.hiddenDirectChannelCount
}}
/>
</a>
</li>
);
@@ -537,10 +553,34 @@ export default class Sidebar extends React.Component {
}
const createChannelTootlip = (
<Tooltip id='new-channel-tooltip' >{'Create new channel'}</Tooltip>
<Tooltip id='new-channel-tooltip' >
<FormattedMessage
id='sidebar.createChannel'
defaultMessage='Create new channel'
/>
</Tooltip>
);
const createGroupTootlip = (
<Tooltip id='new-group-tooltip'>{'Create new group'}</Tooltip>
<Tooltip id='new-group-tooltip'>
<FormattedMessage
id='sidebar.createGroup'
defaultMessage='Create new group'
/>
</Tooltip>
);
const above = (
<FormattedMessage
id='sidebar.unreadAbove'
defaultMessage='Unread post(s) above'
/>
);
const below = (
<FormattedMessage
id='sidebar.unreadBelow'
defaultMessage='Unread post(s) below'
/>
);
return (
@@ -564,12 +604,12 @@ export default class Sidebar extends React.Component {
<UnreadChannelIndicator
show={this.state.showTopUnread}
extraClass='nav-pills__unread-indicator-top'
text={'Unread post(s) above'}
text={above}
/>
<UnreadChannelIndicator
show={this.state.showBottomUnread}
extraClass='nav-pills__unread-indicator-bottom'
text={'Unread post(s) below'}
text={below}
/>
<div
@@ -580,7 +620,10 @@ export default class Sidebar extends React.Component {
<ul className='nav nav-pills nav-stacked'>
<li>
<h4>
{'Channels'}
<FormattedMessage
id='sidebar.channels'
defaultMessage='Channels'
/>
<OverlayTrigger
delayShow={500}
placement='top'
@@ -603,7 +646,10 @@ export default class Sidebar extends React.Component {
className='nav-more'
onClick={this.showMoreChannelsModal}
>
{'More...'}
<FormattedMessage
id='sidebar.moreElips'
defaultMessage='More...'
/>
</a>
</li>
</ul>
@@ -611,7 +657,10 @@ export default class Sidebar extends React.Component {
<ul className='nav nav-pills nav-stacked'>
<li>
<h4>
{'Private Groups'}
<FormattedMessage
id='sidebar.pg'
defaultMessage='Private Groups'
/>
<OverlayTrigger
delayShow={500}
placement='top'
@@ -630,7 +679,14 @@ export default class Sidebar extends React.Component {
{privateChannelItems}
</ul>
<ul className='nav nav-pills nav-stacked'>
<li><h4>{'Direct Messages'}</h4></li>
<li>
<h4>
<FormattedMessage
id='sidebar.direct'
defaultMessage='Direct Messages'
/>
</h4>
</li>
{directMessageItems}
{directMessageMore}
</ul>

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

@@ -9,6 +9,9 @@ import PreferenceStore from '../stores/preference_store.jsx';
import * as Utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
import {FormattedHTMLMessage} from 'mm-intl';
const Preferences = Constants.Preferences;
const TutorialSteps = Constants.TutorialSteps;
@@ -51,20 +54,12 @@ export default class SidebarHeader extends React.Component {
screens.push(
<div>
<h4>{'Main Menu'}</h4>
<p>
{'The '}<strong>{'Main Menu'}</strong>{' is where you can '}
<strong>{'Invite New Members'}</strong>
{', access your '}
<strong>{'Account Settings'}</strong>
{' and set your '}<strong>{'Theme Color'}</strong>{'.'}
</p>
<p>
{'Team administrators can also access their '}<strong>{'Team Settings'}</strong>{' from this menu.'}
</p>
<p>
{'System administrators will find a '}<strong>{'System Console'}</strong>{' option to administrate the entire system.'}
</p>
<FormattedHTMLMessage
id='sidebar_header.tutorial'
defaultMessage='<h4>Main Menu</h4>
<p>The <strong>Main Menu</strong> is where you can <strong>Invite New Members</strong>, access your <strong>Account Settings</strong> and set your <strong>Theme Color</strong>.</p>
<p>Team administrators can also access their <strong>Team Settings</strong> from this menu.</p><p>System administrators will find a <strong>System Console</strong> option to administrate the entire system.</p>'
/>
</div>
);

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

@@ -9,6 +9,8 @@ import * as client from '../utils/client.jsx';
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import * as utils from '../utils/utils.jsx';
import {FormattedMessage} from 'mm-intl';
export default class SidebarRightMenu extends React.Component {
componentDidMount() {
$('.sidebar--left .dropdown-menu').perfectScrollbar();
@@ -49,7 +51,11 @@ export default class SidebarRightMenu extends React.Component {
href='#'
onClick={EventHelpers.showInviteMemberModal}
>
<i className='fa fa-user'></i>{'Invite New Member'}
<i className='fa fa-user'></i>
<FormattedMessage
id='sidebar_right_menu.inviteNew'
defaultMessage='Invite New Member'
/>
</a>
</li>
);
@@ -61,7 +67,11 @@ export default class SidebarRightMenu extends React.Component {
href='#'
onClick={EventHelpers.showGetTeamInviteLinkModal}
>
<i className='glyphicon glyphicon-link'></i>{'Get Team Invite Link'}
<i className='glyphicon glyphicon-link'></i>
<FormattedMessage
id='sidebar_right_menu.teamLink'
defaultMessage='Get Team Invite Link'
/>
</a>
</li>
);
@@ -75,13 +85,23 @@ export default class SidebarRightMenu extends React.Component {
href='#'
data-toggle='modal'
data-target='#team_settings'
><i className='fa fa-globe'></i>{'Team Settings'}</a>
>
<i className='fa fa-globe'></i>
<FormattedMessage
id='sidebar_right_menu.teamSettings'
defaultMessage='Team Settings'
/>
</a>
</li>
);
manageLink = (
<li>
<ToggleModalButton dialogType={TeamMembersModal}>
<i className='fa fa-users'></i>{'Manage Members'}
<i className='fa fa-users'></i>
<FormattedMessage
id='sidebar_right_menu.manageMembers'
defaultMessage='Manage Members'
/>
</ToggleModalButton>
</li>
);
@@ -93,7 +113,12 @@ export default class SidebarRightMenu extends React.Component {
<a
href={'/admin_console?' + utils.getSessionIndex()}
>
<i className='fa fa-wrench'></i>{'System Console'}</a>
<i className='fa fa-wrench'></i>
<FormattedMessage
id='sidebar_right_menu.console'
defaultMessage='System Console'
/>
</a>
</li>
);
}
@@ -114,7 +139,14 @@ export default class SidebarRightMenu extends React.Component {
<a
target='_blank'
href={global.window.mm_config.HelpLink}
><i className='fa fa-question'></i>{'Help'}</a></li>
>
<i className='fa fa-question'></i>
<FormattedMessage
id='sidebar_right_menu.help'
defaultMessage='Help'
/>
</a>
</li>
);
}
@@ -125,7 +157,14 @@ export default class SidebarRightMenu extends React.Component {
<a
target='_blank'
href={global.window.mm_config.ReportAProblemLink}
><i className='fa fa-phone'></i>{'Report a Problem'}</a></li>
>
<i className='fa fa-phone'></i>
<FormattedMessage
id='sidebar_right_menu.report'
defaultMessage='Report a Problem'
/>
</a>
</li>
);
}
return (
@@ -144,7 +183,11 @@ export default class SidebarRightMenu extends React.Component {
href='#'
onClick={() => this.setState({showUserSettingsModal: true})}
>
<i className='fa fa-cog'></i>{'Account Settings'}
<i className='fa fa-cog'></i>
<FormattedMessage
id='sidebar_right_menu.accountSettings'
defaultMessage='Account Settings'
/>
</a>
</li>
{teamSettingsLink}
@@ -156,7 +199,14 @@ export default class SidebarRightMenu extends React.Component {
<a
href='#'
onClick={this.handleLogoutClick}
><i className='fa fa-sign-out'></i>{'Logout'}</a></li>
>
<i className='fa fa-sign-out'></i>
<FormattedMessage
id='sidebar_right_menu.logout'
defaultMessage='Logout'
/>
</a>
</li>
<li className='divider'></li>
{helpLink}
{reportLink}

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

@@ -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>
);
}
}
}

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

@@ -9,6 +9,8 @@ import UsernamePage from './team_signup_username_page.jsx';
import PasswordPage from './team_signup_password_page.jsx';
import BrowserStore from '../stores/browser_store.jsx';
import {FormattedMessage} from 'mm-intl';
export default class SignupTeamComplete extends React.Component {
constructor(props) {
super(props);
@@ -96,7 +98,14 @@ export default class SignupTeamComplete extends React.Component {
);
}
return (<div>You've already completed the signup process for this invitation or this invitation has expired.</div>);
return (
<div>
<FormattedMessage
id='signup_team_complete.completed'
defaultMessage="You've already completed the signup process for this invitation or this invitation has expired."
/>
</div>
);
}
}

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

@@ -0,0 +1,39 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
export default class SignupTeamConfirm extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className='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.email
}}
/>
</p>
</div>
);
}
}
SignupTeamConfirm.defaultProps = {
email: ''
};
SignupTeamConfirm.propTypes = {
email: React.PropTypes.string
};

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

@@ -7,7 +7,32 @@ import UserStore from '../stores/user_store.jsx';
import BrowserStore from '../stores/browser_store.jsx';
import Constants from '../utils/constants.jsx';
export default class SignupUserComplete extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const holders = defineMessages({
required: {
id: 'signup_user_completed.required',
defaultMessage: 'This field is required'
},
validEmail: {
id: 'signup_user_completed.validEmail',
defaultMessage: 'Please enter a valid email address'
},
reserved: {
id: 'signup_user_completed.reserved',
defaultMessage: 'This username is reserved, please choose a new one.'
},
usernameLength: {
id: 'signup_user_completed.usernameLength',
defaultMessage: 'Username must begin with a letter, and contain between {min} to {max} lowercase characters made up of numbers, letters, and the symbols \'.\', \'-\' and \'_\'.'
},
passwordLength: {
id: 'signup_user_completed.passwordLength',
defaultMessage: 'Please enter at least {min} characters'
}
});
class SignupUserComplete extends React.Component {
constructor(props) {
super(props);
@@ -29,30 +54,31 @@ export default class SignupUserComplete extends React.Component {
handleSubmit(e) {
e.preventDefault();
const {formatMessage} = this.props.intl;
const providedEmail = ReactDOM.findDOMNode(this.refs.email).value.trim();
if (!providedEmail) {
this.setState({nameError: '', emailError: 'This field is required', passwordError: ''});
this.setState({nameError: '', emailError: formatMessage(holders.required), passwordError: ''});
return;
}
if (!Utils.isEmail(providedEmail)) {
this.setState({nameError: '', emailError: 'Please enter a valid email address', passwordError: ''});
this.setState({nameError: '', emailError: formatMessage(holders.validEmail), passwordError: ''});
return;
}
const providedUsername = ReactDOM.findDOMNode(this.refs.name).value.trim().toLowerCase();
if (!providedUsername) {
this.setState({nameError: 'This field is required', emailError: '', passwordError: '', serverError: ''});
this.setState({nameError: formatMessage(holders.required), emailError: '', passwordError: '', serverError: ''});
return;
}
const usernameError = Utils.isValidUsername(providedUsername);
if (usernameError === 'Cannot use a reserved word as a username.') {
this.setState({nameError: 'This username is reserved, please choose a new one.', emailError: '', passwordError: '', serverError: ''});
this.setState({nameError: formatMessage(holders.reserved), emailError: '', passwordError: '', serverError: ''});
return;
} else if (usernameError) {
this.setState({
nameError: 'Username must begin with a letter, and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + ' lowercase characters made up of numbers, letters, and the symbols \'.\', \'-\' and \'_\'.',
nameError: formatMessage(holders.usernameLength, {min: Constants.MIN_USERNAME_LENGTH, max: Constants.MAX_USERNAME_LENGTH}),
emailError: '',
passwordError: '',
serverError: ''
@@ -62,7 +88,7 @@ export default class SignupUserComplete extends React.Component {
const providedPassword = ReactDOM.findDOMNode(this.refs.password).value.trim();
if (!providedPassword || providedPassword.length < Constants.MIN_PASSWORD_LENGTH) {
this.setState({nameError: '', emailError: '', passwordError: 'Please enter at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters', serverError: ''});
this.setState({nameError: '', emailError: '', passwordError: formatMessage(holders.passwordLength, {min: Constants.MIN_PASSWORD_LENGTH}), serverError: ''});
return;
}
@@ -95,7 +121,7 @@ export default class SignupUserComplete extends React.Component {
window.location.href = '/' + this.props.teamName + '/channels/town-square';
},
(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?email=' + encodeURIComponent(user.email) + '&teamname=' + encodeURIComponent(this.props.teamName);
} else {
this.setState({serverError: err.message});
@@ -112,7 +138,14 @@ export default class SignupUserComplete extends React.Component {
client.track('signup', 'signup_user_01_welcome');
if (this.state.wizard === 'finished') {
return <div>{"You've already completed the signup process for this invitation or this invitation has expired."}</div>;
return (
<div>
<FormattedMessage
id='signup_user_completed.expired'
defaultMessage="You've already completed the signup process for this invitation or this invitation has expired."
/>
</div>
);
}
// set up error labels
@@ -124,7 +157,18 @@ export default class SignupUserComplete extends React.Component {
}
var nameError = null;
var nameHelpText = <span className='help-block'>{'Username must begin with a letter, and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + " lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'"}</span>;
var nameHelpText = (
<span className='help-block'>
<FormattedMessage
id='signup_user_completed.userHelp'
defaultMessage="Username must begin with a letter, and contain between {min} to {max} lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'"
values={{
min: Constants.MIN_USERNAME_LENGTH,
max: Constants.MAX_USERNAME_LENGTH
}}
/>
</span>
);
var nameDivStyle = 'form-group';
if (this.state.nameError) {
nameError = <label className='control-label'>{this.state.nameError}</label>;
@@ -151,7 +195,16 @@ export default class SignupUserComplete extends React.Component {
// set up the email entry and hide it if an email was provided
var yourEmailIs = '';
if (this.state.user.email) {
yourEmailIs = <span>{'Your email address is '}<strong>{this.state.user.email}</strong>{". You'll use this address to sign in to " + global.window.mm_config.SiteName + '.'}</span>;
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.user.email,
siteName: global.window.mm_config.SiteName
}}
/>
);
}
var emailContainerStyle = 'margin--extra';
@@ -161,7 +214,12 @@ export default class SignupUserComplete extends React.Component {
var email = (
<div className={emailContainerStyle}>
<h5><strong>{"What's your email address?"}</strong></h5>
<h5><strong>
<FormattedMessage
id='signup_user_completed.whatis'
defaultMessage="What's your email address?"
/>
</strong></h5>
<div className={emailDivStyle}>
<input
type='email'
@@ -183,10 +241,16 @@ export default class SignupUserComplete extends React.Component {
signupMessage.push(
<a
className='btn btn-custom-login gitlab'
key='gitlab'
href={'/' + this.props.teamName + '/signup/gitlab' + window.location.search}
>
<span className='icon' />
<span>{'with GitLab'}</span>
<span>
<FormattedMessage
id='signup_user_completed.gitlab'
defaultMessage='with GitLab'
/>
</span>
</a>
);
}
@@ -195,10 +259,16 @@ export default class SignupUserComplete extends React.Component {
signupMessage.push(
<a
className='btn btn-custom-login google'
key='google'
href={'/' + this.props.teamName + '/signup/google' + window.location.search}
>
<span className='icon' />
<span>{'with Google'}</span>
<span>
<FormattedMessage
id='signup_user_completed.google'
defaultMessage='with Google'
/>
</span>
</a>
);
}
@@ -211,7 +281,12 @@ export default class SignupUserComplete extends React.Component {
{email}
{yourEmailIs}
<div className='margin--extra'>
<h5><strong>{'Choose your username'}</strong></h5>
<h5><strong>
<FormattedMessage
id='signup_user_completed.chooseUser'
defaultMessage='Choose your username'
/>
</strong></h5>
<div className={nameDivStyle}>
<input
type='text'
@@ -226,7 +301,12 @@ export default class SignupUserComplete extends React.Component {
</div>
</div>
<div className='margin--extra'>
<h5><strong>{'Choose your password'}</strong></h5>
<h5><strong>
<FormattedMessage
id='signup_user_completed.choosePwd'
defaultMessage='Choose your password'
/>
</strong></h5>
<div className={passwordDivStyle}>
<input
type='password'
@@ -246,7 +326,10 @@ export default class SignupUserComplete extends React.Component {
onClick={this.handleSubmit}
className='btn-primary btn'
>
{'Create Account'}
<FormattedMessage
id='signup_user_completed.create'
defaultMessage='Create Account'
/>
</button>
</p>
</div>
@@ -258,7 +341,12 @@ export default class SignupUserComplete extends React.Component {
<div>
{signupMessage}
<div className='or__container'>
<span>{'or'}</span>
<span>
<FormattedMessage
id='signup_user_completed.or'
defaultMessage='or'
/>
</span>
</div>
</div>
);
@@ -271,10 +359,28 @@ export default class SignupUserComplete extends React.Component {
className='signup-team-logo'
src='/static/images/logo.png'
/>
<h5 className='margin--less'>{'Welcome to:'}</h5>
<h5 className='margin--less'>
<FormattedMessage
id='signup_user_completed.welcome'
defaultMessage='Welcome to:'
/>
</h5>
<h2 className='signup-team__name'>{this.props.teamDisplayName}</h2>
<h2 className='signup-team__subdomain'>{'on ' + global.window.mm_config.SiteName}</h2>
<h4 className='color--light'>{"Let's create your account"}</h4>
<h2 className='signup-team__subdomain'>
<FormattedMessage
id='signup_user_completed.onSite'
defaultMessage='on {siteName}'
values={{
siteName: global.window.mm_config.SiteName
}}
/>
</h2>
<h4 className='color--light'>
<FormattedMessage
id='signup_user_completed.lets'
defaultMessage="Let's create your account"
/>
</h4>
{signupMessage}
{emailSignup}
{serverError}
@@ -293,6 +399,7 @@ SignupUserComplete.defaultProps = {
teamDisplayName: ''
};
SignupUserComplete.propTypes = {
intl: intlShape.isRequired,
teamName: React.PropTypes.string,
hash: React.PropTypes.string,
teamId: React.PropTypes.string,
@@ -300,3 +407,5 @@ SignupUserComplete.propTypes = {
data: React.PropTypes.string,
teamDisplayName: React.PropTypes.string
};
export default injectIntl(SignupUserComplete);

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

@@ -5,6 +5,8 @@ import SuggestionStore from '../../stores/suggestion_store.jsx';
import UserStore from '../../stores/user_store.jsx';
import * as Utils from '../../utils/utils.jsx';
import {FormattedMessage} from 'mm-intl';
const MaxUserSuggestions = 40;
class AtMentionSuggestion extends React.Component {
@@ -16,11 +18,21 @@ class AtMentionSuggestion extends React.Component {
let icon;
if (item.username === 'all') {
username = 'all';
description = 'Notifies everyone in the team';
description = (
<FormattedMessage
id='suggestion.mention.all'
defaultMessage='Notifies everyone in the team'
/>
);
icon = <i className='mention-img fa fa-users fa-2x' />;
} else if (item.username === 'channel') {
username = 'channel';
description = 'Notifies everyone in the channel';
description = (
<FormattedMessage
id='suggestion.mention.channel'
defaultMessage='Notifies everyone in the channel'
/>
);
icon = <i className='mention-img fa fa-users fa-2x' />;
} else {
username = item.username;

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

@@ -3,7 +3,8 @@
import Constants from '../../utils/constants.jsx';
import SuggestionList from './suggestion_list.jsx';
import * as Utils from '../../utils/utils.jsx';
import {FormattedMessage} from 'mm-intl';
export default class SearchSuggestionList extends SuggestionList {
componentDidUpdate(prevProps, prevState) {
@@ -19,9 +20,19 @@ export default class SearchSuggestionList extends SuggestionList {
renderChannelDivider(type) {
let text;
if (type === Constants.OPEN_CHANNEL) {
text = 'Public ' + Utils.getChannelTerm(type) + 's';
text = (
<FormattedMessage
id='suggestion.search.public'
defaultMessage='Public Channels'
/>
);
} else {
text = 'Private ' + Utils.getChannelTerm(type) + 's';
text = (
<FormattedMessage
id='suggestion.search.private'
defaultMessage='Public Groups'
/>
);
}
return (

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

@@ -3,6 +3,8 @@
import * as Client from '../utils/client.jsx';
import {FormattedMessage} from 'mm-intl';
export default class TeamExportTab extends React.Component {
constructor(props) {
super(props);
@@ -35,7 +37,10 @@ export default class TeamExportTab extends React.Component {
messageSection = (
<p className='confirm-import alert alert-warning'>
<i className='fa fa-spinner fa-pulse' />
{' Exporting...'}
<FormattedMessage
id='team_export_tab.exporting'
defaultMessage=' Exporting...'
/>
</p>
);
break;
@@ -43,12 +48,18 @@ export default class TeamExportTab extends React.Component {
messageSection = (
<p className='confirm-import alert alert-success'>
<i className='fa fa-check' />
{' Ready for '}
<FormattedMessage
id='team_export_tab.ready'
defaultMessage=' Ready for '
/>
<a
href={this.state.link}
download={true}
>
{'download'}
<FormattedMessage
id='team_export_tab.download'
defaultMessage='download'
/>
</a>
</p>
);
@@ -57,7 +68,13 @@ export default class TeamExportTab extends React.Component {
messageSection = (
<p className='confirm-import alert alert-warning'>
<i className='fa fa-warning' />
{' Unable to export: ' + this.state.err}
<FormattedMessage
id='team_export_tab.unable'
defaultMessage=' Unable to export: {error}'
values={{
error: this.state.err
}}
/>
</p>
);
break;
@@ -68,10 +85,20 @@ export default class TeamExportTab extends React.Component {
ref='wrapper'
className='user-settings'
>
<h3 className='tab-header'>{'Export'}</h3>
<h3 className='tab-header'>
<FormattedMessage
id='team_export_tab.export'
defaultMessage='Export'
/>
</h3>
<div className='divider-dark first'/>
<ul className='section-max'>
<li className='col-xs-12 section-title'>{'Export your team'}</li>
<li className='col-xs-12 section-title'>
<FormattedMessage
id='team_export_tab.exportTeam'
defaultMessage='Export your team'
/>
</li>
<li className='col-xs-offset-3 col-xs-8'>
<ul className='setting-list'>
<li className='setting-list-item'>
@@ -80,7 +107,10 @@ export default class TeamExportTab extends React.Component {
href='#'
onClick={this.doExport}
>
{'Export'}
<FormattedMessage
id='team_export_tab.export'
defaultMessage='Export'
/>
</a>
</li>
</ul>

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

@@ -8,7 +8,56 @@ import * as Client from '../utils/client.jsx';
import * as Utils from '../utils/utils.jsx';
import TeamStore from '../stores/team_store.jsx';
export default class GeneralTab extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
dirDisabled: {
id: 'general_tab.dirDisabled',
defaultMessage: 'Team Directory has been disabled. Please ask a System Admin to enable the Team Directory in the System Console team settings.'
},
required: {
id: 'general_tab.required',
defaultMessage: 'This field is required'
},
chooseName: {
id: 'general_tab.chooseName',
defaultMessage: 'Please choose a new name for your team'
},
includeDirTitle: {
id: 'general_tab.includeDirTitle',
defaultMessage: 'Include this team in the Team Directory'
},
yes: {
id: 'general_tab.yes',
defaultMessage: 'Yes'
},
no: {
id: 'general_tab.no',
defaultMessage: 'No'
},
dirOff: {
id: 'general_tab.dirOff',
defaultMessage: 'Team directory is turned off for this system.'
},
openInviteTitle: {
id: 'general_tab.openInviteTitle',
defaultMessage: 'Allow anyone to sign-up from login page'
},
codeTitle: {
id: 'general_tab.codeTitle',
defaultMessage: 'Invite Code'
},
codeDesc: {
id: 'general_tab.codeDesc',
defaultMessage: "Click 'Edit' to regenerate Invite Code."
},
teamNameInfo: {
id: 'general_tab.teamNameInfo',
defaultMessage: 'Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.'
}
});
class GeneralTab extends React.Component {
constructor(props) {
super(props);
@@ -66,7 +115,7 @@ export default class GeneralTab extends React.Component {
handleTeamListingRadio(listing) {
if (global.window.mm_config.EnableTeamListing !== 'true' && listing) {
this.setState({clientError: 'Team Directory has been disabled. Please ask a System Admin to enable the Team Directory in the System Console team settings.'});
this.setState({clientError: this.props.intl.formatMessage(holders.dirDisabled)});
} else {
this.setState({allow_team_listing: listing});
}
@@ -118,12 +167,13 @@ export default class GeneralTab extends React.Component {
var state = {serverError: '', clientError: ''};
let valid = true;
const {formatMessage} = this.props.intl;
const name = this.state.name.trim();
if (!name) {
state.clientError = 'This field is required';
state.clientError = formatMessage(holders.required);
valid = false;
} else if (name === this.props.team.display_name) {
state.clientError = 'Please choose a new name for your team';
state.clientError = formatMessage(holders.chooseName);
valid = false;
} else {
state.clientError = '';
@@ -160,7 +210,7 @@ export default class GeneralTab extends React.Component {
if (inviteId) {
state.clientError = '';
} else {
state.clientError = 'This field is required';
state.clientError = this.props.intl.fromatMessage(holders.required);
valid = false;
}
@@ -260,6 +310,7 @@ export default class GeneralTab extends React.Component {
}
const enableTeamListing = global.window.mm_config.EnableTeamListing === 'true';
const {formatMessage} = this.props.intl;
let teamListingSection;
if (this.props.activeSection === 'team_listing') {
@@ -279,7 +330,10 @@ export default class GeneralTab extends React.Component {
defaultChecked={this.state.allow_team_listing}
onChange={this.handleTeamListingRadio.bind(this, true)}
/>
{'Yes'}
<FormattedMessage
id='general_tab.yes'
defaultMessage='Yes'
/>
</label>
<br/>
</div>
@@ -292,24 +346,39 @@ export default class GeneralTab extends React.Component {
defaultChecked={!this.state.allow_team_listing}
onChange={this.handleTeamListingRadio.bind(this, false)}
/>
{'No'}
<FormattedMessage
id='general_tab.no'
defaultMessage='No'
/>
</label>
<br/>
</div>
<div><br/>{'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>
<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/>{'Contact your system administrator to turn on the team directory on the system home page.'}</div>
<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='Include this team in the Team Directory'
title={formatMessage(holders.includeDirTitle)}
inputs={inputs}
submit={submitHandle}
server_error={serverError}
@@ -322,17 +391,17 @@ export default class GeneralTab extends React.Component {
if (enableTeamListing) {
if (this.state.allow_team_listing === true) {
describe = 'Yes';
describe = formatMessage(holders.yes);
} else {
describe = 'No';
describe = formatMessage(holders.no);
}
} else {
describe = 'Team directory is turned off for this system.';
describe = formatMessage(holders.dirOff);
}
teamListingSection = (
<SettingItemMin
title='Include this team in the Team Directory'
title={formatMessage(holders.includeDirTitle)}
describe={describe}
updateSection={this.onUpdateTeamListingSection}
/>
@@ -351,7 +420,10 @@ export default class GeneralTab extends React.Component {
defaultChecked={this.state.allow_open_invite}
onChange={this.handleOpenInviteRadio.bind(this, true)}
/>
{'Yes'}
<FormattedMessage
id='general_tab.yes'
defaultMessage='Yes'
/>
</label>
<br/>
</div>
@@ -363,17 +435,26 @@ export default class GeneralTab extends React.Component {
defaultChecked={!this.state.allow_open_invite}
onChange={this.handleOpenInviteRadio.bind(this, false)}
/>
{'No'}
<FormattedMessage
id='general_tab.no'
defaultMessage='No'
/>
</label>
<br/>
</div>
<div><br/>{'When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.'}</div>
<div>
<br/>
<FormattedMessage
id='general_tab.openInviteDesc'
defaultMessage='When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.'
/>
</div>
</div>
];
openInviteSection = (
<SettingItemMax
title='Allow anyone to sign-up from login page'
title={formatMessage(holders.openInviteTitle)}
inputs={inputs}
submit={this.handleOpenInviteSubmit}
server_error={serverError}
@@ -383,14 +464,14 @@ export default class GeneralTab extends React.Component {
} else {
let describe = '';
if (this.state.allow_open_invite === true) {
describe = 'Yes';
describe = formatMessage(holders.yes);
} else {
describe = 'No';
describe = formatMessage(holders.no);
}
openInviteSection = (
<SettingItemMin
title='Allow anyone to sign-up from login page'
title={formatMessage(holders.openInviteTitle)}
describe={describe}
updateSection={this.onUpdateOpenInviteSection}
/>
@@ -405,7 +486,12 @@ export default class GeneralTab extends React.Component {
inputs.push(
<div key='teamInviteSetting'>
<div className='row'>
<label className='col-sm-5 control-label'>{'Invite Code'}</label>
<label className='col-sm-5 control-label'>
<FormattedMessage
id='general_tab.codeTitle'
defaultMessage='Invite Code'
/>
</label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -419,18 +505,26 @@ export default class GeneralTab extends React.Component {
href='#'
onClick={this.handleGenerateInviteId}
>
{'Re-Generate'}
<FormattedMessage
id='general_tab.regenerate'
defaultMessage='Re-Generate'
/>
</a>
</div>
</div>
</div>
<div className='setting-list__hint'>{'The Invite Code is used as part of the URL in the team invitation link created by **Get Team Invite Link** in the main menu. Regenerating creates a new team invitation link and invalidates the previous link.'}</div>
<div className='setting-list__hint'>
<FormattedMessage
id='general_tab.codeLongDesc'
defaultMessage='The Invite Code is used as part of the URL in the team invitation link created by **Get Team Invite Link** in the main menu. Regenerating creates a new team invitation link and invalidates the previous link.'
/>
</div>
</div>
);
inviteSection = (
<SettingItemMax
title={`Invite Code`}
title={formatMessage(holders.codeTitle)}
inputs={inputs}
submit={this.handleInviteIdSubmit}
server_error={serverError}
@@ -441,8 +535,8 @@ export default class GeneralTab extends React.Component {
} else {
inviteSection = (
<SettingItemMin
title={`Invite Code`}
describe={`Click 'Edit' to regenerate Invite Code.`}
title={formatMessage(holders.codeTitle)}
describe={formatMessage(holders.codeDesc)}
updateSection={this.onUpdateInviteIdSection}
/>
);
@@ -453,7 +547,12 @@ export default class GeneralTab extends React.Component {
if (this.props.activeSection === 'name') {
const inputs = [];
let teamNameLabel = 'Team Name';
let teamNameLabel = (
<FormattedMessage
id='general_tab.teamName'
defaultMessage='Team Name'
/>
);
if (Utils.isMobile()) {
teamNameLabel = '';
}
@@ -478,13 +577,13 @@ export default class GeneralTab extends React.Component {
nameSection = (
<SettingItemMax
title={`Team Name`}
title={formatMessage({id: 'general_tab.teamName'})}
inputs={inputs}
submit={this.handleNameSubmit}
server_error={serverError}
client_error={clientError}
updateSection={this.onUpdateNameSection}
extraInfo='Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.'
extraInfo={formatMessage(holders.teamNameInfo)}
/>
);
} else {
@@ -492,7 +591,7 @@ export default class GeneralTab extends React.Component {
nameSection = (
<SettingItemMin
title={`Team Name`}
title={formatMessage({id: 'general_tab.teamName'})}
describe={describe}
updateSection={this.onUpdateNameSection}
/>
@@ -515,14 +614,22 @@ export default class GeneralTab extends React.Component {
ref='title'
>
<i className='modal-back'></i>
{'General Settings'}
<FormattedMessage
id='general_tab.title'
defaultMessage='General Settings'
/>
</h4>
</div>
<div
ref='wrapper'
className='user-settings'
>
<h3 className='tab-header'>{'General Settings'}</h3>
<h3 className='tab-header'>
<FormattedMessage
id='general_tab.title'
defaultMessage='General Settings'
/>
</h3>
<div className='divider-dark first'/>
{nameSection}
<div className='divider-light'/>
@@ -539,7 +646,10 @@ export default class GeneralTab extends React.Component {
}
GeneralTab.propTypes = {
intl: intlShape.isRequired,
updateSection: React.PropTypes.func.isRequired,
team: React.PropTypes.object.isRequired,
activeSection: React.PropTypes.string.isRequired
};
export default injectIntl(GeneralTab);

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

@@ -4,7 +4,16 @@
import * as utils from '../utils/utils.jsx';
import SettingUpload from './setting_upload.jsx';
export default class TeamImportTab extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const holders = defineMessages({
importSlack: {
id: 'team_import_tab.importSlack',
defaultMessage: 'Import from Slack (Beta)'
}
});
class TeamImportTab extends React.Component {
constructor(props) {
super(props);
@@ -32,16 +41,19 @@ export default class TeamImportTab extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
var uploadHelpText = (
<div>
<p>{'To import a team from Slack go to Slack > Team Settings > Import/Export Data > Export > Start Export. Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team\'\s public channels.'}</p>
<p>{'The Slack import to Mattermost is in "Beta". Slack bot posts do not yet import and Slack @mentions are not currently supported.'}</p>
<FormattedHTMLMessage
id='team_import_tab.importHelp'
defaultMessage="<p>To import a team from Slack go to Slack > Team Settings > Import/Export Data > Export > Start Export. Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team's public channels.</p><p>The Slack import to Mattermost is in 'Beta'. Slack bot posts do not yet import and Slack @mentions are not currently supported.</p>"
/>
</div>
);
var uploadSection = (
<SettingUpload
title='Import from Slack (Beta)'
title={formatMessage(holders.importSlack)}
submit={this.doImportSlack}
helpText={uploadHelpText}
fileTypesAccepted='.zip'
@@ -56,19 +68,30 @@ export default class TeamImportTab extends React.Component {
break;
case 'in-progress':
messageSection = (
<p className='confirm-import alert alert-warning'><i className='fa fa-spinner fa-pulse'></i>{' Importing...'}</p>
<p className='confirm-import alert alert-warning'><i className='fa fa-spinner fa-pulse'></i>
<FormattedMessage
id='team_import_tab.importing'
defaultMessage=' Importing...'
/>
</p>
);
break;
case 'done':
messageSection = (
<p className='confirm-import alert alert-success'>
<i className='fa fa-check' />
{' Import successful: '}
<FormattedMessage
id='team_import_tab.successful'
defaultMessage=' Import successful: '
/>
<a
href={this.state.link}
download='MattermostImportSummary.txt'
>
{'View Summary'}
<FormattedMessage
id='team_import_tab.summary'
defaultMessage='View Summary'
/>
</a>
</p>
);
@@ -77,12 +100,18 @@ export default class TeamImportTab extends React.Component {
messageSection = (
<p className='confirm-import alert alert-warning'>
<i className='fa fa-warning' />
{' Import failure: '}
<FormattedMessage
id='team_import_tab.failure'
defaultMessage=' Import failure: '
/>
<a
href={this.state.link}
download='MattermostImportSummary.txt'
>
{'View Summary'}
<FormattedMessage
id='team_import_tab.summary'
defaultMessage='View Summary'
/>
</a>
</p>
);
@@ -102,13 +131,23 @@ export default class TeamImportTab extends React.Component {
<h4
className='modal-title'
ref='title'
><i className='modal-back'></i>{'Import'}</h4>
><i className='modal-back'></i>
<FormattedMessage
id='team_import_tab.import'
defaultMessage='Import'
/>
</h4>
</div>
<div
ref='wrapper'
className='user-settings'
>
<h3 className='tab-header'>{'Import'}</h3>
<h3 className='tab-header'>
<FormattedMessage
id='team_import_tab.import'
defaultMessage='Import'
/>
</h3>
<div className='divider-dark first'/>
{uploadSection}
<div className='divider-dark'/>
@@ -118,3 +157,9 @@ export default class TeamImportTab extends React.Component {
);
}
}
TeamImportTab.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(TeamImportTab);

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

@@ -4,6 +4,8 @@
import MemberListTeam from './member_list_team.jsx';
import TeamStore from '../stores/team_store.jsx';
import {FormattedMessage} from 'mm-intl';
const Modal = ReactBootstrap.Modal;
export default class TeamMembersModal extends React.Component {
@@ -44,7 +46,13 @@ export default class TeamMembersModal extends React.Component {
onHide={this.props.onHide}
>
<Modal.Header closeButton={true}>
{team.display_name + ' Members'}
<FormattedMessage
id='team_member_modal.members'
defaultMessage='{team} Members'
values={{
team: team.display_name
}}
/>
</Modal.Header>
<Modal.Body ref='modalBody'>
<div className='team-member-list'>
@@ -57,7 +65,10 @@ export default class TeamMembersModal extends React.Component {
className='btn btn-default'
onClick={this.props.onHide}
>
{'Close'}
<FormattedMessage
id='team_member_modal.close'
defaultMessage='Close'
/>
</button>
</Modal.Footer>
</Modal>

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

@@ -4,7 +4,24 @@
import SettingsSidebar from './settings_sidebar.jsx';
import TeamSettings from './team_settings.jsx';
export default class TeamSettingsModal extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
generalTab: {
id: 'team_settings_modal.generalTab',
defaultMessage: 'General'
},
importTab: {
id: 'team_settings_modal.importTab',
defaultMessage: 'Import'
},
exportTab: {
id: 'team_settings_modal.exportTab',
defaultMessage: 'Export'
}
});
class TeamSettingsModal extends React.Component {
constructor(props) {
super(props);
@@ -36,12 +53,13 @@ export default class TeamSettingsModal extends React.Component {
this.setState({activeSection: section});
}
render() {
const {formatMessage} = this.props.intl;
const tabs = [];
tabs.push({name: 'general', uiName: 'General', icon: 'glyphicon glyphicon-cog'});
tabs.push({name: 'import', uiName: 'Import', icon: 'glyphicon glyphicon-upload'});
tabs.push({name: 'general', uiName: formatMessage(holders.generalTab), icon: 'glyphicon glyphicon-cog'});
tabs.push({name: 'import', uiName: formatMessage(holders.importTab), icon: 'glyphicon glyphicon-upload'});
// To enable export uncomment this line
//tabs.push({name: 'export', uiName: 'Export', icon: 'glyphicon glyphicon-download'});
//tabs.push({name: 'export', uiName: formatMessage(holders.exportTab), icon: 'glyphicon glyphicon-download'});
return (
<div
@@ -67,7 +85,10 @@ export default class TeamSettingsModal extends React.Component {
className='modal-title'
ref='title'
>
{'Team Settings'}
<FormattedMessage
id='team_settings_modal.title'
defaultMessage='Team Settings'
/>
</h4>
</div>
<div className='modal-body'>
@@ -96,4 +117,7 @@ export default class TeamSettingsModal extends React.Component {
}
TeamSettingsModal.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(TeamSettingsModal);

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

@@ -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>
);

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

@@ -4,7 +4,20 @@
import * as utils from '../utils/utils.jsx';
import * as client from '../utils/client.jsx';
export default class TeamSignupDisplayNamePage extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
required: {
id: 'team_signup_display_name.required',
defaultMessage: 'This field is required'
},
charLength: {
id: 'team_signup_display_name.charLength',
defaultMessage: 'Name must be 4 or more characters up to a maximum of 15'
}
});
class TeamSignupDisplayNamePage extends React.Component {
constructor(props) {
super(props);
@@ -21,12 +34,13 @@ export default class TeamSignupDisplayNamePage extends React.Component {
submitNext(e) {
e.preventDefault();
const {formatMessage} = this.props.intl;
var displayName = ReactDOM.findDOMNode(this.refs.name).value.trim();
if (!displayName) {
this.setState({nameError: 'This field is required'});
this.setState({nameError: formatMessage(holders.required)});
return;
} else if (displayName.length < 4 || displayName.length > 15) {
this.setState({nameError: 'Name must be 4 or more characters up to a maximum of 15'});
this.setState({nameError: formatMessage(holders.charLength)});
return;
}
@@ -56,7 +70,12 @@ export default class TeamSignupDisplayNamePage extends React.Component {
className='signup-team-logo'
src='/static/images/logo.png'
/>
<h2>{'Team Name'}</h2>
<h2>
<FormattedMessage
id='team_signup_display_name.teamName'
defaultMessage='Team Name'
/>
</h2>
<div className={nameDivClass}>
<div className='row'>
<div className='col-sm-9'>
@@ -76,21 +95,30 @@ export default class TeamSignupDisplayNamePage extends React.Component {
{nameError}
</div>
<div>
{'Name your team in any language. Your team name shows in menus and headings.'}
<FormattedMessage
id='team_signup_display_name.nameHelp'
defaultMessage='Name your team in any language. Your team name shows in menus and headings.'
/>
</div>
<button
type='submit'
className='btn btn-primary margin--extra'
onClick={this.submitNext}
>
Next<i className='glyphicon glyphicon-chevron-right'></i>
<FormattedMessage
id='team_signup_display_name.next'
defaultMessage='Next'
/><i className='glyphicon glyphicon-chevron-right'></i>
</button>
<div className='margin--extra'>
<a
href='#'
onClick={this.submitBack}
>
Back to previous step
<FormattedMessage
id='team_signup_display_name.back'
defaultMessage='Back to previous step'
/>
</a>
</div>
</form>
@@ -100,6 +128,9 @@ export default class TeamSignupDisplayNamePage extends React.Component {
}
TeamSignupDisplayNamePage.propTypes = {
intl: intlShape.isRequired,
state: React.PropTypes.object,
updateParent: React.PropTypes.func
};
export default injectIntl(TeamSignupDisplayNamePage);

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

@@ -3,7 +3,24 @@
import * as Utils from '../utils/utils.jsx';
export default class TeamSignupEmailItem extends React.Component {
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
const holders = defineMessages({
validEmail: {
id: 'team_signup_email.validEmail',
defaultMessage: 'Please enter a valid email address'
},
different: {
id: 'team_signup_email.different',
defaultMessage: 'Please use a different email than the one used at signup'
},
address: {
id: 'team_signup_email.address',
defaultMessage: 'Email Address'
}
});
class TeamSignupEmailItem extends React.Component {
constructor(props) {
super(props);
@@ -16,6 +33,7 @@ export default class TeamSignupEmailItem extends React.Component {
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) {
@@ -23,10 +41,10 @@ export default class TeamSignupEmailItem extends React.Component {
}
if (!Utils.isEmail(email)) {
this.setState({emailError: 'Please enter a valid email address'});
this.setState({emailError: formatMessage(holders.validEmail)});
return false;
} else if (email === teamEmail) {
this.setState({emailError: 'Please use a different email than the one used at signup'});
this.setState({emailError: formatMessage(holders.different)});
return false;
}
@@ -48,7 +66,7 @@ export default class TeamSignupEmailItem extends React.Component {
type='email'
ref='email'
className='form-control'
placeholder='Email Address'
placeholder={this.props.intl.formatMessage(holders.address)}
defaultValue={this.props.email}
maxLength='128'
spellCheck='false'
@@ -60,6 +78,9 @@ export default class TeamSignupEmailItem extends React.Component {
}
TeamSignupEmailItem.propTypes = {
intl: intlShape.isRequired,
focus: React.PropTypes.bool,
email: React.PropTypes.string
};
export default injectIntl(TeamSignupEmailItem);

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

@@ -6,7 +6,20 @@ import BrowserStore from '../stores/browser_store.jsx';
import UserStore from '../stores/user_store.jsx';
import Constants from '../utils/constants.jsx';
export default class TeamSignupPasswordPage extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const holders = defineMessages({
passwordError: {
id: 'team_signup_password.passwordError',
defaultMessage: 'Please enter at least {chars} characters'
},
creating: {
id: 'team_signup_password.creating',
defaultMessage: 'Creating team...'
}
});
class TeamSignupPasswordPage extends React.Component {
constructor(props) {
super(props);
@@ -25,7 +38,7 @@ export default class TeamSignupPasswordPage extends React.Component {
var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
this.setState({passwordError: 'Please enter at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters'});
this.setState({passwordError: this.props.intl.formatMessage(holders.passwordError, {chars: Constants.MIN_PASSWORD_LENGTH})});
return;
}
@@ -56,7 +69,7 @@ export default class TeamSignupPasswordPage extends React.Component {
window.location.href = '/' + teamSignup.team.name + '/channels/town-square';
},
(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?email=' + encodeURIComponent(teamSignup.team.email) + '&teamname=' + encodeURIComponent(teamSignup.team.name);
} else {
this.setState({serverError: err.message});
@@ -93,15 +106,35 @@ export default class TeamSignupPasswordPage extends React.Component {
className='signup-team-logo'
src='/static/images/logo.png'
/>
<h2 className='margin--less'>{'Your password'}</h2>
<h5 className='color--light'>{"Select a password that you'll use to login with your email address:"}</h5>
<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>{'Email'}</strong></h5>
<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>{'Choose your password'}</strong></h5>
<h5><strong>
<FormattedMessage
id='team_signup_password.choosePwd'
defaultMessage='Choose your password'
/>
</strong></h5>
<input
autoFocus={true}
type='password'
@@ -111,7 +144,16 @@ export default class TeamSignupPasswordPage extends React.Component {
maxLength='128'
spellCheck='false'
/>
<span className='color--light help-block'>{'Passwords must contain ' + Constants.MIN_PASSWORD_LENGTH + ' to ' + Constants.MAX_PASSWORD_LENGTH + ' characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.'}</span>
<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}
@@ -123,19 +165,33 @@ export default class TeamSignupPasswordPage extends React.Component {
type='submit'
className='btn btn-primary margin--extra'
id='finish-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Creating team...'}
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.creating)}
onClick={this.submitNext}
>
{'Finish'}
<FormattedMessage
id='team_signup_password.finish'
defaultMessage='Finish'
/>
</button>
</div>
<p>By proceeding to create your account and use {global.window.mm_config.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 {global.window.mm_config.SiteName}.</p>
<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}
>
{'Back to previous step'}
<FormattedMessage
id='team_signup_password.back'
defaultMessage='Back to previous step'
/>
</a>
</div>
</form>
@@ -149,7 +205,10 @@ TeamSignupPasswordPage.defaultProps = {
hash: ''
};
TeamSignupPasswordPage.propTypes = {
intl: intlShape.isRequired,
state: React.PropTypes.object,
hash: React.PropTypes.string,
updateParent: React.PropTypes.func
};
export default injectIntl(TeamSignupPasswordPage);

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

@@ -4,6 +4,8 @@
import EmailItem from './team_signup_email_item.jsx';
import * as Client from '../utils/client.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
export default class TeamSignupSendInvitesPage extends React.Component {
constructor(props) {
super(props);
@@ -117,7 +119,10 @@ export default class TeamSignupSendInvitesPage extends React.Component {
href='#'
onClick={this.submitAddInvite}
>
Add Invitation
<FormattedMessage
id='team_signup_send_invites.addInvitation'
defaultMessage='Add Invitation'
/>
</a>
</div>
</div>
@@ -125,22 +130,32 @@ export default class TeamSignupSendInvitesPage extends React.Component {
bottomContent = (
<p className='color--light'>
{'if you prefer, you can invite team members later'}
<br />
{' and '}
<FormattedHTMLMessage
id='team_signup_send_invites.prefer'
defaultMessage='if you prefer, you can invite team members later<br /> and '
/>
<a
href='#'
onClick={this.submitSkip}
>
{'skip this step '}
<FormattedMessage
id='team_signup_send_invites.skip'
defaultMessage='skip this step '
/>
</a>
{'for now.'}
<FormattedMessage
id='team_signup_send_invites.forNow'
defaultMessage='for now.'
/>
</p>
);
} else {
content = (
<div className='form-group color--light'>
{'Email is currently disabled for your team, and emails cannot be sent. Contact your system administrator to enable email and email invitations.'}
<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>
);
}
@@ -152,7 +167,12 @@ export default class TeamSignupSendInvitesPage extends React.Component {
className='signup-team-logo'
src='/static/images/logo.png'
/>
<h2>{'Invite Team Members'}</h2>
<h2>
<FormattedMessage
id='team_signup_send_invites.title'
defaultMessage='Invite Team Members'
/>
</h2>
{content}
<div className='form-group'>
<button
@@ -160,7 +180,10 @@ export default class TeamSignupSendInvitesPage extends React.Component {
className='btn-primary btn'
onClick={this.submitNext}
>
Next<i className='glyphicon glyphicon-chevron-right' />
<FormattedMessage
id='team_signup_send_invites.next'
defaultMessage='Next'
/><i className='glyphicon glyphicon-chevron-right' />
</button>
</div>
</form>
@@ -170,7 +193,10 @@ export default class TeamSignupSendInvitesPage extends React.Component {
href='#'
onClick={this.submitBack}
>
Back to previous step
<FormattedMessage
id='team_signup_send_invites.back'
defaultMessage='Back to previous step'
/>
</a>
</div>
</div>

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

@@ -5,7 +5,32 @@ import * as Utils from '../utils/utils.jsx';
import * as Client from '../utils/client.jsx';
import Constants from '../utils/constants.jsx';
export default class TeamSignupUrlPage extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const holders = defineMessages({
required: {
id: 'team_signup_url.required',
defaultMessage: 'This field is required'
},
regex: {
id: 'team_signup_url.regex',
defaultMessage: "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash."
},
charLength: {
id: 'team_signup_url.charLength',
defaultMessage: 'Name must be 4 or more characters up to a maximum of 15'
},
taken: {
id: 'team_signup_url.taken',
defaultMessage: 'URL is taken or contains a reserved word'
},
unavailable: {
id: 'team_signup_url.unavailable',
defaultMessage: 'This URL is unavailable. Please try another.'
}
});
class TeamSignupUrlPage extends React.Component {
constructor(props) {
super(props);
@@ -23,9 +48,10 @@ export default class TeamSignupUrlPage extends React.Component {
submitNext(e) {
e.preventDefault();
const {formatMessage} = this.props.intl;
const name = ReactDOM.findDOMNode(this.refs.name).value.trim();
if (!name) {
this.setState({nameError: 'This field is required'});
this.setState({nameError: formatMessage(holders.required)});
return;
}
@@ -33,17 +59,17 @@ export default class TeamSignupUrlPage extends React.Component {
const urlRegex = /^[a-z]+([a-z\-0-9]+|(__)?)[a-z0-9]+$/g;
if (cleanedName !== name || !urlRegex.test(name)) {
this.setState({nameError: "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash."});
this.setState({nameError: formatMessage(holders.regex)});
return;
} else if (cleanedName.length < 4 || cleanedName.length > 15) {
this.setState({nameError: 'Name must be 4 or more characters up to a maximum of 15'});
this.setState({nameError: formatMessage(holders.charLength)});
return;
}
if (global.window.mm_config.RestrictTeamNames === 'true') {
for (let index = 0; index < Constants.RESERVED_TEAM_NAMES.length; index++) {
if (cleanedName.indexOf(Constants.RESERVED_TEAM_NAMES[index]) === 0) {
this.setState({nameError: 'URL is taken or contains a reserved word'});
this.setState({nameError: formatMessage(holders.taken)});
return;
}
}
@@ -52,7 +78,7 @@ export default class TeamSignupUrlPage extends React.Component {
Client.findTeamByName(name,
(data) => {
if (data) {
this.setState({nameError: 'This URL is unavailable. Please try another.'});
this.setState({nameError: formatMessage(holders.unavailable)});
} else {
if (global.window.mm_config.SendEmailNotifications === 'true') {
this.props.state.wizard = 'send_invites';
@@ -96,7 +122,12 @@ export default class TeamSignupUrlPage extends React.Component {
className='signup-team-logo'
src='/static/images/logo.png'
/>
<h2>{`Team URL`}</h2>
<h2>
<FormattedMessage
id='team_signup_url.teamUrl'
defaultMessage='Team URL'
/>
</h2>
<div className={nameDivClass}>
<div className='row'>
<div className='col-sm-11'>
@@ -124,25 +155,39 @@ export default class TeamSignupUrlPage extends React.Component {
</div>
{nameError}
</div>
<p>{`Choose the web address of your new team:`}</p>
<p>
<FormattedMessage
id='team_signup_url.webAddress'
defaultMessage='Choose the web address of your new team:'
/>
</p>
<ul className='color--light'>
<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>
<FormattedHTMLMessage
id='team_signup_url.hint'
defaultMessage="<li>Short and memorable is best</li>
<li>Use lowercase letters, numbers and dashes</li>
<li>Must start with a letter and can't end in a dash</li>"
/>
</ul>
<button
type='submit'
className='btn btn-primary margin--extra'
onClick={this.submitNext}
>
Next<i className='glyphicon glyphicon-chevron-right'></i>
<FormattedMessage
id='team_signup_url.next'
defaultMessage='Next'
/><i className='glyphicon glyphicon-chevron-right'></i>
</button>
<div className='margin--extra'>
<a
href='#'
onClick={this.submitBack}
>
Back to previous step
<FormattedMessage
id='team_signup_url.back'
defaultMessage='Back to previous step'
/>
</a>
</div>
</form>
@@ -152,6 +197,9 @@ export default class TeamSignupUrlPage extends React.Component {
}
TeamSignupUrlPage.propTypes = {
intl: intlShape.isRequired,
state: React.PropTypes.object,
updateParent: React.PropTypes.func
};
export default injectIntl(TeamSignupUrlPage);

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

@@ -5,7 +5,20 @@ import * as Utils from '../utils/utils.jsx';
import * as Client from '../utils/client.jsx';
import Constants from '../utils/constants.jsx';
export default class TeamSignupUsernamePage extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
reserved: {
id: 'team_signup_username.reserved',
defaultMessage: 'This username is reserved, please choose a new one.'
},
invalid: {
id: 'team_signup_username.invalid',
defaultMessage: 'Username must begin with a letter, and contain between {min} to {max} characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''
}
});
class TeamSignupUsernamePage extends React.Component {
constructor(props) {
super(props);
@@ -27,14 +40,15 @@ export default class TeamSignupUsernamePage extends React.Component {
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.setState({nameError: 'This username is reserved, please choose a new one.'});
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: 'Username must begin with a letter, and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + ' characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''});
this.setState({nameError: formatMessage(holders.invalid, {min: Constants.MIN_USERNAME_LENGTH, max: Constants.MAX_USERNAME_LENGTH})});
return;
}
@@ -46,7 +60,18 @@ export default class TeamSignupUsernamePage extends React.Component {
Client.track('signup', 'signup_team_06_username');
var nameError = null;
var nameHelpText = <span className='color--light help-block'>{'Usernames must begin with a letter and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + " characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'"}</span>;
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>;
@@ -61,13 +86,28 @@ export default class TeamSignupUsernamePage extends React.Component {
className='signup-team-logo'
src='/static/images/logo.png'
/>
<h2 className='margin--less'>{'Your username'}</h2>
<h5 className='color--light'>{'Select a memorable username that makes it easy for teammates to identify you:'}</h5>
<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>{'Choose your username'}</strong></h5>
<h5><strong>
<FormattedMessage
id='team_signup_username.chooseUsername'
defaultMessage='Choose your username'
/>
</strong></h5>
<input
autoFocus={true}
type='text'
@@ -89,7 +129,10 @@ export default class TeamSignupUsernamePage extends React.Component {
className='btn btn-primary margin--extra'
onClick={this.submitNext}
>
{'Next'}
<FormattedMessage
id='team_signup_username.next'
defaultMessage='Next'
/>
<i className='glyphicon glyphicon-chevron-right'></i>
</button>
<div className='margin--extra'>
@@ -97,7 +140,10 @@ export default class TeamSignupUsernamePage extends React.Component {
href='#'
onClick={this.submitBack}
>
{'Back to previous step'}
<FormattedMessage
id='team_signup_username.back'
defaultMessage='Back to previous step'
/>
</a>
</div>
</form>
@@ -110,6 +156,9 @@ TeamSignupUsernamePage.defaultProps = {
state: null
};
TeamSignupUsernamePage.propTypes = {
intl: intlShape.isRequired,
state: React.PropTypes.object,
updateParent: React.PropTypes.func
};
export default injectIntl(TeamSignupUsernamePage);

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

@@ -5,7 +5,24 @@ import * as Utils from '../utils/utils.jsx';
import * as Client from '../utils/client.jsx';
import BrowserStore from '../stores/browser_store.jsx';
export default class TeamSignupWelcomePage extends React.Component {
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const holders = defineMessages({
storageError: {
id: 'team_signup_welcome.storageError',
defaultMessage: 'This service requires local storage to be enabled. Please enable it or exit private browsing.'
},
validEmailError: {
id: 'team_signup_welcome.validEmailError',
defaultMessage: 'Please enter a valid email address'
},
address: {
id: 'team_signup_welcome.address',
defaultMessage: 'Email Address'
}
});
class TeamSignupWelcomePage extends React.Component {
constructor(props) {
super(props);
@@ -20,7 +37,7 @@ export default class TeamSignupWelcomePage extends React.Component {
}
submitNext(e) {
if (!BrowserStore.isLocalStorageSupported()) {
this.setState({storageError: 'This service requires local storage to be enabled. Please enable it or exit private browsing.'});
this.setState({storageError: this.props.intl.formatMessage(holders.storageError)});
return;
}
e.preventDefault();
@@ -34,15 +51,16 @@ export default class TeamSignupWelcomePage extends React.Component {
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 = 'Please enter a valid email address';
state.emailError = formatMessage(holders.validEmailError);
this.setState(state);
return;
} else if (!BrowserStore.isLocalStorageSupported()) {
state.emailError = 'This service requires local storage to be enabled. Please enable it or exit private browsing.';
state.emailError = formatMessage(holders.storageError);
this.setState(state);
return;
}
@@ -62,7 +80,7 @@ export default class TeamSignupWelcomePage extends React.Component {
let errorMsg = err.message;
if (err.detailed_error.indexOf('Invalid RCPT TO address provided') >= 0) {
errorMsg = 'Please enter a valid email address';
errorMsg = formatMessage(holders.validEmailError);
}
this.setState({emailError: '', serverError: errorMsg});
@@ -114,18 +132,35 @@ export default class TeamSignupWelcomePage extends React.Component {
className='signup-team-logo'
src='/static/images/logo.png'
/>
<h3 className='sub-heading'>Welcome to:</h3>
<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'>Let's set up your new team</p>
<p className='margin--less'>
<FormattedMessage
id='team_signup_welcome.lets'
defaultMessage="Let's set up your new team"
/>
</p>
<div>
Please confirm your email address:<br />
<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'>
Your account will administer the new team site. <br />
You can add other administrators later.
<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
@@ -134,7 +169,10 @@ export default class TeamSignupWelcomePage extends React.Component {
onClick={this.submitNext}
>
<i className='glyphicon glyphicon-ok'></i>
Yes, this address is correct
<FormattedMessage
id='team_signup_welcome.yes'
defaultMessage='Yes, this address is correct'
/>
</button>
{storageError}
</div>
@@ -147,7 +185,7 @@ export default class TeamSignupWelcomePage extends React.Component {
type='email'
ref='email'
className='form-control'
placeholder='Email Address'
placeholder={this.props.intl.formatMessage(holders.address)}
maxLength='128'
spellCheck='false'
/>
@@ -161,7 +199,10 @@ export default class TeamSignupWelcomePage extends React.Component {
type='button'
onClick={this.handleDiffSubmit}
>
Use this instead
<FormattedMessage
id='team_signup_welcome.instead'
defaultMessage='Use this instead'
/>
</button>
</div>
<a
@@ -169,7 +210,10 @@ export default class TeamSignupWelcomePage extends React.Component {
onClick={this.handleDiffEmail}
className={differentEmailLinkClass}
>
Use a different email
<FormattedMessage
id='team_signup_welcome.different'
defaultMessage='Use a different email'
/>
</a>
</div>
);
@@ -180,6 +224,9 @@ TeamSignupWelcomePage.defaultProps = {
state: {}
};
TeamSignupWelcomePage.propTypes = {
intl: intlShape.isRequired,
updateParent: React.PropTypes.func.isRequired,
state: React.PropTypes.object
};
export default injectIntl(TeamSignupWelcomePage);

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

@@ -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);

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

@@ -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);

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

@@ -11,6 +11,9 @@ import ErrorStore from '../stores/error_store.jsx';
import * as TextFormatting from '../utils/text_formatting.jsx';
import * as Utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
import {FormattedMessage} from 'mm-intl';
const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
export default class Textbox extends React.Component {
@@ -143,7 +146,17 @@ export default class Textbox extends React.Component {
onClick={this.showPreview}
className='textbox-preview-link'
>
{this.state.preview ? 'Edit message' : 'Preview'}
{this.state.preview ? (
<FormattedMessage
id='textbox.edit'
defaultMessage='Edit message'
/>
) : (
<FormattedMessage
id='textbox.preview'
defaultMessage='Preview'
/>
)}
</a>
);
}
@@ -184,7 +197,10 @@ export default class Textbox extends React.Component {
onClick={this.showHelp}
className='textbox-help-link'
>
{'Help'}
<FormattedMessage
id='textbox.help'
defaultMessage='Help'
/>
</a>
</div>
);

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

@@ -9,6 +9,9 @@ import * as Utils from '../../utils/utils.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import Constants from '../../utils/constants.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const Preferences = Constants.Preferences;
const NUM_SCREENS = 3;
@@ -61,10 +64,13 @@ export default class TutorialIntroScreens extends React.Component {
return (
<div>
<h3>{'Welcome to:'}</h3>
<h1>{'Mattermost'}</h1>
<p>{'Your team communication all in one place, instantly searchable and available anywhere.'}</p>
<p>{'Keep your team connected to help them achieve what matters most.'}</p>
<FormattedHTMLMessage
id='tutorial_intro.screenOne'
defaultMessage='<h3>Welcome to:</h3>
<h1>Mattermost</h1>
<p>Your team communication all in one place, instantly searchable and available anywhere</p>
<p>Keep your team connected to help them achieve what matters most.</p>'
/>
{circles}
</div>
);
@@ -74,9 +80,12 @@ export default class TutorialIntroScreens extends React.Component {
return (
<div>
<h3>{'How Mattermost works:'}</h3>
<p>{'Communication happens in public discussion channels, private groups and direct messages.'}</p>
<p>{'Everything is archived and searchable from any web-enabled desktop, laptop or phone.'}</p>
<FormattedHTMLMessage
id='tutorial_intro.screenTwo'
defaultMessage='<h3>How Mattermost works:</h3>
<p>Communication happens in public discussion channels, private groups and direct messages.</p>
<p>Everything is archived and searchable from any web-enabled desktop, laptop or phone.</p>'
/>
{circles}
</div>
);
@@ -92,7 +101,10 @@ export default class TutorialIntroScreens extends React.Component {
data-toggle='modal'
data-target='#invite_member'
>
{'Invite teammates'}
<FormattedMessage
id='tutorial_intro.invite'
defaultMessage='Invite teammates'
/>
</a>
);
} else {
@@ -105,7 +117,10 @@ export default class TutorialIntroScreens extends React.Component {
data-title='Team Invite'
data-value={Utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + team.id}
>
{'Invite teammates'}
<FormattedMessage
id='tutorial_intro.teamInvite'
defaultMessage='Team Invite'
/>
</a>
);
}
@@ -116,7 +131,10 @@ export default class TutorialIntroScreens extends React.Component {
if (global.window.mm_config.SupportEmail) {
supportInfo = (
<p>
{'Need anything, just email us at '}
<FormattedMessage
id='tutorial_intro.support'
defaultMessage='Need anything, just email us at '
/>
<a
href={'mailto:' + global.window.mm_config.SupportEmail}
target='_blank'
@@ -130,13 +148,24 @@ export default class TutorialIntroScreens extends React.Component {
return (
<div>
<h3>{'Youre all set'}</h3>
<h3>
<FormattedMessage
id='tutorial_intro.allSet'
defaultMessage='Youre all set'
/>
</h3>
<p>
{inviteModalLink}
{' when youre ready.'}
<FormattedMessage
id='tutorial_intro.whenReady'
defaultMessage=' when youre ready.'
/>
</p>
{supportInfo}
{'Click “Next” to enter Town Square. This is the first channel teammates see when they sign up. Use it for posting updates everyone needs to know.'}
<FormattedMessage
id='tutorial_intro.end'
defaultMessage='Click “Next” to enter Town Square. This is the first channel teammates see when they sign up. Use it for posting updates everyone needs to know.'
/>
{circles}
</div>
);
@@ -186,14 +215,20 @@ export default class TutorialIntroScreens extends React.Component {
tabIndex='1'
onClick={this.handleNext}
>
{'Next'}
<FormattedMessage
id='tutorial_intro.next'
defaultMessage='Next'
/>
</button>
<a
className='tutorial-skip'
href='#'
onClick={this.skipTutorial}
>
{'Skip tutorial'}
<FormattedMessage
id='tutorial_intro.skip'
defaultMessage='Skip tutorial'
/>
</a>
</div>
</div>

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

@@ -6,6 +6,9 @@ import PreferenceStore from '../../stores/preference_store.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import Constants from '../../utils/constants.jsx';
import {FormattedMessage} from 'mm-intl';
const Preferences = Constants.Preferences;
const Overlay = ReactBootstrap.Overlay;
@@ -46,7 +49,17 @@ export default class TutorialTip extends React.Component {
AsyncClient.savePreferences([preference]);
}
render() {
const buttonText = this.state.currentScreen === this.props.screens.length - 1 ? 'Okay' : 'Next';
const buttonText = this.state.currentScreen === this.props.screens.length - 1 ? (
<FormattedMessage
id='tutorial_tip.ok'
defaultMessage='Okay'
/>
) : (
<FormattedMessage
id='tutorial_tip.next'
defaultMessage='Next'
/>
);
const dots = [];
if (this.props.screens.length > 1) {
@@ -71,7 +84,7 @@ export default class TutorialTip extends React.Component {
}
var tipColor = '';
if (this.props.overlayClass === 'tip-overlay--header') {
if (this.props.overlayClass === 'tip-overlay--header' || this.props.overlayClass === 'tip-overlay--sidebar') {
tipColor = 'White';
}
@@ -111,12 +124,18 @@ export default class TutorialTip extends React.Component {
{buttonText}
</button>
<div className='tip-opt'>
{'Seen this before? '}
<FormattedMessage
id='tutorial_tip.seen'
defaultMessage='Seen this before? '
/>
<a
href='#'
onClick={this.skipTutorial}
>
{'Opt out of these tips.'}
<FormattedMessage
id='tutorial_tip.out'
defaultMessage='Opt out of these tips.'
/>
</a>
</div>
</div>

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

@@ -31,5 +31,5 @@ UnreadChannelIndicator.defaultProps = {
UnreadChannelIndicator.propTypes = {
show: React.PropTypes.bool,
extraClass: React.PropTypes.string,
text: React.PropTypes.string
text: React.PropTypes.object
};

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

@@ -3,6 +3,9 @@
import * as Utils from '../utils/utils.jsx';
import UserStore from '../stores/user_store.jsx';
import {FormattedMessage} from 'mm-intl';
var Popover = ReactBootstrap.Popover;
var OverlayTrigger = ReactBootstrap.OverlayTrigger;
@@ -87,7 +90,10 @@ export default class UserProfile extends React.Component {
className='text-nowrap'
key='user-popover-no-email'
>
{'Email not shared'}
<FormattedMessage
id='user_profile.notShared'
defaultMessage='Email not shared'
/>
</div>
);
} else {

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

@@ -6,7 +6,96 @@ import Constants from '../../utils/constants.jsx';
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
const Popover = ReactBootstrap.Popover;
export default class CustomThemeChooser extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const messages = defineMessages({
sidebarBg: {
id: 'user.settings.custom_theme.sidebarBg',
defaultMessage: 'Sidebar BG'
},
sidebarText: {
id: 'user.settings.custom_theme.sidebarText',
defaultMessage: 'Sidebar Text'
},
sidebarHeaderBg: {
id: 'user.settings.custom_theme.sidebarHeaderBg',
defaultMessage: 'Sidebar Header BG'
},
sidebarHeaderTextColor: {
id: 'user.settings.custom_theme.sidebarHeaderTextColor',
defaultMessage: 'Sidebar Header Text'
},
sidebarUnreadText: {
id: 'user.settings.custom_theme.sidebarUnreadText',
defaultMessage: 'Sidebar Unread Text'
},
sidebarTextHoverBg: {
id: 'user.settings.custom_theme.sidebarTextHoverBg',
defaultMessage: 'Sidebar Text Hover BG'
},
sidebarTextActiveBorder: {
id: 'user.settings.custom_theme.sidebarTextActiveBorder',
defaultMessage: 'Sidebar Text Active Border'
},
sidebarTextActiveColor: {
id: 'user.settings.custom_theme.sidebarTextActiveColor',
defaultMessage: 'Sidebar Text Active Color'
},
onlineIndicator: {
id: 'user.settings.custom_theme.onlineIndicator',
defaultMessage: 'Online Indicator'
},
awayIndicator: {
id: 'user.settings.custom_theme.awayIndicator',
defaultMessage: 'Away Indicator'
},
mentionBj: {
id: 'user.settings.custom_theme.mentionBj',
defaultMessage: 'Mention Jewel BG'
},
mentionColor: {
id: 'user.settings.custom_theme.mentionColor',
defaultMessage: 'Mention Jewel Text'
},
centerChannelBg: {
id: 'user.settings.custom_theme.centerChannelBg',
defaultMessage: 'Center Channel BG'
},
centerChannelColor: {
id: 'user.settings.custom_theme.centerChannelColor',
defaultMessage: 'Center Channel Text'
},
newMessageSeparator: {
id: 'user.settings.custom_theme.newMessageSeparator',
defaultMessage: 'New Message Separator'
},
linkColor: {
id: 'user.settings.custom_theme.linkColor',
defaultMessage: 'Link Color'
},
buttonBg: {
id: 'user.settings.custom_theme.buttonBg',
defaultMessage: 'Button BG'
},
buttonColor: {
id: 'user.settings.custom_theme.buttonColor',
defaultMessage: 'Button Text'
},
mentionHighlightBg: {
id: 'user.settings.custom_theme.mentionHighlightBg',
defaultMessage: 'Mention Highlight BG'
},
mentionHighlightLink: {
id: 'user.settings.custom_theme.mentionHighlightLink',
defaultMessage: 'Mention Highlight Link'
},
codeTheme: {
id: 'user.settings.custom_theme.codeTheme',
defaultMessage: 'Code Theme'
}
});
class CustomThemeChooser extends React.Component {
constructor(props) {
super(props);
@@ -65,6 +154,7 @@ export default class CustomThemeChooser extends React.Component {
this.props.updateTheme(theme);
}
render() {
const {formatMessage} = this.props.intl;
const theme = this.props.theme;
const elements = [];
@@ -102,7 +192,7 @@ export default class CustomThemeChooser extends React.Component {
className='col-sm-4 form-group'
key={'custom-theme-key' + index}
>
<label className='custom-label'>{element.uiName}</label>
<label className='custom-label'>{formatMessage(messages[element.id])}</label>
<div
className='input-group theme-group group--code dropdown'
id={element.id}
@@ -135,7 +225,7 @@ export default class CustomThemeChooser extends React.Component {
className='col-sm-4 form-group'
key={'custom-theme-key' + index}
>
<label className='custom-label'>{element.uiName}</label>
<label className='custom-label'>{formatMessage(messages[element.id])}</label>
<div
className='input-group color-picker'
id={element.id}
@@ -160,7 +250,10 @@ export default class CustomThemeChooser extends React.Component {
const pasteBox = (
<div className='col-sm-12'>
<label className='custom-label'>
{'Copy and paste to share theme colors:'}
<FormattedMessage
id='user.settings.custom_theme.copyPaste'
defaultMessage='Copy and paste to share theme colors:'
/>
</label>
<input
type='text'
@@ -185,6 +278,9 @@ export default class CustomThemeChooser extends React.Component {
}
CustomThemeChooser.propTypes = {
intl: intlShape.isRequired,
theme: React.PropTypes.object.isRequired,
updateTheme: React.PropTypes.func.isRequired
};
export default injectIntl(CustomThemeChooser);

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