Feature/translate license management (#24341)

Translate license management
This commit is contained in:
Nox911 2018-11-05 16:22:45 +03:00 committed by pavel06081991
parent 40d78865ed
commit 666a1d752c
18 changed files with 1646 additions and 289 deletions

View file

@ -15,6 +15,7 @@
"tagCloud": "src/core_plugins/tagcloud",
"xpack.grokDebugger": "x-pack/plugins/grokdebugger",
"xpack.idxMgmt": "x-pack/plugins/index_management",
"xpack.licenseMgmt": "x-pack/plugins/license_management",
"xpack.rollupJobs": "x-pack/plugins/rollup",
"xpack.searchProfiler": "x-pack/plugins/searchprofiler",
"xpack.security": "x-pack/plugins/security",

View file

@ -1,7 +1,90 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TelemetryOptIn should display when telemetry not opted in 1`] = `
<TelemetryOptIn>
<TelemetryOptIn
intl={
Object {
"defaultFormats": Object {
"date": Object {
"full": Object {
"day": "numeric",
"month": "long",
"weekday": "long",
"year": "numeric",
},
"long": Object {
"day": "numeric",
"month": "long",
"year": "numeric",
},
"medium": Object {
"day": "numeric",
"month": "short",
"year": "numeric",
},
"short": Object {
"day": "numeric",
"month": "numeric",
"year": "2-digit",
},
},
"number": Object {
"currency": Object {
"style": "currency",
},
"percent": Object {
"style": "percent",
},
},
"time": Object {
"full": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"long": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"medium": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
},
"short": Object {
"hour": "numeric",
"minute": "numeric",
},
},
},
"defaultLocale": "en",
"formatDate": [Function],
"formatHTMLMessage": [Function],
"formatMessage": [Function],
"formatNumber": [Function],
"formatPlural": [Function],
"formatRelative": [Function],
"formatTime": [Function],
"formats": Object {},
"formatters": Object {
"getDateTimeFormat": [Function],
"getMessageFormat": [Function],
"getNumberFormat": [Function],
"getPluralFormat": [Function],
"getRelativeFormat": [Function],
},
"locale": "en",
"messages": Object {},
"now": [Function],
"onError": [Function],
"textComponent": Symbol(react.fragment),
"timeZone": null,
}
}
>
<EuiText
grow={true}
>
@ -9,7 +92,13 @@ exports[`TelemetryOptIn should display when telemetry not opted in 1`] = `
className="euiText"
>
<p>
Gold and platinum customers: help support give you better service.
<FormattedMessage
defaultMessage="Gold and platinum customers: help support give you better service."
id="xpack.licenseMgmt.telemetryOptIn.customersHelpSupportDescription"
values={Object {}}
>
Gold and platinum customers: help support give you better service.
</FormattedMessage>
</p>
</div>
</EuiText>
@ -28,54 +117,76 @@ exports[`TelemetryOptIn should display when telemetry not opted in 1`] = `
indeterminate={false}
label={
<span>
Send basic feature usage statistics to Elastic periodically.
<EuiPopover
anchorPosition="downCenter"
button={
<EuiLink
color="primary"
onClick={[Function]}
type="button"
>
Read more
</EuiLink>
<FormattedMessage
defaultMessage="Send basic feature usage statistics to Elastic periodically. {popover}"
id="xpack.licenseMgmt.telemetryOptIn.sendBasicFeatureStatisticsLabel"
values={
Object {
"popover": <EuiPopover
anchorPosition="downCenter"
button={
<EuiLink
color="primary"
onClick={[Function]}
type="button"
>
<FormattedMessage
defaultMessage="Read more"
id="xpack.licenseMgmt.telemetryOptIn.readMoreLinkText"
values={Object {}}
/>
</EuiLink>
}
className="eui-AlignBaseline"
closePopover={[Function]}
hasArrow={true}
id="readMorePopover"
isOpen={false}
ownFocus={true}
panelPaddingSize="m"
>
<EuiText
className="licManagement__narrowText"
grow={true}
>
<p>
<FormattedMessage
defaultMessage="This feature periodically sends basic feature usage statistics. This information will not be shared outside of Elastic. See an {exampleLink} or read our {telemetryPrivacyStatementLink}. You can disable this feature any time."
id="xpack.licenseMgmt.telemetryOptIn.featureUsageWarningMessage"
values={
Object {
"exampleLink": <EuiLink
color="primary"
onClick={[Function]}
type="button"
>
<FormattedMessage
defaultMessage="example"
id="xpack.licenseMgmt.telemetryOptIn.exampleLinkText"
values={Object {}}
/>
</EuiLink>,
"telemetryPrivacyStatementLink": <EuiLink
color="primary"
href="https://www.elastic.co/legal/telemetry-privacy-statement"
target="_blank"
type="button"
>
<FormattedMessage
defaultMessage="telemetry privacy statement"
id="xpack.licenseMgmt.telemetryOptIn.telemetryPrivacyStatementLinkText"
values={Object {}}
/>
</EuiLink>,
}
}
/>
</p>
</EuiText>
</EuiPopover>,
}
}
className="eui-AlignBaseline"
closePopover={[Function]}
hasArrow={true}
id="readMorePopover"
isOpen={false}
ownFocus={true}
panelPaddingSize="m"
>
<EuiText
className="licManagement__narrowText"
grow={true}
>
<p>
This feature periodically sends basic feature usage statistics. This information will not be shared outside of Elastic. See an
<EuiLink
color="primary"
onClick={[Function]}
type="button"
>
example
</EuiLink>
or read our
<EuiLink
color="primary"
href="https://www.elastic.co/legal/telemetry-privacy-statement"
target="_blank"
type="button"
>
telemetry privacy statement
</EuiLink>
. You can disable this feature any time.
</p>
</EuiText>
</EuiPopover>
/>
</span>
}
onChange={[Function]}
@ -99,55 +210,136 @@ exports[`TelemetryOptIn should display when telemetry not opted in 1`] = `
htmlFor="isOptingInToTelemetry"
>
<span>
Send basic feature usage statistics to Elastic periodically.
<EuiPopover
anchorPosition="downCenter"
button={
<EuiLink
color="primary"
onClick={[Function]}
type="button"
>
Read more
</EuiLink>
}
className="eui-AlignBaseline"
closePopover={[Function]}
hasArrow={true}
id="readMorePopover"
isOpen={false}
ownFocus={true}
panelPaddingSize="m"
>
<EuiOutsideClickDetector
onOutsideClick={[Function]}
>
<div
className="euiPopover euiPopover--anchorDownCenter eui-AlignBaseline"
id="readMorePopover"
onClick={[Function]}
onKeyDown={[Function]}
>
<div
className="euiPopover__anchor"
>
<EuiLink
color="primary"
onClick={[Function]}
type="button"
>
<button
className="euiLink euiLink--primary"
<FormattedMessage
defaultMessage="Send basic feature usage statistics to Elastic periodically. {popover}"
id="xpack.licenseMgmt.telemetryOptIn.sendBasicFeatureStatisticsLabel"
values={
Object {
"popover": <EuiPopover
anchorPosition="downCenter"
button={
<EuiLink
color="primary"
onClick={[Function]}
type="button"
>
Read more
</button>
</EuiLink>
<FormattedMessage
defaultMessage="Read more"
id="xpack.licenseMgmt.telemetryOptIn.readMoreLinkText"
values={Object {}}
/>
</EuiLink>
}
className="eui-AlignBaseline"
closePopover={[Function]}
hasArrow={true}
id="readMorePopover"
isOpen={false}
ownFocus={true}
panelPaddingSize="m"
>
<EuiText
className="licManagement__narrowText"
grow={true}
>
<p>
<FormattedMessage
defaultMessage="This feature periodically sends basic feature usage statistics. This information will not be shared outside of Elastic. See an {exampleLink} or read our {telemetryPrivacyStatementLink}. You can disable this feature any time."
id="xpack.licenseMgmt.telemetryOptIn.featureUsageWarningMessage"
values={
Object {
"exampleLink": <EuiLink
color="primary"
onClick={[Function]}
type="button"
>
<FormattedMessage
defaultMessage="example"
id="xpack.licenseMgmt.telemetryOptIn.exampleLinkText"
values={Object {}}
/>
</EuiLink>,
"telemetryPrivacyStatementLink": <EuiLink
color="primary"
href="https://www.elastic.co/legal/telemetry-privacy-statement"
target="_blank"
type="button"
>
<FormattedMessage
defaultMessage="telemetry privacy statement"
id="xpack.licenseMgmt.telemetryOptIn.telemetryPrivacyStatementLinkText"
values={Object {}}
/>
</EuiLink>,
}
}
/>
</p>
</EuiText>
</EuiPopover>,
}
}
>
Send basic feature usage statistics to Elastic periodically.
<EuiPopover
anchorPosition="downCenter"
button={
<EuiLink
color="primary"
onClick={[Function]}
type="button"
>
<FormattedMessage
defaultMessage="Read more"
id="xpack.licenseMgmt.telemetryOptIn.readMoreLinkText"
values={Object {}}
/>
</EuiLink>
}
className="eui-AlignBaseline"
closePopover={[Function]}
hasArrow={true}
id="readMorePopover"
isOpen={false}
ownFocus={true}
panelPaddingSize="m"
>
<EuiOutsideClickDetector
onOutsideClick={[Function]}
>
<div
className="euiPopover euiPopover--anchorDownCenter eui-AlignBaseline"
id="readMorePopover"
onClick={[Function]}
onKeyDown={[Function]}
>
<div
className="euiPopover__anchor"
>
<EuiLink
color="primary"
onClick={[Function]}
type="button"
>
<button
className="euiLink euiLink--primary"
onClick={[Function]}
type="button"
>
<FormattedMessage
defaultMessage="Read more"
id="xpack.licenseMgmt.telemetryOptIn.readMoreLinkText"
values={Object {}}
>
Read more
</FormattedMessage>
</button>
</EuiLink>
</div>
</div>
</div>
</EuiOutsideClickDetector>
</EuiPopover>
</EuiOutsideClickDetector>
</EuiPopover>
</FormattedMessage>
</span>
</label>
</div>
@ -155,4 +347,89 @@ exports[`TelemetryOptIn should display when telemetry not opted in 1`] = `
</TelemetryOptIn>
`;
exports[`TelemetryOptIn should not display when telemetry is opted in 1`] = `<TelemetryOptIn />`;
exports[`TelemetryOptIn should not display when telemetry is opted in 1`] = `
<TelemetryOptIn
intl={
Object {
"defaultFormats": Object {
"date": Object {
"full": Object {
"day": "numeric",
"month": "long",
"weekday": "long",
"year": "numeric",
},
"long": Object {
"day": "numeric",
"month": "long",
"year": "numeric",
},
"medium": Object {
"day": "numeric",
"month": "short",
"year": "numeric",
},
"short": Object {
"day": "numeric",
"month": "numeric",
"year": "2-digit",
},
},
"number": Object {
"currency": Object {
"style": "currency",
},
"percent": Object {
"style": "percent",
},
},
"time": Object {
"full": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"long": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"medium": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
},
"short": Object {
"hour": "numeric",
"minute": "numeric",
},
},
},
"defaultLocale": "en",
"formatDate": [Function],
"formatHTMLMessage": [Function],
"formatMessage": [Function],
"formatNumber": [Function],
"formatPlural": [Function],
"formatRelative": [Function],
"formatTime": [Function],
"formats": Object {},
"formatters": Object {
"getDateTimeFormat": [Function],
"getMessageFormat": [Function],
"getNumberFormat": [Function],
"getPluralFormat": [Function],
"getRelativeFormat": [Function],
},
"locale": "en",
"messages": Object {},
"now": [Function],
"onError": [Function],
"textComponent": Symbol(react.fragment),
"timeZone": null,
}
}
/>
`;

View file

@ -5,18 +5,19 @@
*/
import React from 'react';
import { TelemetryOptIn } from '../public/components/telemetry_opt_in';
import { mount } from 'enzyme';
import { mountWithIntl } from '../../../test_utils/enzyme_helpers';
describe('TelemetryOptIn', () => {
test('should display when telemetry not opted in', () => {
const telemetry = require('../public/lib/telemetry');
telemetry.showTelemetryOptIn = () => { return true; };
const rendered = mount(<TelemetryOptIn />);
const rendered = mountWithIntl(<TelemetryOptIn />);
expect(rendered).toMatchSnapshot();
});
test('should not display when telemetry is opted in', () => {
const telemetry = require('../public/lib/telemetry');
telemetry.showTelemetryOptIn = () => { return false; };
const rendered = mount(<TelemetryOptIn />);
const rendered = mountWithIntl(<TelemetryOptIn />);
expect(rendered).toMatchSnapshot();
});
});

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { mount } from 'enzyme';
import { mountWithIntl } from '../../../test_utils/enzyme_helpers';
import React from 'react';
import { Provider } from 'react-redux';
import { uploadLicense } from '../public/store/actions/upload_license';
@ -54,13 +54,13 @@ describe('UploadLicense', () => {
services.kbnUrl.change.mockReset();
});
it('should display an error when submitting invalid JSON', async () => {
const rendered = mount(component);
const rendered = mountWithIntl(component);
store.dispatch(uploadLicense('INVALID', 'trial'));
rendered.update();
expect(rendered).toMatchSnapshot();
});
it('should display an error when ES says license is invalid', async () => {
const rendered = mount(component);
const rendered = mountWithIntl(component);
const invalidLicense = JSON.stringify({ license: { type: 'basic' } });
server.respond(UPLOAD_LICENSE_INVALID);
await uploadLicense(invalidLicense)(store.dispatch, null, services);
@ -68,7 +68,7 @@ describe('UploadLicense', () => {
expect(rendered).toMatchSnapshot();
});
it('should display an error when ES says license is expired', async () => {
const rendered = mount(component);
const rendered = mountWithIntl(component);
const invalidLicense = JSON.stringify({ license: { type: 'basic' } });
server.respond(UPLOAD_LICENSE_EXPIRED);
await uploadLicense(invalidLicense)(store.dispatch, null, services);
@ -85,7 +85,7 @@ describe('UploadLicense', () => {
null,
services
);
const rendered = mount(component);
const rendered = mountWithIntl(component);
expect(rendered).toMatchSnapshot();
});
it('should refresh xpack info and navigate to BASE_PATH when ES accepts new license', async () => {
@ -96,7 +96,7 @@ describe('UploadLicense', () => {
expect(services.kbnUrl.change).toHaveBeenCalledWith(BASE_PATH);
});
it('should display error when ES returns error', async () => {
const rendered = mount(component);
const rendered = mountWithIntl(component);
const license = JSON.stringify({ license: { type: 'basic' } });
server.respond(UPLOAD_LICENSE_TLS_NOT_ENABLED);
await uploadLicense(license)(store.dispatch, null, services);

View file

@ -7,7 +7,7 @@
import { Provider } from 'react-redux';
import { licenseManagementStore } from '../../public/store/store';
import React from 'react';
import { mount } from 'enzyme';
import { mountWithIntl } from '../../../../test_utils/enzyme_helpers';
const highExpirationMillis = new Date('October 13, 2099 00:00:00Z').getTime();
@ -23,7 +23,7 @@ export const createMockLicense = (
};
export const getComponent = (initialState, Component) => {
const store = licenseManagementStore(initialState);
return mount(
return mountWithIntl(
<Provider store={store}>
<Component />
</Provider>

View file

@ -13,6 +13,7 @@ import {
EuiPopover
} from '@elastic/eui';
import { showTelemetryOptIn, getTelemetryFetcher, PRIVACY_STATEMENT_URL, OptInExampleFlyout } from '../../lib/telemetry';
import { FormattedMessage } from '@kbn/i18n/react';
export class TelemetryOptIn extends React.Component {
constructor() {
@ -60,7 +61,12 @@ export class TelemetryOptIn extends React.Component {
toCurrentCustomers = (
<Fragment>
<EuiText>
<p>Gold and platinum customers: help support give you better service.</p>
<p>
<FormattedMessage
id="xpack.licenseMgmt.telemetryOptIn.customersHelpSupportDescription"
defaultMessage="Gold and platinum customers: help support give you better service."
/>
</p>
</EuiText>
<EuiSpacer size="s"/>
</Fragment>
@ -69,7 +75,10 @@ export class TelemetryOptIn extends React.Component {
const readMoreButton = (
<EuiLink onClick={this.onClickReadMore}>
Read more
<FormattedMessage
id="xpack.licenseMgmt.telemetryOptIn.readMoreLinkText"
defaultMessage="Read more"
/>
</EuiLink>
);
@ -84,19 +93,33 @@ export class TelemetryOptIn extends React.Component {
>
<EuiText className="licManagement__narrowText" >
<p>
This feature periodically sends basic feature usage statistics.
This information will not be shared outside of Elastic.
See an <EuiLink onClick={this.onClickExample}>example</EuiLink>
{' '}
or read our
{' '}
<EuiLink
href={PRIVACY_STATEMENT_URL}
target="_blank"
>
telemetry privacy statement
</EuiLink>.
You can disable this feature any time.
<FormattedMessage
id="xpack.licenseMgmt.telemetryOptIn.featureUsageWarningMessage"
defaultMessage="This feature periodically sends basic feature usage statistics.
This information will not be shared outside of Elastic.
See an {exampleLink} or read our {telemetryPrivacyStatementLink}.
You can disable this feature any time."
values={{
exampleLink: (
<EuiLink onClick={this.onClickExample}>
<FormattedMessage
id="xpack.licenseMgmt.telemetryOptIn.exampleLinkText"
defaultMessage="example"
/>
</EuiLink>),
telemetryPrivacyStatementLink: (
<EuiLink
href={PRIVACY_STATEMENT_URL}
target="_blank"
>
<FormattedMessage
id="xpack.licenseMgmt.telemetryOptIn.telemetryPrivacyStatementLinkText"
defaultMessage="telemetry privacy statement"
/>
</EuiLink>
)
}}
/>
</p>
</EuiText>
</EuiPopover>
@ -107,7 +130,17 @@ export class TelemetryOptIn extends React.Component {
{example}
{toCurrentCustomers}
<EuiCheckbox
label={<span>Send basic feature usage statistics to Elastic periodically. {popover}</span>}
label={
<span>
<FormattedMessage
id="xpack.licenseMgmt.telemetryOptIn.sendBasicFeatureStatisticsLabel"
defaultMessage="Send basic feature usage statistics to Elastic periodically. {popover}"
values={{
popover
}}
/>
</span>
}
id="isOptingInToTelemetry"
checked={isOptingInToTelemetry}
onChange={this.onChangeOptIn}

View file

@ -6,10 +6,13 @@
import { management } from 'ui/management';
import { BASE_PATH } from '../common/constants';
import { i18n } from '@kbn/i18n';
management.getSection('elasticsearch').register('license_management', {
visible: true,
display: 'License Management',
display: i18n.translate('xpack.licenseMgmt.managementSectionDisplayName', {
defaultMessage: 'License Management',
}),
order: 4,
url: `#${BASE_PATH}home`
});

View file

@ -9,6 +9,7 @@ import { render, unmountComponentAtNode } from 'react-dom';
import { Provider } from 'react-redux';
import { HashRouter } from 'react-router-dom';
import { setTelemetryOptInService, setTelemetryEnabled, setHttpClient, TelemetryOptInProvider } from './lib/telemetry';
import { I18nProvider } from '@kbn/i18n/react';
import App from './app';
@ -22,11 +23,13 @@ import { licenseManagementStore } from './store';
const renderReact = (elem, store) => {
render(
<Provider store={store}>
<HashRouter>
<App />
</HashRouter>
</Provider>,
<I18nProvider>
<Provider store={store}>
<HashRouter>
<App />
</HashRouter>
</Provider>
</I18nProvider>,
elem
);
};

View file

@ -8,18 +8,28 @@ import React from 'react';
import { BASE_PATH } from '../../../../common/constants';
import { EuiCard, EuiButton } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
export const AddLicense = ({ uploadPath = `#${BASE_PATH}upload_license` }) => {
return (
<EuiCard
title="Update your license"
description="If you already have a new license, upload it now."
title={(<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.addLicense.updateLicenseTitle"
defaultMessage="Update your license"
/>)}
description={(<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.addLicense.useAvailableLicenseDescription"
defaultMessage="If you already have a new license, upload it now."
/>)}
footer={
<EuiButton
data-test-subj="updateLicenseButton"
href={uploadPath}
>
Update license
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.addLicense.updateLicenseButtonLabel"
defaultMessage="Update license"
/>
</EuiButton>
}
/>

View file

@ -7,11 +7,19 @@
import { LicenseStatus as PresentationComponent } from './license_status';
import { connect } from 'react-redux';
import { getLicense, getExpirationDateFormatted, isExpired } from '../../../store/reducers/licenseManagement';
import { i18n } from '@kbn/i18n';
const mapStateToProps = (state) => {
const { isActive, type } = getLicense(state);
return {
status: isActive ? 'Active' : 'Inactive',
status: isActive ?
i18n.translate('xpack.licenseMgmt.licenseDashboard.licenseStatus.activeLicenseStatusText', {
defaultMessage: 'Active'
})
:
i18n.translate('xpack.licenseMgmt.licenseDashboard.licenseStatus.inactiveLicenseStatusText', {
defaultMessage: 'Inactive'
}),
type,
isExpired: isExpired(state),
expiryDate: getExpirationDateFormatted(state)

View file

@ -14,6 +14,7 @@ import {
EuiTitle,
EuiSpacer,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
export class LicenseStatus extends React.PureComponent {
render() {
@ -26,20 +27,58 @@ export class LicenseStatus extends React.PureComponent {
icon = <EuiIcon color="danger" type="alert" />;
message = (
<Fragment>
Your license expired on <strong>{expiryDate}</strong>
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.licenseStatus.expiredLicenseStatusDescription"
defaultMessage="Your license expired on {expiryDate}"
values={{
expiryDate: (
<strong>{expiryDate}</strong>
)
}}
/>
</Fragment>
);
title = `Your ${typeTitleCase} license has expired`;
title = (
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.licenseStatus.expiredLicenseStatusTitle"
defaultMessage="Your {typeTitleCase} license has expired"
values={{
typeTitleCase
}}
/>
);
} else {
icon = <EuiIcon color="success" type="checkInCircleFilled" />;
message = expiryDate ? (
<Fragment>
Your license will expire on <strong>{expiryDate}</strong>
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.licenseStatus.activeLicenseStatusDescription"
defaultMessage="Your license will expire on {expiryDate}"
values={{
expiryDate: (
<strong>{expiryDate}</strong>
)
}}
/>
</Fragment>
) : (
<Fragment>Your license will never expire.</Fragment>
<Fragment>
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.licenseStatus.permanentActiveLicenseStatusDescription"
defaultMessage="Your license will never expire."
/>
</Fragment>
);
title = (
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.licenseStatus.activeLicenseStatusTitle"
defaultMessage="Your {typeTitleCase} license is {status}"
values={{
typeTitleCase,
status: status.toLowerCase()
}}
/>
);
title = `Your ${typeTitleCase} license is ${status.toLowerCase()}`;
}
return (
<div>

View file

@ -7,6 +7,7 @@
import React from 'react';
import { EuiFlexItem, EuiCard, EuiLink, EuiButton } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
export const RequestTrialExtension = ({ shouldShowRequestTrialExtension }) => {
if (!shouldShowRequestTrialExtension) {
@ -14,20 +15,33 @@ export const RequestTrialExtension = ({ shouldShowRequestTrialExtension }) => {
}
const description = (
<span>
If youd like to continuing using security, machine learning, and our
other awesome{' '}
<EuiLink
href="https://www.elastic.co/subscriptions/xpack"
target="_blank"
>
Platinum features
</EuiLink>, request an extension now.
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.requestTrialExtension.howToContinueUsingPluginsDescription"
defaultMessage="If youd like to continuing using security, machine learning, and our
other awesome {platinumLicenseFeaturesLinkText}, request an extension now."
values={{
platinumLicenseFeaturesLinkText: (
<EuiLink
href="https://www.elastic.co/subscriptions/xpack"
target="_blank"
>
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.requestTrialExtension.platinumLicenseFeaturesLinkText"
defaultMessage="Platinum features"
/>
</EuiLink>
)
}}
/>
</span>
);
return (
<EuiFlexItem>
<EuiCard
title="Extend your trial"
title={(<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.requestTrialExtension.extendYourTrialTitle"
defaultMessage="Extend your trial"
/>)}
description={description}
footer={
<EuiButton
@ -35,7 +49,10 @@ export const RequestTrialExtension = ({ shouldShowRequestTrialExtension }) => {
target="_blank"
href="https://www.elastic.co/trialextension"
>
Extend trial
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.requestTrialExtension.extendTrialButtonLabel"
defaultMessage="Extend trial"
/>
</EuiButton>
}
/>

View file

@ -15,6 +15,7 @@ import {
EuiConfirmModal,
EuiText
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
export class RevertToBasic extends React.PureComponent {
cancel = () => {
@ -35,11 +36,20 @@ export class RevertToBasic extends React.PureComponent {
return (
<EuiOverlayMask>
<EuiConfirmModal
title="Confirm Revert to Basic License"
title={(<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.revertToBasic.confirmModalTitle"
defaultMessage="Confirm Revert to Basic License"
/>)}
onCancel={cancelStartBasicLicense}
onConfirm={() => startBasicLicense(licenseType, true)}
cancelButtonText="Cancel"
confirmButtonText="Confirm"
cancelButtonText={(<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.revertToBasic.confirmModal.cancelButtonLabel"
defaultMessage="Cancel"
/>)}
confirmButtonText={(<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.revertToBasic.confirmModal.confirmButtonLabel"
defaultMessage="Confirm"
/>)}
>
<div>
<EuiText>{firstLine}</EuiText>
@ -64,14 +74,24 @@ export class RevertToBasic extends React.PureComponent {
}
const description = (
<span>
Youll revert to our free features and lose access to security, machine
learning and other{' '}
<EuiLink
href="https://www.elastic.co/subscriptions/xpack"
target="_blank"
>
Platinum features
</EuiLink>.
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.revertToBasic.revertToFreeFeaturesDescription"
defaultMessage="Youll revert to our free features and lose access to security,
machine learning and other {platinumLicenseFeaturesLinkText}."
values={{
platinumLicenseFeaturesLinkText: (
<EuiLink
href="https://www.elastic.co/subscriptions/xpack"
target="_blank"
>
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.revertToBasic.platinumLicenseFeaturesLinkText"
defaultMessage="Platinum features"
/>
</EuiLink>
)
}}
/>
</span>
);
@ -79,11 +99,17 @@ export class RevertToBasic extends React.PureComponent {
<EuiFlexItem>
{this.acknowledgeModal()}
<EuiCard
title="Revert to Basic license"
title={(<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.revertToBasic.acknowledgeModalTitle"
defaultMessage="Revert to Basic license"
/>)}
description={description}
footer={
<EuiButton data-test-subj="revertToBasicButton" onClick={() => startBasicLicense(licenseType)}>
Revert to Basic
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.revertToBasic.acknowledgeModal.revertToBasicButtonLabel"
defaultMessage="Revert to Basic"
/>
</EuiButton>
}
/>

View file

@ -25,6 +25,7 @@ import {
import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links';
import { TelemetryOptIn } from '../../../components/telemetry_opt_in';
import { optInToTelemetry } from '../../../lib/telemetry';
import { FormattedMessage } from '@kbn/i18n/react';
const esBase = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}`;
const securityDocumentationLink = `${esBase}/security-settings.html`;
@ -61,7 +62,10 @@ export class StartTrial extends React.PureComponent {
>
<EuiModalHeader>
<EuiModalHeaderTitle data-test-subj="confirmModalTitleText">
Start your free 30-day trial
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.confirmModalTitle"
defaultMessage="Start your free 30-day trial"
/>
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
@ -69,40 +73,97 @@ export class StartTrial extends React.PureComponent {
<div>
<EuiText>
<p>
This trial is for the full set of{' '}
<EuiLink
href="https://www.elastic.co/subscriptions/xpack"
target="_blank"
>
Platinum features
</EuiLink> of the Elastic Stack. You&apos;ll get immediate access to:
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.confirmModalDescription"
defaultMessage="This trial is for the full set of {platinumLicenseFeaturesLinkText} of the Elastic Stack.
You&apos;ll get immediate access to:"
values={{
platinumLicenseFeaturesLinkText: (
<EuiLink
href="https://www.elastic.co/subscriptions/xpack"
target="_blank"
>
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.confirmModalDescription.platinumLicenseFeaturesLinkText"
defaultMessage="Platinum features"
/>
</EuiLink>
)
}}
/>
</p>
<ul>
<li>Machine learning</li>
<li>Alerting</li>
<li>Graph capabilities</li>
<li>JDBC connectivity for SQL</li>
<li>
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.confirmModalDescription.mashingLearningFeatureTitle"
defaultMessage="Machine learning"
/>
</li>
<li>
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.confirmModalDescription.alertingFeatureTitle"
defaultMessage="Alerting"
/>
</li>
<li>
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.confirmModalDescription.graphCapabilitiesFeatureTitle"
defaultMessage="Graph capabilities"
/>
</li>
<li>
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.confirmModalDescription.dateBaseConnectivityFeatureTitle"
defaultMessage="{jdbcStandart} connectivity for {sqlDateBase}"
values={{
jdbcStandart: 'JDBC',
sqlDateBase: 'SQL'
}}
/>
</li>
</ul>
<p>
Security features, such as authentication (native, AD/LDAP, SAML,
PKI), role-based access control, and auditing, require
configuration. See the{' '}
<EuiLink
href={securityDocumentationLink}
target="_blank"
>
documentation
</EuiLink>{' '}
for instructions.
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.confirmModalDescription.securityFeaturesConfigurationDescription"
defaultMessage="Security features, such as authentication ({authenticationTypeList}),
role-based access control, and auditing, require configuration.
See the {securityDocumentationLinkText} for instructions."
values={{
authenticationTypeList: 'native, AD/LDAP, SAML, PKI',
securityDocumentationLinkText: (
<EuiLink
href={securityDocumentationLink}
target="_blank"
>
<FormattedMessage
// eslint-disable-next-line max-len
id="xpack.licenseMgmt.licenseDashboard.startTrial.confirmModalDescription.securityDocumentationLinkText"
defaultMessage="documentation"
/>
</EuiLink>
)
}}
/>
</p>
<p>
By starting this trial, you agree that it is subject to these{' '}
<EuiLink
href="https://elastic.co/legal/trial_license"
target="_blank"
>
terms and conditions
</EuiLink>.
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.confirmModalDescription.termsAndConditionsDescription"
defaultMessage="By starting this trial, you agree that it is subject to these {termsAndConditionsLinkText}."
values={{
termsAndConditionsLinkText: (
<EuiLink
href="https://elastic.co/legal/trial_license"
target="_blank"
>
<FormattedMessage
// eslint-disable-next-line max-len
id="xpack.licenseMgmt.licenseDashboard.startTrial.confirmModalDescription.termsAndConditionsLinkText"
defaultMessage="terms and conditions"
/>
</EuiLink>
)
}}
/>
</p>
</EuiText>
</div>
@ -122,7 +183,10 @@ export class StartTrial extends React.PureComponent {
onClick={this.cancel}
buttonRef={this.cancelRef}
>
Cancel
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.confirmModal.cancelButtonLabel"
defaultMessage="Cancel"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false} className="licManagement__ieFlex">
@ -133,7 +197,10 @@ export class StartTrial extends React.PureComponent {
buttonRef={this.confirmRef}
color="primary"
>
Start my trial
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.confirmModal.startTrialButtonLabel"
defaultMessage="Start my trial"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
@ -152,27 +219,42 @@ export class StartTrial extends React.PureComponent {
}
const description = (
<span>
Experience what security, machine learning, and all our other{' '}
<EuiLink
href="https://www.elastic.co/subscriptions/xpack"
target="_blank"
>
Platinum features
</EuiLink>{' '}
have to offer.
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.platinumFeaturesExperienceDescription"
defaultMessage="Experience what security, machine learning, and all our other {platinumLicenseFeaturesLinkText} have to offer."
values={{
platinumLicenseFeaturesLinkText: (
<EuiLink
href="https://www.elastic.co/subscriptions/xpack"
target="_blank"
>
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.platinumLicenseFeaturesLinkText"
defaultMessage="Platinum features"
/>
</EuiLink>
)
}}
/>
</span>
);
const footer = (
<EuiButton data-test-subj="startTrialButton" onClick={() => this.setState({ showConfirmation: true })}>
Start trial
<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrial.startTrialButtonLabel"
defaultMessage="Start trial"
/>
</EuiButton>
);
return (
<EuiFlexItem>
{this.acknowledgeModal()}
<EuiCard
title="Start a 30-day trial"
title={(<FormattedMessage
id="xpack.licenseMgmt.licenseDashboard.startTrialTitle"
defaultMessage="Start a 30-day trial"
/>)}
description={description}
footer={footer}
/>

View file

@ -23,6 +23,8 @@ import {
} from '@elastic/eui';
import { TelemetryOptIn } from '../../components/telemetry_opt_in';
import { optInToTelemetry } from '../../lib/telemetry';
import { FormattedMessage } from '@kbn/i18n/react';
export class UploadLicense extends React.PureComponent {
componentDidMount() {
this.props.addUploadErrorMessage('');
@ -51,11 +53,20 @@ export class UploadLicense extends React.PureComponent {
return (
<EuiOverlayMask>
<EuiConfirmModal
title="Confirm License Upload"
title={<FormattedMessage
id="xpack.licenseMgmt.uploadLicense.confirmModalTitle"
defaultMessage="Confirm License Upload"
/>}
onCancel={this.cancel}
onConfirm={() => this.send(true)}
cancelButtonText="Cancel"
confirmButtonText="Confirm"
cancelButtonText={<FormattedMessage
id="xpack.licenseMgmt.uploadLicense.confirmModal.cancelButtonLabel"
defaultMessage="Cancel"
/>}
confirmButtonText={<FormattedMessage
id="xpack.licenseMgmt.uploadLicense.confirmModal.confirmButtonLabel"
defaultMessage="Confirm"
/>}
>
<div>
<EuiText>{firstLine}</EuiText>
@ -89,7 +100,12 @@ export class UploadLicense extends React.PureComponent {
if (this.file) {
this.send();
} else {
this.props.addUploadErrorMessage('You must select a license file.');
this.props.addUploadErrorMessage(
<FormattedMessage
id="xpack.licenseMgmt.uploadLicense.licenseFileNotSelectedErrorMessage"
defaultMessage="You must select a license file."
/>
);
}
};
render() {
@ -97,7 +113,12 @@ export class UploadLicense extends React.PureComponent {
return (
<Fragment>
<EuiTitle className="eui-textCenter" size="l">
<h1>Upload your license</h1>
<h1>
<FormattedMessage
id="xpack.licenseMgmt.uploadLicense.uploadLicenseTitle"
defaultMessage="Upload your license"
/>
</h1>
</EuiTitle>
<EuiSpacer />
@ -107,10 +128,22 @@ export class UploadLicense extends React.PureComponent {
{this.acknowledgeModal()}
<EuiText>
<p>Your license key is a JSON file with a signature attached.</p>
<p>
Uploading a license will replace your current{' '}
<strong>{currentLicenseType.toUpperCase()}</strong> license.
<FormattedMessage
id="xpack.licenseMgmt.uploadLicense.licenseKeyTypeDescription"
defaultMessage="Your license key is a JSON file with a signature attached."
/>
</p>
<p>
<FormattedMessage
id="xpack.licenseMgmt.uploadLicense.replacingCurrentLicenseWarningMessage"
defaultMessage="Uploading a license will replace your current {currentLicenseType} license."
values={{
currentLicenseType: (
<strong>{currentLicenseType.toUpperCase()}</strong>
)
}}
/>
</p>
</EuiText>
<EuiSpacer />
@ -118,7 +151,10 @@ export class UploadLicense extends React.PureComponent {
<EuiText>
<EuiFilePicker
id="licenseFile"
initialPromptText="Select or drag your license file"
initialPromptText={<FormattedMessage
id="xpack.licenseMgmt.uploadLicense.selectLicenseFileDescription"
defaultMessage="Select or drag your license file"
/>}
onChange={this.handleFile}
/>
</EuiText>
@ -131,7 +167,12 @@ export class UploadLicense extends React.PureComponent {
<EuiSpacer size="m" />
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiButtonEmpty href={`#${BASE_PATH}home`}>Cancel</EuiButtonEmpty>
<EuiButtonEmpty href={`#${BASE_PATH}home`}>
<FormattedMessage
id="xpack.licenseMgmt.uploadLicense.cancelButtonLabel"
defaultMessage="Cancel"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
@ -140,7 +181,15 @@ export class UploadLicense extends React.PureComponent {
isLoading={applying}
onClick={this.submit}
>
{applying ? 'Uploading...' : 'Upload'}
{applying ?
<FormattedMessage
id="xpack.licenseMgmt.uploadLicense.uploadingButtonLabel"
defaultMessage="Uploading…"
/>
: <FormattedMessage
id="xpack.licenseMgmt.uploadLicense.uploadButtonLabel"
defaultMessage="Upload"
/>}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -7,6 +7,7 @@
import { createAction } from 'redux-actions';
import { startBasic } from '../../lib/es';
import { toastNotifications } from 'ui/notify';
import { i18n } from '@kbn/i18n';
export const startBasicLicenseStatus = createAction(
'LICENSE_MANAGEMENT_START_BASIC_LICENSE_STATUS'
@ -36,9 +37,13 @@ export const startBasicLicense = (currentLicenseType, ack) => async (
const messages = Object.values(acknowledge).slice(1).map((item) => {
return item[0];
});
const first = `Some functionality will be lost if you replace your
${currentLicenseType.toUpperCase()} license with a
BASIC license. Review the list of features below.`;
const first = i18n.translate('xpack.licenseMgmt.replacingCurrentLicenseWithBasicLicenseWarningMessage', {
//eslint-disable-next-line
defaultMessage: 'Some functionality will be lost if you replace your {currentLicenseType} license with a BASIC license. Review the list of features below.',
values: {
currentLicenseType: currentLicenseType.toUpperCase(),
}
});
dispatch(startBasicLicenseStatus({ acknowledge: true, messages: [ first, ...messages] }));
}

View file

@ -9,10 +9,13 @@ import { addLicense } from '../actions/add_license';
import { BASE_PATH } from '../../../common/constants/base_path';
import { putLicense } from '../../lib/es';
import { addUploadErrorMessage } from "./add_error_message";
import { i18n } from '@kbn/i18n';
export const uploadLicenseStatus = createAction('LICENSE_MANAGEMENT_UPLOAD_LICENSE_STATUS');
const genericUploadError = 'Error encountered uploading license:';
const genericUploadError = i18n.translate('xpack.licenseMgmt.uploadLicense.genericUploadErrorMessage', {
defaultMessage: 'Error encountered uploading license:'
});
const dispatchFromResponse = async (response, dispatch, currentLicenseType, newLicenseType, { xPackInfo, kbnUrl }) => {
const { error, acknowledged, license_status: licenseStatus, acknowledge } = response;
@ -22,10 +25,14 @@ const dispatchFromResponse = async (response, dispatch, currentLicenseType, newL
} else if (acknowledged) {
if (licenseStatus === 'invalid') {
dispatch(uploadLicenseStatus({}));
dispatch(addUploadErrorMessage('The supplied license is not valid for this product.'));
dispatch(addUploadErrorMessage(i18n.translate('xpack.licenseMgmt.uploadLicense.invalidLicenseErrorMessage', {
defaultMessage: 'The supplied license is not valid for this product.'
})));
} else if (licenseStatus === 'expired') {
dispatch(uploadLicenseStatus({}));
dispatch(addUploadErrorMessage('The supplied license has expired.'));
dispatch(addUploadErrorMessage(i18n.translate('xpack.licenseMgmt.uploadLicense.expiredLicenseErrorMessage', {
defaultMessage: 'The supplied license has expired.'
})));
} else {
await xPackInfo.refresh();
dispatch(addLicense(xPackInfo.get('license')));
@ -38,9 +45,14 @@ const dispatchFromResponse = async (response, dispatch, currentLicenseType, newL
// first message relates to command line interface, so remove it
const messages = Object.values(acknowledge).slice(1);
// messages can be in nested arrays
const first = `Some functionality will be lost if you replace your
${currentLicenseType.toUpperCase()} license with a
${newLicenseType.toUpperCase()} license. Review the list of features below.`;
const first = i18n.translate('xpack.licenseMgmt.uploadLicense.problemWithUploadedLicenseDescription', {
// eslint-disable-next-line max-len
defaultMessage: 'Some functionality will be lost if you replace your {currentLicenseType} license with a {newLicenseType} license. Review the list of features below.',
values: {
currentLicenseType: currentLicenseType.toUpperCase(),
newLicenseType: newLicenseType.toUpperCase()
}
});
dispatch(uploadLicenseStatus({ acknowledge: true, messages: [ first, ...messages] }));
}
@ -53,13 +65,23 @@ export const uploadLicense = (licenseString, currentLicenseType, acknowledge) =>
({ type: newLicenseType } = JSON.parse(licenseString).license);
} catch (err) {
dispatch(uploadLicenseStatus({}));
return dispatch(addUploadErrorMessage(`${genericUploadError} Check your license file.`));
return dispatch(addUploadErrorMessage(
i18n.translate('xpack.licenseMgmt.uploadLicense.checkLicenseFileErrorMessage', {
defaultMessage: '{genericUploadError} Check your license file.',
values: {
genericUploadError
}
})
));
}
try {
const response = await putLicense(licenseString, acknowledge);
await dispatchFromResponse(response, dispatch, currentLicenseType, newLicenseType, services);
} catch (err) {
const message = (err.responseJSON && err.responseJSON.error.reason) ? err.responseJSON.error.reason : 'Unknown error.';
const message = (err.responseJSON && err.responseJSON.error.reason) ? err.responseJSON.error.reason : i18n.translate(
'xpack.licenseMgmt.uploadLicense.unknownErrorErrorMessage', {
defaultMessage: 'Unknown error.'
});
dispatch(uploadLicenseStatus({}));
dispatch(addUploadErrorMessage(`${genericUploadError} ${message}`));
}