]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10696 Remove ability to upgrade/downgrade an edition from Marketplace (#269)
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Mon, 28 May 2018 11:10:23 +0000 (13:10 +0200)
committerSonarTech <sonartech@sonarsource.com>
Tue, 12 Jun 2018 18:20:58 +0000 (20:20 +0200)
* 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

48 files changed:
server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
server/sonar-docs/src/tooltips/editions/community.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/editions/datacenter.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/editions/developer.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/editions/enterprise.md [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/ui/ws/GlobalAction.java
server/sonar-server/src/test/java/org/sonar/server/ui/ws/GlobalActionTest.java
server/sonar-web/src/main/js/api/marketplace.ts
server/sonar-web/src/main/js/app/components/AdminContainer.tsx
server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx
server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx [deleted file]
server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotifContainer.tsx [deleted file]
server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx
server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx [deleted file]
server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx
server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap
server/sonar-web/src/main/js/app/styles/components/alerts.css
server/sonar-web/src/main/js/app/utils/exposeLibraries.ts
server/sonar-web/src/main/js/apps/marketplace/App.tsx
server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx
server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx
server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx
server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap
server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx
server/sonar-web/src/main/js/apps/marketplace/components/EditionBoxBadge.tsx [deleted file]
server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx [deleted file]
server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx [deleted file]
server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx [deleted file]
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBoxBadge-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/UninstallEditionForm-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBoxBadge-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/UninstallEditionForm-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/marketplace/style.css
server/sonar-web/src/main/js/apps/marketplace/utils.ts
server/sonar-web/src/main/js/store/marketplace/actions.ts
server/sonar-web/src/main/js/store/marketplace/reducer.ts
server/sonar-web/src/main/js/store/marketplace/utils.ts [deleted file]
server/sonar-web/src/main/js/store/rootReducer.js
sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java
sonar-core/src/main/resources/org/sonar/l10n/core.properties
sonar-core/src/test/java/org/sonar/core/config/CorePropertyDefinitionsTest.java

index a6279b12c60bf7b5ce097c9cf9cfc9fdb1279ae2..e9c72239901e661593c5232217a0ef7a597fd312 100644 (file)
@@ -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 (file)
index 0000000..61ca7e8
--- /dev/null
@@ -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 (file)
index 0000000..228f816
--- /dev/null
@@ -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 (file)
index 0000000..6c1bf7f
--- /dev/null
@@ -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 (file)
index 0000000..918dddb
--- /dev/null
@@ -0,0 +1,3 @@
+### Enterprise Edition
+
+Developer Edition + portfolio management, executive reporting, parallel processing of analysis reports and 20 languages.
index b2b70701e6e68a90672051fecab752a951b7884f..1b5edecc03f23ab5136fe66d2ec7ce2e4923481a 100644 (file)
@@ -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<String, String> systemSettingValuesByKey;
index 0daaf5c5a3a3b3079bf34ad479b020b21caf7a0a..186264842f83ec6f50604bc5be3ddb0cc600f34e 100644 (file)
@@ -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\"" +
       "  }" +
index c04fe5160ae1a06103b86fcf23bb9dffa7f8da03..238efa8b3a3d6258962f5312110ea8816d40bf58 100644 (file)
  * 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<EditionStatus> {
   return getJSON('/api/editions/status');
 }
 
-export function getEditionsList(url: string): Promise<EditionsPerVersion> {
-  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<EditionStatus> {
   return postJSON('/api/editions/apply_license', data).catch(throwGlobalError);
 }
-
-export function uninstallEdition(): Promise<void | Response> {
-  return post('/api/editions/uninstall').catch(throwGlobalError);
-}
-
-export function dismissErrorMessage(): Promise<void | Response> {
-  return post('/api/editions/clear_error_message').catch(throwGlobalError);
-}
index c81e450022dbafb5fe3d65d4cb63ba46fe3f46dd..3347e3e922fb5fd934e3d4070423edc05b59a909 100644 (file)
@@ -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<StateProps & DispatchProps & OwnProps> {
+type Props = StateProps & DispatchToProps & OwnProps;
+
+class AdminContainer extends React.PureComponent<Props> {
   static contextTypes = {
     canAdmin: PropTypes.bool.isRequired
   };
@@ -73,17 +62,13 @@ class AdminContainer extends React.PureComponent<StateProps & DispatchProps & Ow
       handleRequiredAuthorization();
     } else {
       this.fetchNavigationSettings();
-      this.props.fetchEditions(this.props.editionsUrl, this.props.appState.version);
-      this.fetchEditionStatus();
+      this.props.fetchCurrentEdition();
     }
   }
 
   fetchNavigationSettings = () =>
     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<StateProps & DispatchProps & Ow
       <div>
         <Helmet defaultTitle={defaultTitle} titleTemplate={'%s - ' + defaultTitle} />
         <SettingsNav
-          editionStatus={this.props.editionStatus}
           extensions={adminPages}
           fetchPendingPlugins={this.props.fetchPendingPlugins}
           location={this.props.location}
@@ -113,16 +97,15 @@ class AdminContainer extends React.PureComponent<StateProps & DispatchProps & Ow
 
 const mapStateToProps = (state: any): StateProps => ({
   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<StateProps, DispatchToProps, OwnProps>(mapStateToProps, mapDispatchToProps)(
+  AdminContainer
+);
index f5ca55b6f8348f77e58b3477b8eb4a18b11dd237..8f9d34a94fbcf92e563d93ccd52823022bd972a8 100644 (file)
@@ -81,7 +81,7 @@ export default class ComponentNav extends React.PureComponent<Props> {
     }
     return (
       <ContextNavBar
-        height={notifComponent ? theme.contextNavHeightRaw + 20 : theme.contextNavHeightRaw}
+        height={notifComponent ? theme.contextNavHeightRaw + 30 : theme.contextNavHeightRaw}
         id="context-navigation"
         notif={notifComponent}>
         <div className="navbar-context-justified">
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 (file)
index c4f1af5..0000000
+++ /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<Props, State> {
-  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 (
-      <NavBarNotif className="alert alert-info">
-        <i className="spinner spacer-right" />
-        <span>
-          {edition
-            ? translateWithParameters(
-                'marketplace.edition_status_x.' + editionStatus.installationStatus,
-                edition.name
-              )
-            : translate('marketplace.edition_status', editionStatus.installationStatus)}
-        </span>
-      </NavBarNotif>
-    );
-  }
-
-  renderRestartMsg(edition?: Edition) {
-    const { editionStatus, preventRestart } = this.props;
-    return (
-      <NavBarNotif className="alert alert-success">
-        <span>
-          {edition
-            ? translateWithParameters(
-                'marketplace.edition_status_x.' + editionStatus.installationStatus,
-                edition.name
-              )
-            : translate('marketplace.edition_status', editionStatus.installationStatus)}
-        </span>
-        {edition &&
-          edition.key === 'datacenter' && (
-            <span className="little-spacer-left">
-              <FormattedMessage
-                defaultMessage={translate('marketplace.see_documentation_to_enable_cluster')}
-                id="marketplace.see_documentation_to_enable_cluster"
-                values={{
-                  url: (
-                    <a
-                      href="https://redirect.sonarsource.com/doc/data-center-edition.html"
-                      rel="noopener noreferrer"
-                      target="_blank">
-                      {edition.name}
-                    </a>
-                  )
-                }}
-              />
-            </span>
-          )}
-        {!preventRestart && (
-          <Button className="js-restart spacer-left" onClick={this.handleOpenRestart}>
-            {translate('marketplace.restart')}
-          </Button>
-        )}
-        {!preventRestart &&
-          this.state.openRestart && <RestartForm onClose={this.hanleCloseRestart} />}
-      </NavBarNotif>
-    );
-  }
-
-  renderManualMsg(edition?: Edition) {
-    const { editionStatus } = this.props;
-    return (
-      <NavBarNotif className="alert alert-danger">
-        {edition
-          ? translateWithParameters(
-              'marketplace.edition_status_x.' + editionStatus.installationStatus,
-              edition.name
-            )
-          : translate('marketplace.edition_status', editionStatus.installationStatus)}
-        <a
-          className="spacer-left"
-          href={
-            edition && edition.key === 'datacenter'
-              ? 'https://redirect.sonarsource.com/doc/data-center-edition.html'
-              : 'https://redirect.sonarsource.com/doc/how-to-install-an-edition.html'
-          }
-          target="_blank">
-          {translate('marketplace.how_to_install')}
-        </a>
-        {edition && (
-          <a
-            className="button spacer-left"
-            download={`sonarqube-${edition.name}.zip`}
-            href={edition.downloadUrl}
-            target="_blank">
-            {translate('marketplace.download_package')}
-          </a>
-        )}
-      </NavBarNotif>
-    );
-  }
-
-  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 (
-        <NavBarNotif className="alert alert-danger" onCancel={this.handleDismissError}>
-          {installError}
-        </NavBarNotif>
-      );
-    }
-
-    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 (file)
index 8173ffd..0000000
+++ /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<StateToProps, DispatchToProps, OwnProps>(
-  mapStateToProps,
-  mapDispatchToProps
-)(SettingsEditionsNotif);
index 397603fe68d3714cd2e8a8984d53a583e85fd040..08b49b3099fb1b027dae2a56718d21fe33a521eb 100644 (file)
 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<Props> {
   }
 
   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(<SettingsEditionsNotifContainer editionStatus={editionStatus} />);
-    }
-
-    if (
-      pendingPlugins.installing.length > 0 ||
-      pendingPlugins.removing.length > 0 ||
-      pendingPlugins.updating.length > 0
-    ) {
-      notifComponents.push(
+    let notifComponent;
+    if (totalPendingPlugins > 0) {
+      notifComponent = (
         <PendingPluginsActionNotif
           pending={pendingPlugins}
           refreshPending={this.props.fetchPendingPlugins}
@@ -256,18 +246,11 @@ export default class SettingsNav extends React.PureComponent<Props> {
       );
     }
 
-    const notifContainer =
-      notifComponents.length > 0 ? (
-        <div className="alert-container">
-          {notifComponents.map((element, index) => <div key={index}>{element}</div>)}
-        </div>
-      ) : null;
-
     return (
       <ContextNavBar
-        height={theme.contextNavHeightRaw + 38 * notifComponents.length}
+        height={notifComponent ? theme.contextNavHeightRaw + 30 : theme.contextNavHeightRaw}
         id="context-navigation"
-        notif={notifContainer}>
+        notif={notifComponent}>
         <header className="navbar-context-header">
           <h1>{translate('layout.settings')}</h1>
         </header>
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 (file)
index 0f40d0b..0000000
+++ /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<any>;
-
-beforeEach(() => {
-  dismissMsg.mockClear();
-});
-
-it('should display an in progress notif', () => {
-  const wrapper = shallow(
-    <SettingsEditionsNotif
-      editionStatus={{ installationStatus: 'AUTOMATIC_IN_PROGRESS' }}
-      preventRestart={false}
-      setEditionStatus={jest.fn()}
-    />
-  );
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should display a ready notification', () => {
-  const wrapper = shallow(
-    <SettingsEditionsNotif
-      editionStatus={{ installationStatus: 'AUTOMATIC_READY' }}
-      preventRestart={false}
-      setEditionStatus={jest.fn()}
-    />
-  );
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should display a manual installation notification', () => {
-  const wrapper = shallow(
-    <SettingsEditionsNotif
-      editionStatus={{ installationStatus: 'MANUAL_IN_PROGRESS', nextEditionKey: 'foo' }}
-      editions={[
-        {
-          key: 'foo',
-          name: 'Foo',
-          textDescription: 'Foo desc',
-          downloadUrl: 'download_url',
-          homeUrl: 'more_url',
-          licenseRequestUrl: 'license_url'
-        }
-      ]}
-      preventRestart={false}
-      setEditionStatus={jest.fn()}
-    />
-  );
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should display install errors', () => {
-  const wrapper = shallow(
-    <SettingsEditionsNotif
-      editionStatus={{ installationStatus: 'AUTOMATIC_IN_PROGRESS', installError: 'Foo error' }}
-      preventRestart={false}
-      setEditionStatus={jest.fn()}
-    />
-  );
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should allow to dismiss install errors', async () => {
-  const setEditionStatus = jest.fn();
-  const wrapper = mount(
-    <SettingsEditionsNotif
-      editionStatus={{ installationStatus: 'NONE', installError: 'Foo error' }}
-      preventRestart={false}
-      setEditionStatus={setEditionStatus}
-    />
-  );
-  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(
-    <SettingsEditionsNotif
-      editionStatus={{ installationStatus: 'AUTOMATIC_READY' }}
-      preventRestart={true}
-      setEditionStatus={jest.fn()}
-    />
-  );
-  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(
-    <SettingsEditionsNotif
-      editions={editions}
-      editionStatus={{ installationStatus: 'AUTOMATIC_READY', nextEditionKey: 'datacenter' }}
-      preventRestart={false}
-      setEditionStatus={jest.fn()}
-    />
-  );
-  expect(wrapper.find('FormattedMessage').exists()).toBeTruthy();
-});
index 256662531cb263b7beae2e7ecd16bb3d979be2ed..71ca8fd83edc868a3095289eae59c2332dc695e7 100644 (file)
@@ -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(
+    <SettingsNav
+      extensions={extensions}
+      fetchPendingPlugins={() => {}}
+      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 (file)
index 051f00f..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display a manual installation notification 1`] = `
-<NavBarNotif
-  className="alert alert-danger"
->
-  marketplace.edition_status_x.MANUAL_IN_PROGRESS.Foo
-  <a
-    className="spacer-left"
-    href="https://redirect.sonarsource.com/doc/how-to-install-an-edition.html"
-    target="_blank"
-  >
-    marketplace.how_to_install
-  </a>
-  <a
-    className="button spacer-left"
-    download="sonarqube-Foo.zip"
-    href="download_url"
-    target="_blank"
-  >
-    marketplace.download_package
-  </a>
-</NavBarNotif>
-`;
-
-exports[`should display a ready notification 1`] = `
-<NavBarNotif
-  className="alert alert-success"
->
-  <span>
-    marketplace.edition_status.AUTOMATIC_READY
-  </span>
-  <Button
-    className="js-restart spacer-left"
-    onClick={[Function]}
-  >
-    marketplace.restart
-  </Button>
-</NavBarNotif>
-`;
-
-exports[`should display an in progress notif 1`] = `
-<NavBarNotif
-  className="alert alert-info"
->
-  <i
-    className="spinner spacer-right"
-  />
-  <span>
-    marketplace.edition_status.AUTOMATIC_IN_PROGRESS
-  </span>
-</NavBarNotif>
-`;
-
-exports[`should display install errors 1`] = `
-<NavBarNotif
-  className="alert alert-danger"
-  onCancel={[Function]}
->
-  Foo error
-</NavBarNotif>
-`;
index 29a88938c4b4cfa030b60121e89bf58d0c4e23e7..56d25c693cfa77324e397d7e1105d304380833ff 100644 (file)
@@ -1,10 +1,29 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`should display a pending plugin notif 1`] = `
+<PendingPluginsActionNotif
+  pending={
+    Object {
+      "installing": Array [
+        Object {
+          "implementationBuild": "1",
+          "key": "foo",
+          "name": "Foo",
+          "version": "1.0",
+        },
+      ],
+      "removing": Array [],
+      "updating": Array [],
+    }
+  }
+  refreshPending={[Function]}
+/>
+`;
+
 exports[`should work with extensions 1`] = `
 <ContextNavBar
   height={72}
   id="context-navigation"
-  notif={null}
 >
   <header
     className="navbar-context-header"
index c8a9efd700c2531fe77febfbbcf899c99f995241..6cbd7fb0896a09a259539d4962c016b147ff1dff 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-.alert-container .alert {
-  margin-bottom: 0px;
-  padding: 0 8px;
-  line-height: 38px;
-}
-
 .alert {
   display: block;
   margin-bottom: 8px;
index 995a9b15aa780c797a0d759f228ba925290cde92..ed1178b283aa68a0c2e221f09e87395e2f9eb0e7 100644 (file)
@@ -27,7 +27,6 @@ import DateFromNow from '../../components/intl/DateFromNow';
 import DateFormatter from '../../components/intl/DateFormatter';
 import DateTimeFormatter from '../../components/intl/DateTimeFormatter';
 import FavoriteContainer from '../../components/controls/FavoriteContainer';
-import LicenseEditionSet from '../../apps/marketplace/components/LicenseEditionSet';
 import HomePageSelect from '../../components/controls/HomePageSelect';
 import ListFooter from '../../components/controls/ListFooter';
 import Modal from '../../components/controls/Modal';
@@ -82,7 +81,6 @@ const exposeLibraries = () => {
     HelpTooltip,
     HomePageSelect,
     Level,
-    LicenseEditionSet,
     ListFooter,
     LockIcon,
     Modal,
index 8b151058a493ae073b41ea34a8af4730bdeb60c0..966ab400de039bf390f02c4371ab9f06ca8e8553 100644 (file)
@@ -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<Props, State> {
     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<Props, State> {
   };
 
   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<Props, State> {
         <Suggestions suggestions="marketplace" />
         <Helmet title={translate('marketplace.page')} />
         <Header />
-        <EditionBoxes
-          canInstall={standaloneMode && !this.props.editionsReadOnly}
-          canUninstall={standaloneMode}
-          editionStatus={editionStatus}
-          editions={editions}
-          loading={this.props.loadingEditions}
-          updateCenterActive={this.props.updateCenterActive}
-          updateEditionStatus={this.props.setEditionStatus}
-        />
+        <EditionBoxes currentEdition={currentEdition} />
         <Search
           query={query}
           updateCenterActive={this.props.updateCenterActive}
index 73611432c0b0e67106be3005f0bb6ad2a259761c..eabc536fda5da1044903773d215aaa8f8c759361 100644 (file)
@@ -22,13 +22,10 @@ import App from './App';
 import {
   getAppState,
   getGlobalSettingValue,
-  getMarketplaceState,
-  getMarketplaceEditions,
-  getMarketplaceEditionStatus,
+  getMarketplaceCurrentEdition,
   getMarketplacePendingPlugins
 } from '../../store/rootReducer';
-import { Edition, EditionStatus } from '../../api/marketplace';
-import { setEditionStatus, fetchPendingPlugins } from '../../store/marketplace/actions';
+import { fetchPendingPlugins } from '../../store/marketplace/actions';
 import { RawQuery } from '../../helpers/query';
 import { PluginPendingResult } from '../../api/plugins';
 
@@ -37,32 +34,27 @@ interface OwnProps {
 }
 
 interface StateToProps {
-  editions?: Edition[];
-  editionsReadOnly: boolean;
-  editionStatus?: EditionStatus;
-  loadingEditions: boolean;
+  currentEdition?: string;
   pendingPlugins: PluginPendingResult;
   standaloneMode: boolean;
   updateCenterActive: boolean;
 }
 
 interface DispatchToProps {
-  setEditionStatus: (editionStatus: EditionStatus) => 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<StateToProps, DispatchToProps, OwnProps>(
   mapStateToProps,
index d169e5b31a483c2bae6a23b00840fa9ab480cdd7..0b762d28f7ef4f8ef595e10a33fce9dd7fd1878d 100644 (file)
  * 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<Props, State> {
-  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 (
-        <LicenseEditionForm
-          edition={installEdition}
-          editions={sortedEditions}
-          isDowngrade={
-            installedIdx !== undefined &&
-            installEditionIdx !== undefined &&
-            installEditionIdx < installedIdx
-          }
-          onClose={this.handleCloseLicenseForm}
-          updateEditionStatus={this.props.updateEditionStatus}
-        />
-      );
-    }
-
-    if (canUninstall && openUninstallForm && editionStatus && editionStatus.currentEditionKey) {
-      return (
-        <UninstallEditionForm
-          edition={sortedEditions.find(edition => 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 <i className="big-spacer-bottom spinner" />;
-    }
-
-    if (!editions) {
-      return (
-        <div className="spacer-bottom marketplace-editions">
-          <span className="alert alert-info">
-            <FormattedMessage
-              defaultMessage={translate('marketplace.editions_unavailable')}
-              id="marketplace.editions_unavailable"
-              values={{
-                url: (
-                  <a href="https://redirect.sonarsource.com/editions/editions.html" target="_blank">
-                    SonarSource.com
-                  </a>
-                )
-              }}
-            />
-          </span>
-        </div>
-      );
-    }
+  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 (
       <div className="spacer-bottom marketplace-editions">
-        <EditionBox
-          actionLabel={translate('marketplace.downgrade')}
-          disableAction={inProgressStatus}
-          displayAction={canUninstall && currentIdx > 0}
-          edition={sortedEditions[0]}
-          editionStatus={status}
-          key={sortedEditions[0].key}
-          onAction={this.handleOpenUninstallForm}
-        />
-        {sortedEditions
-          .slice(1)
-          .map((edition, idx) => (
-            <EditionBox
-              actionLabel={
-                currentIdx > 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 => (
+          <EditionBox
+            currentEdition={currentEdition || 'community'}
+            edition={edition}
+            key={edition.key}
+            ncloc={ncloc}
+            serverId={serverId}
+          />
+        ))}
       </div>
     );
   }
index 6e153cd084d19a8bef0884eb661f1710e406f415..dd49bb10079b3922d9614b3f5b3eb70ea996c428 100644 (file)
 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(
-    <EditionBoxes
-      canInstall={true}
-      canUninstall={true}
-      loading={false}
-      editionStatus={DEFAULT_STATUS}
-      updateCenterActive={true}
-      updateEditionStatus={jest.fn()}
-      {...props}
-    />
-  );
+  return shallow(<EditionBoxes currentEdition={undefined} {...props} />);
 }
index 85ba8946957a6c404f3f633f2b2f26ab5f9faa3e..687a2cde364bf4bed371032f95fc9c9a8e2155c7 100644 (file)
@@ -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`] = `
 <div
   className="spacer-bottom marketplace-editions"
 >
-  <span
-    className="alert alert-info"
-  >
-    <FormattedMessage
-      defaultMessage="marketplace.editions_unavailable"
-      id="marketplace.editions_unavailable"
-      values={
-        Object {
-          "url": <a
-            href="https://redirect.sonarsource.com/editions/editions.html"
-            target="_blank"
-          >
-            SonarSource.com
-          </a>,
-        }
+  <EditionBox
+    currentEdition="developer"
+    edition={
+      Object {
+        "homeUrl": "more_url",
+        "key": "comunity",
       }
-    />
-  </span>
+    }
+    key="comunity"
+  />
+  <EditionBox
+    currentEdition="developer"
+    edition={
+      Object {
+        "downloadUrl": "download_url",
+        "homeUrl": "more_url",
+        "key": "developer",
+      }
+    }
+    key="developer"
+  />
 </div>
 `;
 
 exports[`should display the edition boxes correctly 1`] = `
-<i
-  className="big-spacer-bottom spinner"
-/>
-`;
-
-exports[`should display the edition boxes correctly 2`] = `
 <div
   className="spacer-bottom marketplace-editions"
 >
   <EditionBox
-    actionLabel="marketplace.downgrade"
-    disableAction={false}
-    displayAction={true}
+    currentEdition="community"
     edition={
       Object {
-        "downloadUrl": "download_url",
         "homeUrl": "more_url",
         "key": "comunity",
-        "licenseRequestUrl": "license_url",
-        "name": "Comunity Edition",
-        "textDescription": "bar",
-      }
-    }
-    editionStatus={
-      Object {
-        "currentEditionKey": "developer",
-        "installationStatus": "NONE",
-        "nextEditionKey": "",
       }
     }
     key="comunity"
-    onAction={[Function]}
   />
   <EditionBox
-    actionLabel="marketplace.upgrade"
-    disableAction={false}
-    displayAction={false}
+    currentEdition="community"
     edition={
       Object {
         "downloadUrl": "download_url",
         "homeUrl": "more_url",
         "key": "developer",
-        "licenseRequestUrl": "license_url",
-        "name": "Developer Edition",
-        "textDescription": "foo",
-      }
-    }
-    editionStatus={
-      Object {
-        "currentEditionKey": "developer",
-        "installationStatus": "NONE",
-        "nextEditionKey": "",
       }
     }
     key="developer"
-    onAction={[Function]}
   />
 </div>
 `;
index 0ad689bc93d85832cfc4ab69bf0b051eb1fa7fb6..968aa5660209e5ec9f3836e8b3ba5e814d65ec71 100644 (file)
  * 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<Props> {
-  handleAction = () => {
-    this.props.onAction(this.props.edition);
-  };
-
-  render() {
-    const { disableAction, displayAction, edition, editionStatus } = this.props;
-    return (
-      <div className="boxed-group boxed-group-inner marketplace-edition">
-        {editionStatus && <EditionBoxBadge editionKey={edition.key} status={editionStatus} />}
-        <div>
-          <h3 className="spacer-bottom">{edition.name}</h3>
-          <p>{edition.textDescription}</p>
-        </div>
-        <div className="marketplace-edition-action spacer-top">
-          <a href={edition.homeUrl} target="_blank">
-            {translate('marketplace.learn_more')}
-          </a>
-          {displayAction && (
-            <Button disabled={disableAction} onClick={this.handleAction}>
-              {this.props.actionLabel}
-            </Button>
-          )}
-        </div>
+export default function EditionBox({ currentEdition, edition, ncloc, serverId }: Props) {
+  const isInstalled = currentEdition === edition.key;
+  const url = getEditionUrl(edition, { ncloc, serverId, sourceEdition: currentEdition });
+  return (
+    <div className="boxed-group boxed-group-inner marketplace-edition">
+      {isInstalled && (
+        <span className="marketplace-edition-badge badge badge-normal-size display-flex-center">
+          <CheckIcon className="little-spacer-right" size={14} />
+          {translate('marketplace.installed')}
+        </span>
+      )}
+      <div>
+        <DocInclude path={'/tooltips/editions/' + edition.key} />
+      </div>
+      <div className="marketplace-edition-action spacer-top">
+        <a href={url} target="_blank">
+          {translate('marketplace.learn_more')}
+        </a>
       </div>
-    );
-  }
+    </div>
+  );
 }
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 (file)
index 341bd2e..0000000
+++ /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 (
-        <span className="marketplace-edition-badge badge badge-normal-size">
-          {status.installationStatus === 'AUTOMATIC_IN_PROGRESS'
-            ? translate('marketplace.installing')
-            : translate('marketplace.pending')}
-        </span>
-      );
-    }
-  } else if (isInstalled) {
-    return (
-      <span className="marketplace-edition-badge badge badge-normal-size display-flex-center">
-        <CheckIcon size={14} className="little-spacer-right" />
-        {translate('marketplace.installed')}
-      </span>
-    );
-  }
-
-  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 (file)
index a4c4828..0000000
+++ /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<Props, State> {
-  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 (
-      <Modal contentLabel={header} onRequestClose={this.props.onClose}>
-        <header className="modal-head">
-          <h2>{header}</h2>
-        </header>
-
-        <LicenseEditionSet
-          className="modal-body"
-          edition={edition}
-          editions={this.props.editions}
-          updateLicense={this.handleLicenseChange}
-        />
-
-        <footer className="modal-foot">
-          {submitting && <i className="spinner spacer-right" />}
-          {status && (
-            <Button
-              className="js-confirm"
-              disabled={!license || submitting}
-              onClick={this.handleConfirmClick}>
-              {status === 'AUTOMATIC_INSTALL'
-                ? translate('marketplace.install')
-                : translate('save')}
-            </Button>
-          )}
-          <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
-        </footer>
-      </Modal>
-    );
-  }
-}
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 (file)
index b8099a6..0000000
+++ /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<Props, State> {
-  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<HTMLTextAreaElement>) => {
-    const license = event.currentTarget.value;
-    if (license) {
-      this.fetchLicensePreview(license);
-      this.setState({ license });
-    } else {
-      this.resetLicense({});
-    }
-  };
-
-  handleTermsCheck = (checked: boolean) => {
-    this.setLicense({ acceptTerms: checked });
-  };
-
-  resetLicense<K extends keyof State>(state: Pick<State, K>) {
-    this.setLicense(
-      Object.assign(
-        {
-          license: '',
-          licenseEdition: undefined,
-          previewStatus: undefined,
-          wrongEdition: false
-        },
-        state
-      )
-    );
-  }
-
-  setLicense<K extends keyof State>(state: Pick<State, K>) {
-    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 (
-        <div className="spacer-top">
-          {wrongEdition && (
-            <p className="alert alert-danger">
-              {edition
-                ? translateWithParameters('marketplace.wrong_license_type_x', edition.name)
-                : translate('marketplace.wrong_license_type')}
-            </p>
-          )}
-          {edition && (
-            <a href={this.getLicenseFormUrl(edition)} target="_blank">
-              {translate('marketplace.i_need_a_license')}
-            </a>
-          )}
-        </div>
-      );
-    }
-
-    return (
-      <div className="spacer-top">
-        <p
-          className={classNames('alert', {
-            'alert-warning': previewStatus === 'AUTOMATIC_INSTALL',
-            'alert-success': previewStatus === 'NO_INSTALL',
-            'alert-danger': previewStatus === 'MANUAL_INSTALL'
-          })}>
-          {translateWithParameters(
-            'marketplace.license_preview_status.' + previewStatus,
-            licenseEdition ? licenseEdition.name : translate('marketplace.commercial_edition')
-          )}
-          {licenseEdition &&
-            licenseEdition.key === 'datacenter' &&
-            previewStatus !== 'NO_INSTALL' && (
-              <span className="little-spacer-left">
-                <FormattedMessage
-                  defaultMessage={translate('marketplace.how_to_setup_cluster_url')}
-                  id="marketplace.how_to_setup_cluster_url"
-                  values={{
-                    url: (
-                      <a
-                        href="https://redirect.sonarsource.com/doc/data-center-edition.html"
-                        rel="noopener noreferrer"
-                        target="_blank">
-                        {licenseEdition.name}
-                      </a>
-                    )
-                  }}
-                />
-              </span>
-            )}
-        </p>
-        {previewStatus !== 'NO_INSTALL' && (
-          <span className="js-edition-tos">
-            <Checkbox
-              checked={this.state.acceptTerms}
-              id="edition-terms"
-              onCheck={this.handleTermsCheck}>
-              <label className="little-spacer-left" htmlFor="edition-terms">
-                {translate('marketplace.i_accept_the')}
-              </label>
-            </Checkbox>
-            <a
-              className="nowrap little-spacer-left"
-              href="http://dist.sonarsource.com/SonarSource_Terms_And_Conditions.pdf"
-              rel="noopener noreferrer"
-              target="_blank">
-              {translate('marketplace.terms_and_conditions')}
-            </a>
-          </span>
-        )}
-      </div>
-    );
-  }
-
-  render() {
-    const { className, edition } = this.props;
-    const { license, loading } = this.state;
-
-    return (
-      <div className={className}>
-        {edition && (
-          <label className="display-inline-block spacer-bottom" htmlFor="set-license">
-            {translateWithParameters('marketplace.enter_license_for_x', edition.name)}
-            <em className="mandatory">*</em>
-          </label>
-        )}
-        <textarea
-          autoFocus={true}
-          className="display-block input-super-large"
-          id="set-license"
-          onChange={this.handleLicenseChange}
-          required={true}
-          rows={8}
-          style={{ resize: 'none' }}
-          value={license}
-        />
-
-        <DeferredSpinner
-          customSpinner={
-            <p className="spacer-top">
-              <i className="spinner spacer-right text-bottom" />
-              {translate('marketplace.checking_license')}
-            </p>
-          }
-          loading={loading}>
-          {this.renderAlert()}
-        </DeferredSpinner>
-      </div>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx
deleted file mode 100644 (file)
index 1b9be29..0000000
+++ /dev/null
@@ -1,94 +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 { Edition, EditionStatus, uninstallEdition } 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;
-  editionStatus: EditionStatus;
-  onClose: () => void;
-  updateEditionStatus: (editionStatus: EditionStatus) => void;
-}
-
-interface State {
-  loading: boolean;
-}
-
-export default class UninstallEditionForm extends React.PureComponent<Props, State> {
-  mounted = false;
-  state: State = { loading: false };
-
-  componentDidMount() {
-    this.mounted = true;
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  handleConfirmClick = () => {
-    this.setState({ loading: true });
-    uninstallEdition()
-      .then(() => {
-        this.props.updateEditionStatus({
-          ...this.props.editionStatus,
-          currentEditionKey: undefined,
-          installationStatus: 'UNINSTALL_IN_PROGRESS'
-        });
-        this.props.onClose();
-      })
-      .catch(() => {
-        if (this.mounted) {
-          this.setState({ loading: false });
-        }
-      });
-  };
-
-  render() {
-    const { edition } = this.props;
-    const { loading } = this.state;
-    const currentEdition = edition ? edition.name : translate('marketplace.commercial_edition');
-    const header = translateWithParameters('marketplace.downgrade_to_community_edition');
-    return (
-      <Modal contentLabel={header} onRequestClose={this.props.onClose}>
-        <header className="modal-head">
-          <h2>{header}</h2>
-        </header>
-
-        <div className="modal-body">
-          <p>{translateWithParameters('marketplace.uninstall_x_confirmation', currentEdition)}</p>
-        </div>
-
-        <footer className="modal-foot">
-          {loading && <i className="spinner spacer-right" />}
-          <Button disabled={loading} onClick={this.handleConfirmClick}>
-            {translate('marketplace.downgrade')}
-          </Button>
-          <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
-            {translate('cancel')}
-          </ResetButtonLink>
-        </footer>
-      </Modal>
-    );
-  }
-}
index c217058bb4f6d97337a58dea0c413828a1f5c302..ec0303053a47933ed901432801cd6fa71f4bd5da 100644 (file)
  */
 import * as React from 'react';
 import { shallow } from 'enzyme';
-import { Edition, EditionStatus } from '../../../../api/marketplace';
 import EditionBox from '../EditionBox';
 
-const DEFAULT_STATUS: EditionStatus = {
-  currentEditionKey: '',
-  nextEditionKey: '',
-  installationStatus: 'NONE'
-};
-
-const DEFAULT_EDITION: Edition = {
+const DEFAULT_EDITION = {
   key: 'foo',
-  name: 'Foo',
-  textDescription: 'Foo desc',
   downloadUrl: 'download_url',
-  homeUrl: 'more_url',
-  licenseRequestUrl: 'license_url'
+  homeUrl: 'more_url'
 };
 
 it('should display the edition', () => {
-  expect(getWrapper()).toMatchSnapshot();
-});
-
-it('should disable action button', () => {
-  expect(getWrapper({ disableAction: true })).toMatchSnapshot();
+  expect(getWrapper({ ncloc: 1000, serverId: 'serverId' })).toMatchSnapshot();
 });
 
-it('should correctly hide action buttons', () => {
-  expect(getWrapper({ displayAction: false })).toMatchSnapshot();
+it('should show insalled badge', () => {
+  expect(getWrapper({ currentEdition: 'foo' })).toMatchSnapshot();
 });
 
 function getWrapper(props = {}) {
-  return shallow(
-    <EditionBox
-      actionLabel="action"
-      disableAction={false}
-      displayAction={true}
-      edition={DEFAULT_EDITION}
-      editionStatus={DEFAULT_STATUS}
-      onAction={jest.fn()}
-      {...props}
-    />
-  );
+  return shallow(<EditionBox currentEdition="community" edition={DEFAULT_EDITION} {...props} />);
 }
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBoxBadge-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBoxBadge-test.tsx
deleted file mode 100644 (file)
index dfe79a8..0000000
+++ /dev/null
@@ -1,81 +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 { shallow } from 'enzyme';
-import { EditionStatus } from '../../../../api/marketplace';
-import EditionBoxBadge from '../EditionBoxBadge';
-
-const DEFAULT_STATUS: EditionStatus = {
-  currentEditionKey: '',
-  nextEditionKey: '',
-  installationStatus: 'NONE'
-};
-
-it('should display installed badge', () => {
-  expect(
-    getWrapper({
-      status: {
-        currentEditionKey: 'foo',
-        nextEditionKey: '',
-        installationStatus: 'NONE'
-      }
-    })
-  ).toMatchSnapshot();
-});
-
-it('should display installing badge', () => {
-  expect(
-    getWrapper({
-      status: {
-        currentEditionKey: 'foo',
-        nextEditionKey: 'foo',
-        installationStatus: 'AUTOMATIC_IN_PROGRESS'
-      }
-    })
-  ).toMatchSnapshot();
-});
-
-it('should display pending badge', () => {
-  expect(
-    getWrapper({
-      status: {
-        currentEditionKey: '',
-        nextEditionKey: 'foo',
-        installationStatus: 'AUTOMATIC_READY'
-      }
-    })
-  ).toMatchSnapshot();
-});
-
-it('should not display a badge', () => {
-  expect(
-    getWrapper({
-      status: {
-        currentEditionKey: '',
-        nextEditionKey: 'bar',
-        installationStatus: 'AUTOMATIC_READY'
-      }
-    }).type()
-  ).toBeNull();
-});
-
-function getWrapper(props = {}) {
-  return shallow(<EditionBoxBadge editionKey="foo" status={DEFAULT_STATUS} {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx
deleted file mode 100644 (file)
index 9814570..0000000
+++ /dev/null
@@ -1,98 +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 { shallow } from 'enzyme';
-import { click } from '../../../../helpers/testUtils';
-import LicenseEditionForm from '../LicenseEditionForm';
-
-jest.mock('../../../../api/marketplace', () => ({
-  applyLicense: jest.fn(() =>
-    Promise.resolve({ nextEditionKey: 'foo', installationStatus: 'AUTOMATIC_IN_PROGRESS' })
-  )
-}));
-
-const applyLicense = require('../../../../api/marketplace').applyLicense as jest.Mock<any>;
-
-const DEFAULT_EDITION = {
-  key: 'foo',
-  name: 'Foo',
-  textDescription: 'Foo desc',
-  downloadUrl: 'download_url',
-  homeUrl: 'more_url',
-  licenseRequestUrl: 'license_url'
-};
-
-beforeEach(() => {
-  applyLicense.mockClear();
-});
-
-it('should display correctly', () => {
-  expect(getWrapper()).toMatchSnapshot();
-});
-
-it('should correctly change the button based on the status and license', () => {
-  const wrapper = getWrapper();
-  let button;
-  (wrapper.instance() as LicenseEditionForm).mounted = true;
-
-  wrapper.setState({ license: 'mylicense', status: 'NO_INSTALL' });
-  button = wrapper.find('Button');
-  expect(button).toMatchSnapshot();
-
-  wrapper.setState({ license: undefined, status: 'MANUAL_INSTALL' });
-  button = wrapper.find('Button');
-  expect(button).toMatchSnapshot();
-
-  wrapper.setState({ status: 'AUTOMATIC_INSTALL' });
-  button = wrapper.find('Button');
-  expect(button).toMatchSnapshot();
-
-  wrapper.setState({ license: 'mylicense' });
-  expect(wrapper.find('Button').prop('disabled')).toBeFalsy();
-});
-
-it('should update the edition status after install', async () => {
-  const updateEditionStatus = jest.fn();
-  const wrapper = getWrapper({ updateEditionStatus });
-  const form = wrapper.instance() as LicenseEditionForm;
-  form.handleLicenseChange('mylicense', 'AUTOMATIC_INSTALL');
-  wrapper.update();
-  click(wrapper.find('Button'));
-  expect(applyLicense).toHaveBeenCalledWith({ license: 'mylicense' });
-  await new Promise(setImmediate);
-  expect(updateEditionStatus).toHaveBeenCalledWith({
-    nextEditionKey: 'foo',
-    installationStatus: 'AUTOMATIC_IN_PROGRESS'
-  });
-});
-
-function getWrapper(props = {}) {
-  return shallow(
-    <LicenseEditionForm
-      edition={DEFAULT_EDITION}
-      editions={[DEFAULT_EDITION]}
-      isDowngrade={false}
-      onClose={jest.fn()}
-      updateEditionStatus={jest.fn()}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx
deleted file mode 100644 (file)
index 691f9c3..0000000
+++ /dev/null
@@ -1,126 +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 { shallow } from 'enzyme';
-import { change, waitAndUpdate } from '../../../../helpers/testUtils';
-import LicenseEditionSet from '../LicenseEditionSet';
-
-jest.mock('../../../../api/marketplace', () => ({
-  getLicensePreview: jest.fn(() =>
-    Promise.resolve({ nextEditionKey: 'foo', previewStatus: 'NO_INSTALL' })
-  ),
-  getFormData: jest.fn(() => Promise.resolve({ serverId: 'foo', ncloc: 1000 }))
-}));
-
-jest.mock('lodash', () => {
-  const lodash = require.requireActual('lodash');
-  lodash.debounce = (fn: Function) => (...args: any[]) => fn(...args);
-  return lodash;
-});
-
-const getLicensePreview = require('../../../../api/marketplace').getLicensePreview as jest.Mock<
-  any
->;
-
-const DEFAULT_EDITION = {
-  key: 'foo',
-  name: 'Foo',
-  textDescription: 'Foo desc',
-  downloadUrl: 'download_url',
-  homeUrl: 'more_url',
-  licenseRequestUrl: 'license_url'
-};
-
-beforeEach(() => {
-  getLicensePreview.mockClear();
-});
-
-it('should display correctly', () => {
-  expect(getWrapper()).toMatchSnapshot();
-});
-
-it('should display the get license link with parameters', async () => {
-  const wrapper = getWrapper();
-  await waitAndUpdate(wrapper);
-  expect(wrapper.find('a')).toMatchSnapshot();
-});
-
-it('should correctly display status message after checking license', async () => {
-  let wrapper = await testLicenseStatus('NO_INSTALL');
-  expect(wrapper.find('p.alert')).toMatchSnapshot();
-  wrapper = await testLicenseStatus('AUTOMATIC_INSTALL');
-  expect(wrapper.find('p.alert')).toMatchSnapshot();
-  wrapper = await testLicenseStatus('MANUAL_INSTALL');
-  expect(wrapper.find('p.alert')).toMatchSnapshot();
-  wrapper = await testLicenseStatus('AUTOMATIC_INSTALL', jest.fn(), 'bar');
-  expect(wrapper.find('p.alert')).toMatchSnapshot();
-  wrapper = await testLicenseStatus('AUTOMATIC_INSTALL', jest.fn(), 'bar', { edition: undefined });
-  expect(wrapper.find('p.alert')).toMatchSnapshot();
-});
-
-it('should display terms of license checkbox', async () => {
-  let updateLicense = jest.fn();
-  let wrapper = await testLicenseStatus('NO_INSTALL', updateLicense);
-  expect(wrapper.find('.js-edition-tos').exists()).toBeFalsy();
-  expect(updateLicense).toHaveBeenCalledWith('mylicense', 'NO_INSTALL');
-
-  updateLicense = jest.fn();
-  wrapper = await testLicenseStatus('AUTOMATIC_INSTALL', updateLicense);
-  const tosCheckbox = wrapper.find('.js-edition-tos');
-  expect(tosCheckbox.find('a').exists()).toBeTruthy();
-  expect(updateLicense).toHaveBeenLastCalledWith(undefined, 'AUTOMATIC_INSTALL');
-  (tosCheckbox.find('Checkbox').prop('onCheck') as Function)(true);
-  expect(updateLicense).toHaveBeenLastCalledWith('mylicense', 'AUTOMATIC_INSTALL');
-
-  updateLicense = jest.fn();
-  wrapper = await testLicenseStatus('MANUAL_INSTALL', updateLicense);
-  expect(wrapper.find('.js-edition-tos').exists()).toBeTruthy();
-  expect(updateLicense).toHaveBeenLastCalledWith(undefined, 'MANUAL_INSTALL');
-});
-
-function getWrapper(props = {}) {
-  return shallow(
-    <LicenseEditionSet
-      edition={DEFAULT_EDITION}
-      editions={[DEFAULT_EDITION]}
-      updateLicense={jest.fn()}
-      {...props}
-    />
-  );
-}
-
-async function testLicenseStatus(
-  status: string,
-  updateLicense = jest.fn(),
-  nextEditionKey = 'foo',
-  props = {}
-) {
-  getLicensePreview.mockImplementation(() =>
-    Promise.resolve({ nextEditionKey, previewStatus: status })
-  );
-  const wrapper = getWrapper({ updateLicense, ...props });
-  change(wrapper.find('textarea'), 'mylicense');
-  expect(getLicensePreview).toHaveBeenCalled();
-  await new Promise(setImmediate);
-  expect(updateLicense).toHaveBeenCalled();
-  wrapper.update();
-  return wrapper;
-}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/UninstallEditionForm-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/UninstallEditionForm-test.tsx
deleted file mode 100644 (file)
index f8dbede..0000000
+++ /dev/null
@@ -1,72 +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 { shallow } from 'enzyme';
-import { click } from '../../../../helpers/testUtils';
-import UninstallEditionForm from '../UninstallEditionForm';
-
-jest.mock('../../../../api/marketplace', () => ({
-  uninstallEdition: jest.fn(() => Promise.resolve())
-}));
-
-const uninstallEdition = require('../../../../api/marketplace').uninstallEdition as jest.Mock<any>;
-
-const DEFAULT_EDITION = {
-  key: 'foo',
-  name: 'Foo',
-  textDescription: 'Foo desc',
-  downloadUrl: 'download_url',
-  homeUrl: 'more_url',
-  licenseRequestUrl: 'license_url'
-};
-
-beforeEach(() => {
-  uninstallEdition.mockClear();
-});
-
-it('should display correctly', () => {
-  expect(getWrapper()).toMatchSnapshot();
-});
-
-it('should update the edition status after uninstall', async () => {
-  const updateEditionStatus = jest.fn();
-  const wrapper = getWrapper({ updateEditionStatus });
-  (wrapper.instance() as UninstallEditionForm).mounted = true;
-  click(wrapper.find('Button'));
-  expect(uninstallEdition).toHaveBeenCalled();
-  await new Promise(setImmediate);
-  expect(updateEditionStatus).toHaveBeenCalledWith({
-    currentEditionKey: undefined,
-    installationStatus: 'UNINSTALL_IN_PROGRESS'
-  });
-});
-
-function getWrapper(props = {}) {
-  return shallow(
-    <UninstallEditionForm
-      edition={DEFAULT_EDITION}
-      editionStatus={{ currentEditionKey: 'foo', installationStatus: 'NONE' }}
-      onClose={jest.fn()}
-      updateEditionStatus={jest.fn()}
-      {...props}
-    />
-  );
-}
index b9925cec2c1b50ab64df568ac88195e0231edd04..39c71155bc692cd59b5d4f3ce81ca89d6e0050c2 100644 (file)
@@ -1,34 +1,19 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`should correctly hide action buttons 1`] = `
+exports[`should display the edition 1`] = `
 <div
   className="boxed-group boxed-group-inner marketplace-edition"
 >
-  <EditionBoxBadge
-    editionKey="foo"
-    status={
-      Object {
-        "currentEditionKey": "",
-        "installationStatus": "NONE",
-        "nextEditionKey": "",
-      }
-    }
-  />
   <div>
-    <h3
-      className="spacer-bottom"
-    >
-      Foo
-    </h3>
-    <p>
-      Foo desc
-    </p>
+    <DocInclude
+      path="/tooltips/editions/foo"
+    />
   </div>
   <div
     className="marketplace-edition-action spacer-top"
   >
     <a
-      href="more_url"
+      href="more_url?ncloc=1000&serverId=serverId&sourceEdition=community"
       target="_blank"
     >
       marketplace.learn_more
@@ -37,88 +22,33 @@ exports[`should correctly hide action buttons 1`] = `
 </div>
 `;
 
-exports[`should disable action button 1`] = `
+exports[`should show insalled badge 1`] = `
 <div
   className="boxed-group boxed-group-inner marketplace-edition"
 >
-  <EditionBoxBadge
-    editionKey="foo"
-    status={
-      Object {
-        "currentEditionKey": "",
-        "installationStatus": "NONE",
-        "nextEditionKey": "",
-      }
-    }
-  />
-  <div>
-    <h3
-      className="spacer-bottom"
-    >
-      Foo
-    </h3>
-    <p>
-      Foo desc
-    </p>
-  </div>
-  <div
-    className="marketplace-edition-action spacer-top"
+  <span
+    className="marketplace-edition-badge badge badge-normal-size display-flex-center"
   >
-    <a
-      href="more_url"
-      target="_blank"
-    >
-      marketplace.learn_more
-    </a>
-    <Button
-      disabled={true}
-      onClick={[Function]}
-    >
-      action
-    </Button>
-  </div>
-</div>
-`;
-
-exports[`should display the edition 1`] = `
-<div
-  className="boxed-group boxed-group-inner marketplace-edition"
->
-  <EditionBoxBadge
-    editionKey="foo"
-    status={
-      Object {
-        "currentEditionKey": "",
-        "installationStatus": "NONE",
-        "nextEditionKey": "",
-      }
-    }
-  />
+    <CheckIcon
+      className="little-spacer-right"
+      size={14}
+    />
+    marketplace.installed
+  </span>
   <div>
-    <h3
-      className="spacer-bottom"
-    >
-      Foo
-    </h3>
-    <p>
-      Foo desc
-    </p>
+    <DocInclude
+      path="/tooltips/editions/foo"
+    />
   </div>
   <div
     className="marketplace-edition-action spacer-top"
   >
     <a
-      href="more_url"
+      href="more_url?sourceEdition=foo"
       target="_blank"
     >
       marketplace.learn_more
     </a>
-    <Button
-      disabled={false}
-      onClick={[Function]}
-    >
-      action
-    </Button>
   </div>
 </div>
 `;
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBoxBadge-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBoxBadge-test.tsx.snap
deleted file mode 100644 (file)
index 733b006..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display installed badge 1`] = `
-<span
-  className="marketplace-edition-badge badge badge-normal-size display-flex-center"
->
-  <CheckIcon
-    className="little-spacer-right"
-    size={14}
-  />
-  marketplace.installed
-</span>
-`;
-
-exports[`should display installing badge 1`] = `
-<span
-  className="marketplace-edition-badge badge badge-normal-size"
->
-  marketplace.installing
-</span>
-`;
-
-exports[`should display pending badge 1`] = `
-<span
-  className="marketplace-edition-badge badge badge-normal-size"
->
-  marketplace.pending
-</span>
-`;
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap
deleted file mode 100644 (file)
index c0dcce3..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should correctly change the button based on the status and license 1`] = `
-<Button
-  className="js-confirm"
-  disabled={false}
-  onClick={[Function]}
->
-  save
-</Button>
-`;
-
-exports[`should correctly change the button based on the status and license 2`] = `
-<Button
-  className="js-confirm"
-  disabled={true}
-  onClick={[Function]}
->
-  save
-</Button>
-`;
-
-exports[`should correctly change the button based on the status and license 3`] = `
-<Button
-  className="js-confirm"
-  disabled={true}
-  onClick={[Function]}
->
-  marketplace.install
-</Button>
-`;
-
-exports[`should display correctly 1`] = `
-<Modal
-  contentLabel="marketplace.upgrade_to_x.Foo"
-  onRequestClose={[MockFunction]}
->
-  <header
-    className="modal-head"
-  >
-    <h2>
-      marketplace.upgrade_to_x.Foo
-    </h2>
-  </header>
-  <LicenseEditionSet
-    className="modal-body"
-    edition={
-      Object {
-        "downloadUrl": "download_url",
-        "homeUrl": "more_url",
-        "key": "foo",
-        "licenseRequestUrl": "license_url",
-        "name": "Foo",
-        "textDescription": "Foo desc",
-      }
-    }
-    editions={
-      Array [
-        Object {
-          "downloadUrl": "download_url",
-          "homeUrl": "more_url",
-          "key": "foo",
-          "licenseRequestUrl": "license_url",
-          "name": "Foo",
-          "textDescription": "Foo desc",
-        },
-      ]
-    }
-    updateLicense={[Function]}
-  />
-  <footer
-    className="modal-foot"
-  >
-    <ResetButtonLink
-      onClick={[MockFunction]}
-    >
-      cancel
-    </ResetButtonLink>
-  </footer>
-</Modal>
-`;
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap
deleted file mode 100644 (file)
index 5ff0d2f..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should correctly display status message after checking license 1`] = `
-<p
-  className="alert alert-success"
->
-  marketplace.license_preview_status.NO_INSTALL.Foo
-</p>
-`;
-
-exports[`should correctly display status message after checking license 2`] = `
-<p
-  className="alert alert-warning"
->
-  marketplace.license_preview_status.AUTOMATIC_INSTALL.Foo
-</p>
-`;
-
-exports[`should correctly display status message after checking license 3`] = `
-<p
-  className="alert alert-danger"
->
-  marketplace.license_preview_status.MANUAL_INSTALL.Foo
-</p>
-`;
-
-exports[`should correctly display status message after checking license 4`] = `
-<p
-  className="alert alert-danger"
->
-  marketplace.wrong_license_type_x.Foo
-</p>
-`;
-
-exports[`should correctly display status message after checking license 5`] = `
-<p
-  className="alert alert-danger"
->
-  marketplace.wrong_license_type
-</p>
-`;
-
-exports[`should display correctly 1`] = `
-<div>
-  <label
-    className="display-inline-block spacer-bottom"
-    htmlFor="set-license"
-  >
-    marketplace.enter_license_for_x.Foo
-    <em
-      className="mandatory"
-    >
-      *
-    </em>
-  </label>
-  <textarea
-    autoFocus={true}
-    className="display-block input-super-large"
-    id="set-license"
-    onChange={[Function]}
-    required={true}
-    rows={8}
-    style={
-      Object {
-        "resize": "none",
-      }
-    }
-    value=""
-  />
-  <DeferredSpinner
-    customSpinner={
-      <p
-        className="spacer-top"
-      >
-        <i
-          className="spinner spacer-right text-bottom"
-        />
-        marketplace.checking_license
-      </p>
-    }
-    loading={false}
-    timeout={100}
-  >
-    <div
-      className="spacer-top"
-    >
-      <a
-        href="license_url"
-        target="_blank"
-      >
-        marketplace.i_need_a_license
-      </a>
-    </div>
-  </DeferredSpinner>
-</div>
-`;
-
-exports[`should display the get license link with parameters 1`] = `
-<a
-  href="license_url?serverId=foo&ncloc=1000"
-  target="_blank"
->
-  marketplace.i_need_a_license
-</a>
-`;
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/UninstallEditionForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/UninstallEditionForm-test.tsx.snap
deleted file mode 100644 (file)
index 491d59e..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display correctly 1`] = `
-<Modal
-  contentLabel="marketplace.downgrade_to_community_edition."
-  onRequestClose={[MockFunction]}
->
-  <header
-    className="modal-head"
-  >
-    <h2>
-      marketplace.downgrade_to_community_edition.
-    </h2>
-  </header>
-  <div
-    className="modal-body"
-  >
-    <p>
-      marketplace.uninstall_x_confirmation.Foo
-    </p>
-  </div>
-  <footer
-    className="modal-foot"
-  >
-    <Button
-      disabled={false}
-      onClick={[Function]}
-    >
-      marketplace.downgrade
-    </Button>
-    <ResetButtonLink
-      className="js-modal-close"
-      onClick={[MockFunction]}
-    >
-      cancel
-    </ResetButtonLink>
-  </footer>
-</Modal>
-`;
index 9e51642bbc52317cdb4f6824499168803780abc2..0e2ef2a2831226145ae52638c0a0b5e86d5cb3da 100644 (file)
   margin-right: 8px;
 }
 
+.marketplace-edition .markdown h3 {
+  font-size: 14px;
+  margin-top: 0;
+}
+
 .marketplace-edition-badge {
   position: absolute;
   right: -1px;
index 690b687312110da9a297305da5354cdc153e1c12..9aebc85c3ad889037d18bde4e83d9a7772b66911 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { memoize, sortBy } from 'lodash';
+import { stringify } from 'querystring';
+import { memoize } from 'lodash';
 import { Plugin, PluginAvailable, PluginInstalled, PluginPending } from '../../api/plugins';
 import { cleanQuery, parseAsString, RawQuery, serializeString } from '../../helpers/query';
-import { Edition } from '../../api/marketplace';
+import { omitNil } from '../../helpers/request';
+
+export interface Edition {
+  downloadUrl?: string;
+  homeUrl: string;
+  key: string;
+}
 
 export interface Query {
   filter: string;
   search?: string;
 }
 
-export function isPluginAvailable(plugin: Plugin): plugin is PluginAvailable {
-  return (plugin as any).release !== undefined;
-}
+export const EDITIONS: Edition[] = [
+  {
+    key: 'community',
+    homeUrl: 'https://redirect.sonarsource.com/editions/community.html'
+  },
+  {
+    key: 'developer',
+    homeUrl: 'https://redirect.sonarsource.com/editions/developer.html',
+    downloadUrl:
+      'https://sonarsource.bintray.com/CommercialDistribution/editions/developer-edition-7.0.0.717.zip'
+  },
+  {
+    key: 'enterprise',
+    homeUrl: 'https://redirect.sonarsource.com/editions/enterprise.html',
+    downloadUrl:
+      'https://sonarsource.bintray.com/CommercialDistribution/editions/enterprise-edition-7.0.0.717.zip'
+  },
+  {
+    key: 'datacenter',
+    homeUrl: 'https://redirect.sonarsource.com/editions/datacenter.html',
+    downloadUrl:
+      'https://sonarsource.bintray.com/CommercialDistribution/editions/datacenter-edition-7.0.0.717.zip'
+  }
+];
 
-export function isPluginInstalled(plugin: Plugin): plugin is PluginInstalled {
-  return isPluginPending(plugin) && (plugin as any).updatedAt !== undefined;
-}
-
-export function isPluginPending(plugin: Plugin): plugin is PluginPending {
-  return (plugin as any).version !== undefined;
+export function getEditionUrl(
+  edition: Edition,
+  data: { serverId?: string; ncloc?: number; sourceEdition: string }
+) {
+  let url = edition.homeUrl;
+  const query = stringify(omitNil(data));
+  if (query) {
+    url += '?' + query;
+  }
+  return url;
 }
 
 export function filterPlugins(plugins: Plugin[], search: string): Plugin[] {
@@ -50,9 +82,16 @@ export function filterPlugins(plugins: Plugin[], search: string): Plugin[] {
   });
 }
 
-const EDITIONS_ORDER = ['community', 'developer', 'enterprise', 'datacenter'];
-export function sortEditions(editions: Edition[]): Edition[] {
-  return sortBy(editions, edition => EDITIONS_ORDER.indexOf(edition.key));
+export function isPluginAvailable(plugin: Plugin): plugin is PluginAvailable {
+  return (plugin as any).release !== undefined;
+}
+
+export function isPluginInstalled(plugin: Plugin): plugin is PluginInstalled {
+  return isPluginPending(plugin) && (plugin as any).updatedAt !== undefined;
+}
+
+export function isPluginPending(plugin: Plugin): plugin is PluginPending {
+  return (plugin as any).version !== undefined;
 }
 
 export const DEFAULT_FILTER = 'all';
index 894c74f23980d34cd748a3488e9c4eb6eeb76251..7e9b6156d9caa423ba8d91e140f180561761df07 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { Dispatch } from 'react-redux';
-import { getEditionsForVersion, getEditionsForLastVersion } from './utils';
-import { Edition, EditionStatus, getEditionStatus, getEditionsList } from '../../api/marketplace';
+import { getEditionStatus } from '../../api/marketplace';
 import { getPendingPlugins, PluginPendingResult } from '../../api/plugins';
 
-interface LoadEditionsAction {
-  type: 'LOAD_EDITIONS';
-  loading: boolean;
-}
-
 interface SetPendingPluginsAction {
   type: 'SET_PENDING_PLUGINS';
   pending: PluginPendingResult;
 }
 
-interface SetEditionsAction {
-  type: 'SET_EDITIONS';
-  editions: Edition[];
-  readOnly: boolean;
-}
-
-interface SetEditionStatusAction {
-  type: 'SET_EDITION_STATUS';
-  status: EditionStatus;
+interface SetCurrentEditionAction {
+  type: 'SET_CURRENT_EDITION';
+  currentEdition?: string;
 }
 
-export type Action =
-  | LoadEditionsAction
-  | SetEditionsAction
-  | SetEditionStatusAction
-  | SetPendingPluginsAction;
-
-export function loadEditions(loading = true): LoadEditionsAction {
-  return { type: 'LOAD_EDITIONS', loading };
-}
+export type Action = SetCurrentEditionAction | SetPendingPluginsAction;
 
 export function setPendingPlugins(pending: PluginPendingResult): SetPendingPluginsAction {
   return { type: 'SET_PENDING_PLUGINS', pending };
 }
 
-export function setEditions(editions: Edition[], readOnly?: boolean): SetEditionsAction {
-  return { type: 'SET_EDITIONS', editions, readOnly: !!readOnly };
-}
+export const setCurrentEdition = (currentEdition?: string) => (dispatch: Dispatch<Action>) => {
+  dispatch({ type: 'SET_CURRENT_EDITION', currentEdition });
+};
 
-let editionTimer: number | undefined;
-export const setEditionStatus = (status: EditionStatus) => (dispatch: Dispatch<Action>) => {
-  dispatch({ type: 'SET_EDITION_STATUS', status });
-  if (editionTimer) {
-    window.clearTimeout(editionTimer);
-    editionTimer = undefined;
-  }
-  if (status.installationStatus === 'AUTOMATIC_IN_PROGRESS') {
-    editionTimer = window.setTimeout(() => {
-      getEditionStatus().then(status => setEditionStatus(status)(dispatch), () => {});
-      editionTimer = undefined;
-    }, 2000);
-  }
+export const fetchCurrentEdition = () => (dispatch: Dispatch<Action>) => {
+  getEditionStatus().then(
+    editionStatus => dispatch(setCurrentEdition(editionStatus.currentEditionKey)),
+    () => {}
+  );
 };
 
 export const fetchPendingPlugins = () => (dispatch: Dispatch<Action>) => {
@@ -84,18 +56,3 @@ export const fetchPendingPlugins = () => (dispatch: Dispatch<Action>) => {
     () => {}
   );
 };
-
-export const fetchEditions = (url: string, version: string) => (dispatch: Dispatch<Action>) => {
-  dispatch(loadEditions(true));
-  getEditionsList(url).then(
-    editionsPerVersion => {
-      const editions = getEditionsForVersion(editionsPerVersion, version);
-      if (editions) {
-        dispatch(setEditions(editions));
-      } else {
-        dispatch(setEditions(getEditionsForLastVersion(editionsPerVersion), true));
-      }
-    },
-    () => dispatch(loadEditions(false))
-  );
-};
index 11c5e28d7f543b2e46ae0ac802a901ccbdb95c65..55ad93d69db3abd16a86644c70c68d99f1ef11b4 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { Action } from './actions';
-import { Edition, EditionStatus } from '../../api/marketplace';
 import { PluginPendingResult } from '../../api/plugins';
 
 interface State {
-  editions?: Edition[];
-  loading: boolean;
-  status?: EditionStatus;
-  readOnly: boolean;
+  currentEdition?: string;
   pending: PluginPendingResult;
 }
 
-const defaultState: State = {
-  loading: true,
-  readOnly: false,
-  pending: { installing: [], removing: [], updating: [] }
-};
+const defaultState: State = { pending: { installing: [], removing: [], updating: [] } };
 
 export default function(state: State = defaultState, action: Action): State {
-  if (action.type === 'SET_EDITIONS') {
-    return { ...state, editions: action.editions, readOnly: action.readOnly, loading: false };
-  }
-  if (action.type === 'LOAD_EDITIONS') {
-    return { ...state, loading: action.loading };
-  }
   if (action.type === 'SET_PENDING_PLUGINS') {
     return {
       ...state,
       pending: action.pending
     };
   }
-  if (action.type === 'SET_EDITION_STATUS') {
-    const hasChanged = Object.keys(action.status).some(
-      (key: keyof EditionStatus) => !state.status || state.status[key] !== action.status[key]
-    );
-    // Prevent from rerendering the whole admin if the status didn't change
-    if (hasChanged) {
-      return { ...state, status: action.status };
-    }
+  if (action.type === 'SET_CURRENT_EDITION') {
+    return {
+      ...state,
+      currentEdition: action.currentEdition
+    };
   }
   return state;
 }
 
-export const getEditions = (state: State) => state.editions;
-export const getEditionStatus = (state: State) => state.status;
+export const getCurrentEdition = (state: State) => state.currentEdition;
 export const getPendingPlugins = (state: State) => state.pending;
diff --git a/server/sonar-web/src/main/js/store/marketplace/utils.ts b/server/sonar-web/src/main/js/store/marketplace/utils.ts
deleted file mode 100644 (file)
index 7e22118..0000000
+++ /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 { sortBy } from 'lodash';
-import { Edition, EditionsPerVersion } from '../../api/marketplace';
-
-export function getEditionsForLastVersion(editions: EditionsPerVersion): Edition[] {
-  const sortedVersion = sortBy(Object.keys(editions), [
-    (version: string) => -Number(version.split('.')[0]),
-    (version: string) => -Number(version.split('.')[1] || 0),
-    (version: string) => -Number(version.split('.')[2] || 0)
-  ]);
-  return editions[sortedVersion[0]];
-}
-
-export function getEditionsForVersion(
-  editions: EditionsPerVersion,
-  version: string
-): Edition[] | undefined {
-  const minorVersion = version.match(/\d+\.\d+.\d+/);
-  if (minorVersion) {
-    if (editions[minorVersion[0]]) {
-      return editions[minorVersion[0]];
-    }
-  }
-  const majorVersion = version.match(/\d+\.\d+/);
-  if (majorVersion) {
-    if (editions[majorVersion[0]]) {
-      return editions[majorVersion[0]];
-    }
-  }
-  return undefined;
-}
index 0ca7aab07c43d23e267fb76844baa9dcd9edc2a0..7581381ce28802e91d65db1db801de27928febd8 100644 (file)
@@ -73,10 +73,8 @@ export const isFavorite = (state, componentKey) =>
 
 export const getMarketplaceState = state => state.marketplace;
 
-export const getMarketplaceEditions = state => fromMarketplace.getEditions(state.marketplace);
-
-export const getMarketplaceEditionStatus = state =>
-  fromMarketplace.getEditionStatus(state.marketplace);
+export const getMarketplaceCurrentEdition = state =>
+  fromMarketplace.getCurrentEdition(state.marketplace);
 
 export const getMarketplacePendingPlugins = state =>
   fromMarketplace.getPendingPlugins(state.marketplace);
index 286d0ece9bec979a10c29c613463e0c4645f6994..9d2d9e48c43df7d04408c2cba80fdb5b4d963b5a 100644 (file)
@@ -47,7 +47,6 @@ public class CorePropertyDefinitions {
   public static final String ORGANIZATIONS_CREATE_PERSONAL_ORG = "sonar.organizations.createPersonalOrg";
   public static final String ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS = "sonar.onboardingTutorial.showToNewUsers";
   public static final String DISABLE_NOTIFICATION_ON_BUILT_IN_QPROFILES = "sonar.builtInQualityProfiles.disableNotificationOnUpdate";
-  public static final String EDITIONS_CONFIG_URL = "sonar.editions.jsonUrl";
 
   private CorePropertyDefinitions() {
     // only static stuff
@@ -232,15 +231,6 @@ public class CorePropertyDefinitions {
         .category(CATEGORY_ORGANIZATIONS)
         .type(BOOLEAN)
         .hidden()
-        .build(),
-
-      // EDITIONS
-      PropertyDefinition.builder(EDITIONS_CONFIG_URL)
-        .name("Defines URL of JSON file with the definitions of SonarSource editions.")
-        .defaultValue("https://update.sonarsource.org/editions.json")
-        .category(CATEGORY_ORGANIZATIONS)
-        .type(BOOLEAN)
-        .hidden()
         .build()));
     return defs;
   }
index a8fb7a714777c9c320404c515768fd17a329fc5d..e0348723652a6a665abfbd537d3161784aa5a309 100644 (file)
@@ -2150,9 +2150,6 @@ marketplace.installed=Installed
 marketplace.installing=Installing...
 marketplace.upgrade=Upgrade
 marketplace.downgrade=Downgrade
-marketplace.downgrade_to_community_edition=Downgrade to Community Edition
-marketplace.uninstall_x_confirmation=Are you sure you want to uninstall {0} and get back to the Community Edition?
-marketplace.pending=Pending...
 marketplace.checking_license=Checking your license...
 marketplace._installed=installed
 marketplace.available_under_commercial_license=Available under our commercial editions
@@ -2178,24 +2175,12 @@ marketplace.uninstall=Uninstall
 marketplace.i_accept_the=I accept the
 marketplace.commercial_edition=Commercial Edition
 marketplace.terms_and_conditions=Terms and Conditions
-marketplace.editions_unavailable=Explore our Commercial Editions on {url}: advanced feature packs brought to you by SonarSource
-marketplace.edition_status.AUTOMATIC_IN_PROGRESS=Installing your new Commercial Edition... Please wait...
-marketplace.edition_status.AUTOMATIC_READY=Commercial Edition successfully installed. Please restart the server to activate your new edition.
-marketplace.edition_status.UNINSTALL_IN_PROGRESS=Commercial Edition successfully downgraded. Please restart the server to remove the features.
-marketplace.edition_status.MANUAL_IN_PROGRESS=Commercial Edition can't automatically be installed because of internet access issues. Please manually install the package.
-marketplace.edition_status_x.AUTOMATIC_IN_PROGRESS=Installing your new {0}... Please wait...
-marketplace.edition_status_x.AUTOMATIC_READY={0} successfully installed. Please restart the server to activate your new edition.
-marketplace.edition_status_x.UNINSTALL_IN_PROGRESS=Successfully downgraded to {0}. Please restart the server to remove the features.
-marketplace.edition_status_x.MANUAL_IN_PROGRESS={0} can't automatically be installed because of internet access issues. Please manually install the package.
 marketplace.release_notes=Release Notes
-marketplace.how_to_install=How to install it?
-marketplace.see_documentation_to_enable_cluster=See {url} documentation to set up a cluster.
 marketplace.how_to_setup_cluster_url=Further configuration is required to set up a cluster. See {url} documentation.
 marketplace.enter_license_for_x=Enter your license key for {0}
 marketplace.wrong_license_type=Your license is not compatible with any existing editions. Please provide a valid license.
 marketplace.wrong_license_type_x=Your license is not compatible with the selected edition. Please provide a valid license for {0}.
 marketplace.i_need_a_license=I need a license key
-marketplace.download_package=Download package
 marketplace.search=Search by features, tags, or categories...
 
 
index b3054a31a22143ac21403b3c84aa3a93ed43c5c4..75724cd1569ed435cd3864f93206bb352f2760d1 100644 (file)
@@ -30,7 +30,7 @@ public class CorePropertyDefinitionsTest {
   @Test
   public void all() {
     List<PropertyDefinition> defs = CorePropertyDefinitions.all();
-    assertThat(defs).hasSize(59);
+    assertThat(defs).hasSize(58);
   }
 
   @Test