From bdde08fff517a28f6f639e724b810a3679fdc452 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gr=C3=A9goire=20Aubert?= Date: Mon, 28 May 2018 13:10:23 +0200 Subject: [PATCH] SONAR-10696 Remove ability to upgrade/downgrade an edition from Marketplace (#269) * SONAR-10699 Remove upgrade/downgrade buttons * SONAR-10697 Drop edition.json support in the marketplace * SONAR-10717 Drop 'sonar.editions.jsonUrl' property * SONAR-10702 Edition's "Learn more" links redirect to the form page with arguments * SONAR-10698 Get Edition data from the doc * SONAR-10700 Remove LicenseEditionForm and LicenseEditionSet from Marketplace --- .../ComputeEngineContainerImplTest.java | 2 +- .../src/tooltips/editions/community.md | 3 + .../src/tooltips/editions/datacenter.md | 3 + .../src/tooltips/editions/developer.md | 3 + .../src/tooltips/editions/enterprise.md | 3 + .../org/sonar/server/ui/ws/GlobalAction.java | 2 - .../sonar/server/ui/ws/GlobalActionTest.java | 2 - .../sonar-web/src/main/js/api/marketplace.ts | 38 +-- .../main/js/app/components/AdminContainer.tsx | 47 +-- .../components/nav/component/ComponentNav.tsx | 2 +- .../nav/settings/SettingsEditionsNotif.tsx | 179 ------------ .../SettingsEditionsNotifContainer.tsx | 49 ---- .../components/nav/settings/SettingsNav.tsx | 41 +-- .../__tests__/SettingsEditionsNotif-test.tsx | 130 --------- .../settings/__tests__/SettingsNav-test.tsx | 25 ++ .../SettingsEditionsNotif-test.tsx.snap | 62 ---- .../__snapshots__/SettingsNav-test.tsx.snap | 21 +- .../main/js/app/styles/components/alerts.css | 6 - .../src/main/js/app/utils/exposeLibraries.ts | 2 - .../src/main/js/apps/marketplace/App.tsx | 27 +- .../main/js/apps/marketplace/AppContainer.tsx | 34 +-- .../main/js/apps/marketplace/EditionBoxes.tsx | 157 +++------- .../__tests__/EditionBoxes-test.tsx | 108 +------ .../__snapshots__/EditionBoxes-test.tsx.snap | 76 ++--- .../marketplace/components/EditionBox.tsx | 60 ++-- .../components/EditionBoxBadge.tsx | 62 ---- .../components/LicenseEditionForm.tsx | 114 -------- .../components/LicenseEditionSet.tsx | 269 ------------------ .../components/UninstallEditionForm.tsx | 94 ------ .../components/__tests__/EditionBox-test.tsx | 36 +-- .../__tests__/EditionBoxBadge-test.tsx | 81 ------ .../__tests__/LicenseEditionForm-test.tsx | 98 ------- .../__tests__/LicenseEditionSet-test.tsx | 126 -------- .../__tests__/UninstallEditionForm-test.tsx | 72 ----- .../__snapshots__/EditionBox-test.tsx.snap | 106 ++----- .../EditionBoxBadge-test.tsx.snap | 29 -- .../LicenseEditionForm-test.tsx.snap | 81 ------ .../LicenseEditionSet-test.tsx.snap | 105 ------- .../UninstallEditionForm-test.tsx.snap | 39 --- .../src/main/js/apps/marketplace/style.css | 5 + .../src/main/js/apps/marketplace/utils.ts | 67 ++++- .../src/main/js/store/marketplace/actions.ts | 69 +---- .../src/main/js/store/marketplace/reducer.ts | 34 +-- .../src/main/js/store/marketplace/utils.ts | 49 ---- .../src/main/js/store/rootReducer.js | 6 +- .../core/config/CorePropertyDefinitions.java | 10 - .../resources/org/sonar/l10n/core.properties | 15 - .../config/CorePropertyDefinitionsTest.java | 2 +- 48 files changed, 305 insertions(+), 2346 deletions(-) create mode 100644 server/sonar-docs/src/tooltips/editions/community.md create mode 100644 server/sonar-docs/src/tooltips/editions/datacenter.md create mode 100644 server/sonar-docs/src/tooltips/editions/developer.md create mode 100644 server/sonar-docs/src/tooltips/editions/enterprise.md delete mode 100644 server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx delete mode 100644 server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotifContainer.tsx delete mode 100644 server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx delete mode 100644 server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/EditionBoxBadge.tsx delete mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx delete mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx delete mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx delete mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBoxBadge-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/__tests__/UninstallEditionForm-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBoxBadge-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/UninstallEditionForm-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/store/marketplace/utils.ts diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index a6279b12c60..e9c72239901 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -121,7 +121,7 @@ public class ComputeEngineContainerImplTest { + 27 // level 1 + 55 // content of DaoModule + 3 // content of EsModule - + 59 // content of CorePropertyDefinitions + + 58 // content of CorePropertyDefinitions + 1 // StopFlagContainer ); assertThat( diff --git a/server/sonar-docs/src/tooltips/editions/community.md b/server/sonar-docs/src/tooltips/editions/community.md new file mode 100644 index 00000000000..61ca7e8bee5 --- /dev/null +++ b/server/sonar-docs/src/tooltips/editions/community.md @@ -0,0 +1,3 @@ +### Community Edition + +Comes with support for 9 programming languages, numerous plugins, integration with DevOps tool chains, and ability to connect to SonarLint in the IDE. diff --git a/server/sonar-docs/src/tooltips/editions/datacenter.md b/server/sonar-docs/src/tooltips/editions/datacenter.md new file mode 100644 index 00000000000..228f816a023 --- /dev/null +++ b/server/sonar-docs/src/tooltips/editions/datacenter.md @@ -0,0 +1,3 @@ +### Data Center Edition + +Enterprise Edition + component redundancy and data integrity diff --git a/server/sonar-docs/src/tooltips/editions/developer.md b/server/sonar-docs/src/tooltips/editions/developer.md new file mode 100644 index 00000000000..6c1bf7f0461 --- /dev/null +++ b/server/sonar-docs/src/tooltips/editions/developer.md @@ -0,0 +1,3 @@ +### Developer Edition + +Community Edition + branch analysis, SonarLint push notifications, and 16 languages. diff --git a/server/sonar-docs/src/tooltips/editions/enterprise.md b/server/sonar-docs/src/tooltips/editions/enterprise.md new file mode 100644 index 00000000000..918dddbf05b --- /dev/null +++ b/server/sonar-docs/src/tooltips/editions/enterprise.md @@ -0,0 +1,3 @@ +### Enterprise Edition + +Developer Edition + portfolio management, executive reporting, parallel processing of analysis reports and 20 languages. diff --git a/server/sonar-server/src/main/java/org/sonar/server/ui/ws/GlobalAction.java b/server/sonar-server/src/main/java/org/sonar/server/ui/ws/GlobalAction.java index b2b70701e6e..1b5edecc03f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ui/ws/GlobalAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ui/ws/GlobalAction.java @@ -45,7 +45,6 @@ import org.sonar.server.ui.VersionFormatter; import org.sonar.server.user.UserSession; import static org.sonar.api.CoreProperties.RATING_GRID; -import static org.sonar.core.config.CorePropertyDefinitions.EDITIONS_CONFIG_URL; import static org.sonar.core.config.WebConstants.SONAR_LF_ENABLE_GRAVATAR; import static org.sonar.core.config.WebConstants.SONAR_LF_GRAVATAR_SERVER_URL; import static org.sonar.core.config.WebConstants.SONAR_LF_LOGO_URL; @@ -59,7 +58,6 @@ public class GlobalAction implements NavigationWsAction, Startable { SONAR_LF_LOGO_WIDTH_PX, SONAR_LF_ENABLE_GRAVATAR, SONAR_LF_GRAVATAR_SERVER_URL, - EDITIONS_CONFIG_URL, RATING_GRID); private final Map systemSettingValuesByKey; diff --git a/server/sonar-server/src/test/java/org/sonar/server/ui/ws/GlobalActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/ui/ws/GlobalActionTest.java index 0daaf5c5a3a..186264842f8 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/ui/ws/GlobalActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/ui/ws/GlobalActionTest.java @@ -105,7 +105,6 @@ public class GlobalActionTest { settings.setProperty("sonar.lf.gravatarServerUrl", "https://secure.gravatar.com/avatar/{EMAIL_MD5}.jpg?s={SIZE}&d=identicon"); settings.setProperty("sonar.lf.enableGravatar", true); settings.setProperty("sonar.updatecenter.activate", false); - settings.setProperty("sonar.editions.jsonUrl", "https://foo.bar/editions.json"); settings.setProperty("sonar.technicalDebt.ratingGrid", "0.05,0.1,0.2,0.5"); // This setting should be ignored as it's not needed settings.setProperty("sonar.defaultGroup", "sonar-users"); @@ -117,7 +116,6 @@ public class GlobalActionTest { " \"sonar.lf.logoWidthPx\": \"135\"," + " \"sonar.lf.gravatarServerUrl\": \"https://secure.gravatar.com/avatar/{EMAIL_MD5}.jpg?s={SIZE}&d=identicon\"," + " \"sonar.lf.enableGravatar\": \"true\"," + - " \"sonar.editions.jsonUrl\": \"https://foo.bar/editions.json\"," + " \"sonar.updatecenter.activate\": \"false\"," + " \"sonar.technicalDebt.ratingGrid\": \"0.05,0.1,0.2,0.5\"" + " }" + diff --git a/server/sonar-web/src/main/js/api/marketplace.ts b/server/sonar-web/src/main/js/api/marketplace.ts index c04fe5160ae..238efa8b3a3 100644 --- a/server/sonar-web/src/main/js/api/marketplace.ts +++ b/server/sonar-web/src/main/js/api/marketplace.ts @@ -17,45 +17,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { checkStatus, corsRequest, getJSON, parseJSON, post, postJSON } from '../helpers/request'; +import { getJSON, postJSON } from '../helpers/request'; import throwGlobalError from '../app/utils/throwGlobalError'; -export interface Edition { - key: string; - name: string; - textDescription: string; - homeUrl: string; - licenseRequestUrl: string; - downloadUrl: string; -} - -export interface EditionsPerVersion { - [version: string]: Edition[]; -} - export interface EditionStatus { currentEditionKey?: string; - nextEditionKey?: string; - installError?: string; - installationStatus: - | 'NONE' - | 'AUTOMATIC_IN_PROGRESS' - | 'MANUAL_IN_PROGRESS' - | 'AUTOMATIC_READY' - | 'UNINSTALL_IN_PROGRESS'; } export function getEditionStatus(): Promise { return getJSON('/api/editions/status'); } -export function getEditionsList(url: string): Promise { - return corsRequest(url) - .submit() - .then(checkStatus) - .then(parseJSON); -} - export function getLicensePreview(data: { license: string; }): Promise<{ @@ -72,11 +44,3 @@ export function getFormData(): Promise<{ serverId: string; ncloc: number }> { export function applyLicense(data: { license: string }): Promise { return postJSON('/api/editions/apply_license', data).catch(throwGlobalError); } - -export function uninstallEdition(): Promise { - return post('/api/editions/uninstall').catch(throwGlobalError); -} - -export function dismissErrorMessage(): Promise { - return post('/api/editions/clear_error_message').catch(throwGlobalError); -} diff --git a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx index c81e450022d..3347e3e922f 100644 --- a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx @@ -22,20 +22,10 @@ import * as PropTypes from 'prop-types'; import Helmet from 'react-helmet'; import { connect } from 'react-redux'; import SettingsNav from './nav/settings/SettingsNav'; -import { - getAppState, - getGlobalSettingValue, - getMarketplaceEditionStatus, - getMarketplacePendingPlugins -} from '../../store/rootReducer'; +import { getAppState, getMarketplacePendingPlugins } from '../../store/rootReducer'; import { getSettingsNavigation } from '../../api/nav'; -import { EditionStatus, getEditionStatus } from '../../api/marketplace'; import { setAdminPages } from '../../store/appState/duck'; -import { - fetchEditions, - setEditionStatus, - fetchPendingPlugins -} from '../../store/marketplace/actions'; +import { fetchCurrentEdition, fetchPendingPlugins } from '../../store/marketplace/actions'; import { translate } from '../../helpers/l10n'; import { Extension } from '../types'; import { PluginPendingResult } from '../../api/plugins'; @@ -47,23 +37,22 @@ interface StateProps { organizationsEnabled: boolean; version: string; }; - editionStatus?: EditionStatus; - editionsUrl: string; pendingPlugins: PluginPendingResult; } -interface DispatchProps { - fetchEditions: (url: string, version: string) => void; +interface DispatchToProps { fetchPendingPlugins: () => void; + fetchCurrentEdition: () => void; setAdminPages: (adminPages: Extension[]) => void; - setEditionStatus: (editionStatus: EditionStatus) => void; } interface OwnProps { location: {}; } -class AdminContainer extends React.PureComponent { +type Props = StateProps & DispatchToProps & OwnProps; + +class AdminContainer extends React.PureComponent { static contextTypes = { canAdmin: PropTypes.bool.isRequired }; @@ -73,17 +62,13 @@ class AdminContainer extends React.PureComponent getSettingsNavigation().then(r => this.props.setAdminPages(r.extensions), () => {}); - fetchEditionStatus = () => - getEditionStatus().then(editionStatus => this.props.setEditionStatus(editionStatus), () => {}); - render() { const { adminPages, organizationsEnabled } = this.props.appState; @@ -98,7 +83,6 @@ class AdminContainer extends React.PureComponent ({ appState: getAppState(state), - editionStatus: getMarketplaceEditionStatus(state), - editionsUrl: (getGlobalSettingValue(state, 'sonar.editions.jsonUrl') || {}).value, pendingPlugins: getMarketplacePendingPlugins(state) }); -const mapDispatchToProps: DispatchProps = { - setAdminPages, - setEditionStatus, - fetchEditions, - fetchPendingPlugins +const mapDispatchToProps: DispatchToProps = { + fetchCurrentEdition, + fetchPendingPlugins, + setAdminPages }; -export default connect(mapStateToProps, mapDispatchToProps)(AdminContainer); +export default connect(mapStateToProps, mapDispatchToProps)( + AdminContainer +); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx index f5ca55b6f83..8f9d34a94fb 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx @@ -81,7 +81,7 @@ export default class ComponentNav extends React.PureComponent { } return (
diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx deleted file mode 100644 index c4f1af5e32b..00000000000 --- a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx +++ /dev/null @@ -1,179 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import * as React from 'react'; -import { FormattedMessage } from 'react-intl'; -import NavBarNotif from '../../../../components/nav/NavBarNotif'; -import RestartForm from '../../../../components/common/RestartForm'; -import { Button } from '../../../../components/ui/buttons'; -import { dismissErrorMessage, Edition, EditionStatus } from '../../../../api/marketplace'; -import { translate, translateWithParameters } from '../../../../helpers/l10n'; - -interface Props { - editions?: Edition[]; - editionStatus: EditionStatus; - preventRestart: boolean; - setEditionStatus: (editionStatus: EditionStatus) => void; -} - -interface State { - openRestart: boolean; -} - -export default class SettingsEditionsNotif extends React.PureComponent { - state: State = { openRestart: false }; - - handleOpenRestart = () => this.setState({ openRestart: true }); - hanleCloseRestart = () => this.setState({ openRestart: false }); - - handleDismissError = () => - dismissErrorMessage().then( - () => this.props.setEditionStatus({ ...this.props.editionStatus, installError: undefined }), - () => {} - ); - - renderStatusMsg(edition?: Edition) { - const { editionStatus } = this.props; - return ( - - - - {edition - ? translateWithParameters( - 'marketplace.edition_status_x.' + editionStatus.installationStatus, - edition.name - ) - : translate('marketplace.edition_status', editionStatus.installationStatus)} - - - ); - } - - renderRestartMsg(edition?: Edition) { - const { editionStatus, preventRestart } = this.props; - return ( - - - {edition - ? translateWithParameters( - 'marketplace.edition_status_x.' + editionStatus.installationStatus, - edition.name - ) - : translate('marketplace.edition_status', editionStatus.installationStatus)} - - {edition && - edition.key === 'datacenter' && ( - - - {edition.name} - - ) - }} - /> - - )} - {!preventRestart && ( - - )} - {!preventRestart && - this.state.openRestart && } - - ); - } - - renderManualMsg(edition?: Edition) { - const { editionStatus } = this.props; - return ( - - {edition - ? translateWithParameters( - 'marketplace.edition_status_x.' + editionStatus.installationStatus, - edition.name - ) - : translate('marketplace.edition_status', editionStatus.installationStatus)} - - {translate('marketplace.how_to_install')} - - {edition && ( - - {translate('marketplace.download_package')} - - )} - - ); - } - - renderStatusAlert() { - const { currentEditionKey, installationStatus, nextEditionKey } = this.props.editionStatus; - const nextEdition = - this.props.editions && this.props.editions.find(edition => edition.key === nextEditionKey); - const currentEdition = - this.props.editions && - this.props.editions.find( - edition => - edition.key === currentEditionKey || (!currentEditionKey && edition.key === 'community') - ); - - switch (installationStatus) { - case 'AUTOMATIC_IN_PROGRESS': - return this.renderStatusMsg(nextEdition); - case 'AUTOMATIC_READY': - return this.renderRestartMsg(nextEdition); - case 'UNINSTALL_IN_PROGRESS': - return this.renderRestartMsg(currentEdition); - case 'MANUAL_IN_PROGRESS': - return this.renderManualMsg(nextEdition); - } - return null; - } - - render() { - const { installError } = this.props.editionStatus; - if (installError) { - return ( - - {installError} - - ); - } - - return this.renderStatusAlert(); - } -} diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotifContainer.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotifContainer.tsx deleted file mode 100644 index 8173ffdb62a..00000000000 --- a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotifContainer.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { connect } from 'react-redux'; -import SettingsEditionsNotif from './SettingsEditionsNotif'; -import { getAppState, getMarketplaceEditions } from '../../../../store/rootReducer'; -import { Edition, EditionStatus } from '../../../../api/marketplace'; -import { setEditionStatus } from '../../../../store/marketplace/actions'; - -interface OwnProps { - editionStatus: EditionStatus; -} - -interface StateToProps { - editions?: Edition[]; - preventRestart: boolean; -} - -interface DispatchToProps { - setEditionStatus: (editionStatus: EditionStatus) => void; -} - -const mapStateToProps = (state: any): StateToProps => ({ - editions: getMarketplaceEditions(state), - preventRestart: !getAppState(state).standalone -}); - -const mapDispatchToProps = { setEditionStatus }; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(SettingsEditionsNotif); diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx index 397603fe68d..08b49b3099f 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx @@ -20,20 +20,17 @@ import * as React from 'react'; import * as classNames from 'classnames'; import { IndexLink, Link } from 'react-router'; -import SettingsEditionsNotifContainer from './SettingsEditionsNotifContainer'; import PendingPluginsActionNotif from './PendingPluginsActionNotif'; import * as theme from '../../../../app/theme'; import ContextNavBar from '../../../../components/nav/ContextNavBar'; +import Dropdown from '../../../../components/controls/Dropdown'; import NavBarTabs from '../../../../components/nav/NavBarTabs'; -import { EditionStatus } from '../../../../api/marketplace'; import { Extension } from '../../../types'; -import { translate } from '../../../../helpers/l10n'; -import Dropdown from '../../../../components/controls/Dropdown'; import { PluginPendingResult } from '../../../../api/plugins'; import DropdownIcon from '../../../../components/icons-components/DropdownIcon'; +import { translate } from '../../../../helpers/l10n'; interface Props { - editionStatus?: EditionStatus; extensions: Extension[]; fetchPendingPlugins: () => void; location: {}; @@ -232,23 +229,16 @@ export default class SettingsNav extends React.PureComponent { } render() { - const { editionStatus, extensions, pendingPlugins } = this.props; + const { extensions, pendingPlugins } = this.props; const hasSupportExtension = extensions.find(extension => extension.key === 'license/support'); + const totalPendingPlugins = + pendingPlugins.installing.length + + pendingPlugins.removing.length + + pendingPlugins.updating.length; - const notifComponents = []; - if ( - editionStatus && - (editionStatus.installError || editionStatus.installationStatus !== 'NONE') - ) { - notifComponents.push(); - } - - if ( - pendingPlugins.installing.length > 0 || - pendingPlugins.removing.length > 0 || - pendingPlugins.updating.length > 0 - ) { - notifComponents.push( + let notifComponent; + if (totalPendingPlugins > 0) { + notifComponent = ( { ); } - const notifContainer = - notifComponents.length > 0 ? ( -
- {notifComponents.map((element, index) =>
{element}
)} -
- ) : null; - return ( + notif={notifComponent}>

{translate('layout.settings')}

diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx deleted file mode 100644 index 0f40d0bc59a..00000000000 --- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx +++ /dev/null @@ -1,130 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -/* eslint-disable import/order */ -import * as React from 'react'; -import { mount, shallow } from 'enzyme'; -import { click } from '../../../../../helpers/testUtils'; -import SettingsEditionsNotif from '../SettingsEditionsNotif'; - -jest.mock('../../../../../api/marketplace', () => ({ - dismissErrorMessage: jest.fn(() => Promise.resolve()) -})); - -const dismissMsg = require('../../../../../api/marketplace').dismissErrorMessage as jest.Mock; - -beforeEach(() => { - dismissMsg.mockClear(); -}); - -it('should display an in progress notif', () => { - const wrapper = shallow( - - ); - expect(wrapper).toMatchSnapshot(); -}); - -it('should display a ready notification', () => { - const wrapper = shallow( - - ); - expect(wrapper).toMatchSnapshot(); -}); - -it('should display a manual installation notification', () => { - const wrapper = shallow( - - ); - expect(wrapper).toMatchSnapshot(); -}); - -it('should display install errors', () => { - const wrapper = shallow( - - ); - expect(wrapper).toMatchSnapshot(); -}); - -it('should allow to dismiss install errors', async () => { - const setEditionStatus = jest.fn(); - const wrapper = mount( - - ); - click(wrapper.find('button')); - expect(dismissMsg).toHaveBeenCalled(); - await new Promise(setImmediate); - expect(setEditionStatus).toHaveBeenCalledWith({ - installationStatus: 'NONE', - installError: undefined - }); -}); - -it('should not display the restart button', () => { - const wrapper = shallow( - - ); - expect(wrapper.find('button.js-restart').exists()).toBeFalsy(); -}); - -it('should have a link to cluster documentation for datacenter edition', () => { - const editions = [{ key: 'datacenter' }] as any; - const wrapper = shallow( - - ); - expect(wrapper.find('FormattedMessage').exists()).toBeTruthy(); -}); diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx index 256662531cb..71ca8fd83ed 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx @@ -35,3 +35,28 @@ it('should work with extensions', () => { expect(wrapper).toMatchSnapshot(); expect(wrapper.find('Dropdown')).toMatchSnapshot(); }); + +it('should display a pending plugin notif', () => { + const extensions = [{ key: 'foo', name: 'Foo' }]; + const wrapper = shallow( + {}} + location={{}} + organizationsEnabled={false} + pendingPlugins={{ + installing: [ + { + key: 'foo', + name: 'Foo', + version: '1.0', + implementationBuild: '1' + } + ], + removing: [], + updating: [] + }} + /> + ); + expect(wrapper.find('ContextNavBar').prop('notif')).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap deleted file mode 100644 index 051f00fce94..00000000000 --- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap +++ /dev/null @@ -1,62 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display a manual installation notification 1`] = ` - - marketplace.edition_status_x.MANUAL_IN_PROGRESS.Foo - - marketplace.how_to_install - - - marketplace.download_package - - -`; - -exports[`should display a ready notification 1`] = ` - - - marketplace.edition_status.AUTOMATIC_READY - - - -`; - -exports[`should display an in progress notif 1`] = ` - - - - marketplace.edition_status.AUTOMATIC_IN_PROGRESS - - -`; - -exports[`should display install errors 1`] = ` - - Foo error - -`; diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap index 29a88938c4b..56d25c693cf 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap @@ -1,10 +1,29 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`should display a pending plugin notif 1`] = ` + +`; + exports[`should work with extensions 1`] = `
{ HelpTooltip, HomePageSelect, Level, - LicenseEditionSet, ListFooter, LockIcon, Modal, diff --git a/server/sonar-web/src/main/js/apps/marketplace/App.tsx b/server/sonar-web/src/main/js/apps/marketplace/App.tsx index 8b151058a49..966ab400de0 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/App.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/App.tsx @@ -36,21 +36,16 @@ import { PluginPendingResult, getInstalledPlugins } from '../../api/plugins'; -import { Edition, EditionStatus } from '../../api/marketplace'; import { RawQuery } from '../../helpers/query'; import { translate } from '../../helpers/l10n'; import './style.css'; export interface Props { - editions?: Edition[]; - editionsReadOnly: boolean; - editionStatus?: EditionStatus; + currentEdition?: string; fetchPendingPlugins: () => void; - loadingEditions: boolean; location: { pathname: string; query: RawQuery }; pendingPlugins: PluginPendingResult; standaloneMode: boolean; - setEditionStatus: (editionStatus: EditionStatus) => void; updateCenterActive: boolean; } @@ -66,13 +61,7 @@ export default class App extends React.PureComponent { router: PropTypes.object.isRequired }; - constructor(props: Props) { - super(props); - this.state = { - loadingPlugins: true, - plugins: [] - }; - } + state: State = { loadingPlugins: true, plugins: [] }; componentDidMount() { this.mounted = true; @@ -130,7 +119,7 @@ export default class App extends React.PureComponent { }; render() { - const { editions, editionStatus, standaloneMode, pendingPlugins } = this.props; + const { currentEdition, standaloneMode, pendingPlugins } = this.props; const { loadingPlugins, plugins } = this.state; const query = parseQuery(this.props.location.query); const filteredPlugins = query.search ? filterPlugins(plugins, query.search) : plugins; @@ -140,15 +129,7 @@ export default class App extends React.PureComponent {
- + void; fetchPendingPlugins: () => void; } -const mapStateToProps = (state: any) => ({ - editions: getMarketplaceEditions(state), - editionsReadOnly: getMarketplaceState(state).readOnly, - editionStatus: getMarketplaceEditionStatus(state), - loadingEditions: getMarketplaceState(state).loading, - pendingPlugins: getMarketplacePendingPlugins(state), - standaloneMode: getAppState(state).standalone, - updateCenterActive: - (getGlobalSettingValue(state, 'sonar.updatecenter.activate') || {}).value === 'true' -}); +const mapStateToProps = (state: any) => { + return { + currentEdition: getMarketplaceCurrentEdition(state), + pendingPlugins: getMarketplacePendingPlugins(state), + standaloneMode: getAppState(state).standalone, + updateCenterActive: + (getGlobalSettingValue(state, 'sonar.updatecenter.activate') || {}).value === 'true' + }; +}; -const mapDispatchToProps = { setEditionStatus, fetchPendingPlugins }; +const mapDispatchToProps = { fetchPendingPlugins }; export default connect( mapStateToProps, diff --git a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx index d169e5b31a4..0b762d28f7e 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx @@ -17,144 +17,59 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import * as React from 'react'; -import { FormattedMessage } from 'react-intl'; import EditionBox from './components/EditionBox'; -import LicenseEditionForm from './components/LicenseEditionForm'; -import UninstallEditionForm from './components/UninstallEditionForm'; -import { sortEditions } from './utils'; -import { Edition, EditionStatus } from '../../api/marketplace'; -import { translate } from '../../helpers/l10n'; +import { EDITIONS } from './utils'; +import { getFormData } from '../../api/marketplace'; export interface Props { - canInstall: boolean; - canUninstall: boolean; - editions?: Edition[]; - editionStatus?: EditionStatus; - loading: boolean; - updateCenterActive: boolean; - updateEditionStatus: (editionStatus: EditionStatus) => void; + currentEdition?: string; } interface State { - installEdition?: Edition; - openUninstallForm: boolean; + serverId?: string; + ncloc?: number; } export default class EditionBoxes extends React.PureComponent { - state: State = { openUninstallForm: false }; - - handleOpenLicenseForm = (edition: Edition) => this.setState({ installEdition: edition }); - handleCloseLicenseForm = () => this.setState({ installEdition: undefined }); - - handleOpenUninstallForm = () => this.setState({ openUninstallForm: true }); - handleCloseUninstallForm = () => this.setState({ openUninstallForm: false }); - - renderForms(sortedEditions: Edition[], installedIdx?: number) { - const { canInstall, canUninstall, editionStatus } = this.props; - const { installEdition, openUninstallForm } = this.state; - const installEditionIdx = - installEdition && sortedEditions.findIndex(edition => edition.key === installEdition.key); + mounted = false; + state: State = {}; - if (canInstall && installEdition) { - return ( - - ); - } - - if (canUninstall && openUninstallForm && editionStatus && editionStatus.currentEditionKey) { - return ( - edition.key === editionStatus.currentEditionKey)} - editionStatus={editionStatus} - onClose={this.handleCloseUninstallForm} - updateEditionStatus={this.props.updateEditionStatus} - /> - ); - } - - return null; + componentDidMount() { + this.mounted = true; + this.fetchFormData(); } - render() { - const { canInstall, canUninstall, editions, loading } = this.props; - - if (loading) { - return ; - } - - if (!editions) { - return ( -
- - - SonarSource.com - - ) - }} - /> - -
- ); - } + componentWillUnmount() { + this.mounted = false; + } - const sortedEditions = sortEditions(editions); - const status = this.props.editionStatus || { installationStatus: 'NONE' }; - const inProgressStatus = [ - 'AUTOMATIC_IN_PROGRESS', - 'AUTOMATIC_READY', - 'UNINSTALL_IN_PROGRESS' - ].includes(status.installationStatus); - const installedIdx = sortedEditions.findIndex( - edition => edition.key === status.currentEditionKey + fetchFormData = () => { + getFormData().then( + formData => { + if (this.mounted) { + this.setState({ ...formData }); + } + }, + () => {} ); - const nextIdx = sortedEditions.findIndex(edition => edition.key === status.nextEditionKey); - const currentIdx = inProgressStatus ? nextIdx : installedIdx; + }; + + render() { + const { currentEdition } = this.props; + const { serverId, ncloc } = this.state; return (
- 0} - edition={sortedEditions[0]} - editionStatus={status} - key={sortedEditions[0].key} - onAction={this.handleOpenUninstallForm} - /> - {sortedEditions - .slice(1) - .map((edition, idx) => ( - idx + 1 - ? translate('marketplace.downgrade') - : translate('marketplace.upgrade') - } - disableAction={inProgressStatus} - displayAction={canInstall && currentIdx !== idx + 1} - edition={edition} - editionStatus={status} - key={edition.key} - onAction={this.handleOpenLicenseForm} - /> - ))} - - {this.renderForms(sortedEditions, installedIdx)} + {EDITIONS.map(edition => ( + + ))}
); } diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx index 6e153cd084d..dd49bb10079 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx @@ -20,110 +20,22 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import EditionBoxes from '../EditionBoxes'; -import { EditionStatus } from '../../../api/marketplace'; -const DEFAULT_STATUS: EditionStatus = { - currentEditionKey: 'developer', - nextEditionKey: '', - installationStatus: 'NONE' -}; - -const DEFAULT_EDITIONS = [ - { - key: 'developer', - name: 'Developer Edition', - textDescription: 'foo', - downloadUrl: 'download_url', - homeUrl: 'more_url', - licenseRequestUrl: 'license_url' - }, - { - key: 'comunity', - name: 'Comunity Edition', - textDescription: 'bar', - downloadUrl: 'download_url', - homeUrl: 'more_url', - licenseRequestUrl: 'license_url' - } -]; +jest.mock('../utils', () => ({ + EDITIONS: [ + { key: 'comunity', homeUrl: 'more_url' }, + { key: 'developer', downloadUrl: 'download_url', homeUrl: 'more_url' } + ] +})); it('should display the edition boxes correctly', () => { - const wrapper = getWrapper({ editions: DEFAULT_EDITIONS, loading: true }); - expect(wrapper).toMatchSnapshot(); - wrapper.setProps({ loading: false }); - expect(wrapper).toMatchSnapshot(); -}); - -it('should display an error message', () => { - const wrapper = getWrapper(); - expect(wrapper).toMatchSnapshot(); -}); - -it('should display community without the downgrade button', () => { - const communityBox = getWrapper({ - editions: DEFAULT_EDITIONS, - editionStatus: { - currentEditionKey: '', - installationStatus: 'NONE' - }, - loading: false - }) - .find('EditionBox') - .first(); - expect(communityBox.prop('displayAction')).toBeFalsy(); -}); - -it('should not display action buttons', () => { - const wrapper = getWrapper({ - editions: DEFAULT_EDITIONS, - editionStatus: { - currentEditionKey: '', - installationStatus: 'NONE' - }, - loading: false, - canInstall: false, - canUninstall: false - }); - wrapper.find('EditionBox').forEach(box => expect(box.prop('displayAction')).toBeFalsy()); -}); - -it('should display disabled action buttons', () => { - const wrapper = getWrapper({ - editions: DEFAULT_EDITIONS, - editionStatus: { installationStatus: 'AUTOMATIC_IN_PROGRESS', nextEditionKey: 'developer' }, - loading: false - }); - - wrapper.find('EditionBox').forEach(box => expect(box.prop('disableAction')).toBeTruthy()); - expect(wrapper.find('EditionBox').map(box => box.prop('displayAction'))).toEqual([true, false]); - - wrapper.setProps({ - editionStatus: { currentEditionKey: 'developer', installationStatus: 'UNINSTALL_IN_PROGRESS' } - }); - wrapper.find('EditionBox').forEach(box => expect(box.prop('disableAction')).toBeTruthy()); - expect(wrapper.find('EditionBox').map(box => box.prop('displayAction'))).toEqual([false, true]); - - wrapper.setProps({ editionStatus: { installationStatus: 'AUTOMATIC_READY' } }); - wrapper.find('EditionBox').forEach(box => expect(box.prop('disableAction')).toBeTruthy()); + expect(getWrapper()).toMatchSnapshot(); }); -it('should open the license form', () => { - const wrapper = getWrapper({ editions: DEFAULT_EDITIONS }); - (wrapper.instance() as EditionBoxes).handleOpenLicenseForm(DEFAULT_EDITIONS[0]); - wrapper.update(); - expect(wrapper.find('LicenseEditionForm').exists()).toBeTruthy(); +it('should display the developer edition as installed', () => { + expect(getWrapper({ currentEdition: 'developer' })).toMatchSnapshot(); }); function getWrapper(props = {}) { - return shallow( - - ); + return shallow(); } diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap index 85ba8946957..687a2cde364 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap @@ -1,87 +1,57 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should display an error message 1`] = ` +exports[`should display the developer edition as installed 1`] = `
- - - SonarSource.com - , - } + - + } + key="comunity" + /> +
`; exports[`should display the edition boxes correctly 1`] = ` - -`; - -exports[`should display the edition boxes correctly 2`] = `
`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx index 0ad689bc93d..968aa566020 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx @@ -18,45 +18,37 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import EditionBoxBadge from './EditionBoxBadge'; -import { Edition, EditionStatus } from '../../../api/marketplace'; -import { Button } from '../../../components/ui/buttons'; +import CheckIcon from '../../../components/icons-components/CheckIcon'; import { translate } from '../../../helpers/l10n'; +import DocInclude from '../../../components/docs/DocInclude'; +import { Edition, getEditionUrl } from '../utils'; interface Props { - actionLabel: string; - disableAction: boolean; - displayAction: boolean; + currentEdition: string; edition: Edition; - editionStatus?: EditionStatus; - onAction: (edition: Edition) => void; + ncloc?: number; + serverId?: string; } -export default class EditionBox extends React.PureComponent { - handleAction = () => { - this.props.onAction(this.props.edition); - }; - - render() { - const { disableAction, displayAction, edition, editionStatus } = this.props; - return ( -
- {editionStatus && } -
-

{edition.name}

-

{edition.textDescription}

-
-
- - {translate('marketplace.learn_more')} - - {displayAction && ( - - )} -
+export default function EditionBox({ currentEdition, edition, ncloc, serverId }: Props) { + const isInstalled = currentEdition === edition.key; + const url = getEditionUrl(edition, { ncloc, serverId, sourceEdition: currentEdition }); + return ( +
+ {isInstalled && ( + + + {translate('marketplace.installed')} + + )} +
+ +
+ - ); - } +
+ ); } diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBoxBadge.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBoxBadge.tsx deleted file mode 100644 index 341bd2e221b..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBoxBadge.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import * as React from 'react'; -import CheckIcon from '../../../components/icons-components/CheckIcon'; -import { EditionStatus } from '../../../api/marketplace'; -import { translate } from '../../../helpers/l10n'; - -interface Props { - editionKey: string; - status: EditionStatus; -} - -export default function EditionBoxBadge({ editionKey, status }: Props) { - const isInstalled = - status.currentEditionKey === editionKey || - (!status.currentEditionKey && editionKey === 'community'); - const isProgressing = - status.nextEditionKey === editionKey || (!status.nextEditionKey && editionKey === 'community'); - const inProgressStatus = [ - 'AUTOMATIC_READY', - 'AUTOMATIC_IN_PROGRESS', - 'UNINSTALL_IN_PROGRESS' - ].includes(status.installationStatus); - - if (inProgressStatus) { - if (isProgressing) { - return ( - - {status.installationStatus === 'AUTOMATIC_IN_PROGRESS' - ? translate('marketplace.installing') - : translate('marketplace.pending')} - - ); - } - } else if (isInstalled) { - return ( - - - {translate('marketplace.installed')} - - ); - } - - return null; -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx deleted file mode 100644 index a4c48287869..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx +++ /dev/null @@ -1,114 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import * as React from 'react'; -import LicenseEditionSet from './LicenseEditionSet'; -import { Edition, EditionStatus, applyLicense } from '../../../api/marketplace'; -import Modal from '../../../components/controls/Modal'; -import { Button, ResetButtonLink } from '../../../components/ui/buttons'; -import { translate, translateWithParameters } from '../../../helpers/l10n'; - -export interface Props { - edition: Edition; - editions: Edition[]; - isDowngrade: boolean; - onClose: () => void; - updateEditionStatus: (editionStatus: EditionStatus) => void; -} - -interface State { - license: string; - status?: string; - submitting: boolean; -} - -export default class LicenseEditionForm extends React.PureComponent { - mounted = false; - state: State = { license: '', submitting: false }; - - componentDidMount() { - this.mounted = true; - } - - componentWillUnmount() { - this.mounted = false; - } - - handleLicenseChange = (license: string, status?: string) => { - if (this.mounted) { - this.setState({ license, status }); - } - }; - - handleConfirmClick = () => { - const { license, status } = this.state; - if (license && status) { - this.setState({ submitting: true }); - applyLicense({ license }).then( - editionStatus => { - this.props.updateEditionStatus(editionStatus); - this.props.onClose(); - }, - () => { - if (this.mounted) { - this.setState({ submitting: false }); - } - } - ); - } - }; - - render() { - const { edition, isDowngrade } = this.props; - const { license, submitting, status } = this.state; - - const header = isDowngrade - ? translateWithParameters('marketplace.downgrade_to_x', edition.name) - : translateWithParameters('marketplace.upgrade_to_x', edition.name); - return ( - -
-

{header}

-
- - - -
- {submitting && } - {status && ( - - )} - {translate('cancel')} -
-
- ); - } -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx deleted file mode 100644 index b8099a65079..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx +++ /dev/null @@ -1,269 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { stringify } from 'querystring'; -import * as React from 'react'; -import * as classNames from 'classnames'; -import { FormattedMessage } from 'react-intl'; -import { debounce } from 'lodash'; -import Checkbox from '../../../components/controls/Checkbox'; -import DeferredSpinner from '../../../components/common/DeferredSpinner'; -import { omitNil } from '../../../helpers/request'; -import { Edition, getFormData, getLicensePreview } from '../../../api/marketplace'; -import { translate, translateWithParameters } from '../../../helpers/l10n'; - -export interface Props { - className?: string; - edition?: Edition; - editions: Edition[]; - updateLicense: (license?: string, status?: string) => void; -} - -interface State { - acceptTerms: boolean; - formData?: { - serverId?: string; - ncloc?: number; - }; - license: string; - licenseEdition?: Edition; - loading: boolean; - previewStatus?: string; - wrongEdition: boolean; -} - -export default class LicenseEditionSet extends React.PureComponent { - mounted = false; - - constructor(props: Props) { - super(props); - this.state = { acceptTerms: false, license: '', loading: false, wrongEdition: false }; - this.fetchLicensePreview = debounce(this.fetchLicensePreview, 100); - } - - componentDidMount() { - this.mounted = true; - this.fetchFormData(); - } - - componentWillUnmount() { - this.mounted = false; - } - - fetchLicensePreview = (license: string) => { - this.setState({ loading: true }); - getLicensePreview({ license }).then( - ({ previewStatus, nextEditionKey }) => { - if (this.mounted) { - const { edition } = this.props; - const licenseEdition = this.props.editions.find( - edition => edition.key === nextEditionKey - ); - const wrongEdition = Boolean( - !licenseEdition || (edition && edition.key !== nextEditionKey) - ); - this.setLicense({ license, loading: false, licenseEdition, previewStatus, wrongEdition }); - } - }, - () => { - if (this.mounted) { - this.resetLicense({ license, loading: false }); - } - } - ); - }; - - fetchFormData = () => { - getFormData().then( - formData => { - if (this.mounted) { - this.setState({ formData }); - } - }, - () => {} - ); - }; - - getLicenseFormUrl = (edition: Edition) => { - let url = edition.licenseRequestUrl; - if (this.state.formData) { - const query = stringify(omitNil(this.state.formData)); - if (query) { - url += '?' + query; - } - } - return url; - }; - - handleLicenseChange = (event: React.SyntheticEvent) => { - const license = event.currentTarget.value; - if (license) { - this.fetchLicensePreview(license); - this.setState({ license }); - } else { - this.resetLicense({}); - } - }; - - handleTermsCheck = (checked: boolean) => { - this.setLicense({ acceptTerms: checked }); - }; - - resetLicense(state: Pick) { - this.setLicense( - Object.assign( - { - license: '', - licenseEdition: undefined, - previewStatus: undefined, - wrongEdition: false - }, - state - ) - ); - } - - setLicense(state: Pick) { - this.setState(state, this.updateParentLicense); - } - - updateParentLicense = () => { - const { acceptTerms, license, previewStatus, wrongEdition } = this.state; - this.props.updateLicense( - previewStatus !== 'NO_INSTALL' && !acceptTerms ? undefined : license, - wrongEdition ? undefined : previewStatus - ); - }; - - renderAlert() { - const { licenseEdition, previewStatus, wrongEdition } = this.state; - if (!previewStatus || wrongEdition) { - const { edition } = this.props; - - return ( -
- {wrongEdition && ( -

- {edition - ? translateWithParameters('marketplace.wrong_license_type_x', edition.name) - : translate('marketplace.wrong_license_type')} -

- )} - {edition && ( - - {translate('marketplace.i_need_a_license')} - - )} -
- ); - } - - return ( -
-

- {translateWithParameters( - 'marketplace.license_preview_status.' + previewStatus, - licenseEdition ? licenseEdition.name : translate('marketplace.commercial_edition') - )} - {licenseEdition && - licenseEdition.key === 'datacenter' && - previewStatus !== 'NO_INSTALL' && ( - - - {licenseEdition.name} - - ) - }} - /> - - )} -

- {previewStatus !== 'NO_INSTALL' && ( - - - - - - {translate('marketplace.terms_and_conditions')} - - - )} -
- ); - } - - render() { - const { className, edition } = this.props; - const { license, loading } = this.state; - - return ( -
- {edition && ( - - )} -