diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-10-20 15:45:41 +0200 |
---|---|---|
committer | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-10-23 08:01:13 -0700 |
commit | afdec5241d0bdeb88583f520c75d6f4de2d583d7 (patch) | |
tree | 1a7a89d7b7d6ad0c6488a113813b1fa3aab0fca6 /server | |
parent | ee4d391a17cddca6478e95db65601b398af3f78f (diff) | |
download | sonarqube-afdec5241d0bdeb88583f520c75d6f4de2d583d7.tar.gz sonarqube-afdec5241d0bdeb88583f520c75d6f4de2d583d7.zip |
Display last available version editions in read only when current edition is not found
Diffstat (limited to 'server')
13 files changed, 192 insertions, 150 deletions
diff --git a/server/sonar-web/src/main/js/apps/marketplace/App.tsx b/server/sonar-web/src/main/js/apps/marketplace/App.tsx index c5012002c95..c57f3de78fd 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/App.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/App.tsx @@ -39,7 +39,14 @@ import { import { Edition, EditionStatus, getEditionsList, getEditionStatus } from '../../api/marketplace'; import { RawQuery } from '../../helpers/query'; import { translate } from '../../helpers/l10n'; -import { getEditionsForVersion, filterPlugins, parseQuery, Query, serializeQuery } from './utils'; +import { + getEditionsForLastVersion, + getEditionsForVersion, + filterPlugins, + parseQuery, + Query, + serializeQuery +} from './utils'; export interface Props { editionsUrl: string; @@ -51,6 +58,7 @@ export interface Props { interface State { editions?: Edition[]; + editionsReadOnly: boolean; editionStatus?: EditionStatus; loadingEditions: boolean; loadingPlugins: boolean; @@ -73,6 +81,7 @@ export default class App extends React.PureComponent<Props, State> { constructor(props: Props) { super(props); this.state = { + editionsReadOnly: false, loadingEditions: true, loadingPlugins: true, pending: { @@ -113,37 +122,26 @@ export default class App extends React.PureComponent<Props, State> { fetchAllPlugins = () => { this.setState({ loadingPlugins: true }); - Promise.all([getInstalledPluginsWithUpdates(), getAvailablePlugins()]).then( - ([installed, available]) => { - if (this.mounted) { - this.setState({ - loadingPlugins: false, - plugins: sortBy(uniqBy([...installed, ...available.plugins], 'key'), 'name') - }); - } - }, - () => { - if (this.mounted) { - this.setState({ loadingPlugins: false }); - } + Promise.all([ + getInstalledPluginsWithUpdates(), + getAvailablePlugins() + ]).then(([installed, available]) => { + if (this.mounted) { + this.setState({ + loadingPlugins: false, + plugins: sortBy(uniqBy([...installed, ...available.plugins], 'key'), 'name') + }); } - ); + }, this.stopLoadingPlugins); }; fetchUpdatesOnly = () => { this.setState({ loadingPlugins: true }); - getPluginUpdates().then( - plugins => { - if (this.mounted) { - this.setState({ loadingPlugins: false, plugins }); - } - }, - () => { - if (this.mounted) { - this.setState({ loadingPlugins: false }); - } + getPluginUpdates().then(plugins => { + if (this.mounted) { + this.setState({ loadingPlugins: false, plugins }); } - ); + }, this.stopLoadingPlugins); }; fetchPendingPlugins = () => @@ -171,10 +169,16 @@ export default class App extends React.PureComponent<Props, State> { getEditionsList(this.props.editionsUrl).then( editionsPerVersion => { if (this.mounted) { - this.setState({ + const newState = { editions: getEditionsForVersion(editionsPerVersion, this.props.sonarqubeVersion), + editionsReadOnly: false, loadingEditions: false - }); + }; + if (!newState.editions) { + newState.editions = getEditionsForLastVersion(editionsPerVersion); + newState.editionsReadOnly = true; + } + this.setState(newState); } }, () => { @@ -204,6 +208,12 @@ export default class App extends React.PureComponent<Props, State> { this.context.router.push({ pathname: this.props.location.pathname, query }); }; + stopLoadingPlugins = () => { + if (this.mounted) { + this.setState({ loadingPlugins: false }); + } + }; + render() { const { standaloneMode } = this.props; const { editions, editionStatus, loadingPlugins, plugins, pending } = this.state; @@ -222,7 +232,7 @@ export default class App extends React.PureComponent<Props, State> { updateEditionStatus={this.updateEditionStatus} /> )} - {!standaloneMode && ( + {standaloneMode && ( <PendingActions refreshPending={this.fetchPendingPlugins} pending={pending} /> )} </div> @@ -232,7 +242,7 @@ export default class App extends React.PureComponent<Props, State> { loading={this.state.loadingEditions} editionStatus={editionStatus} editionsUrl={this.props.editionsUrl} - readOnly={!standaloneMode} + readOnly={!standaloneMode || this.state.editionsReadOnly} sonarqubeVersion={this.props.sonarqubeVersion} updateCenterActive={this.props.updateCenterActive} updateEditionStatus={this.updateEditionStatus} diff --git a/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx b/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx index 4bc679d4d2a..79498c13948 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx @@ -74,6 +74,7 @@ export default class PluginsList extends React.PureComponent<Props> { /> ); } + return null; }; render() { diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx index d637c7116ae..27130a159a4 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx @@ -48,7 +48,7 @@ export default class EditionBox extends React.PureComponent<Props> { if (isInstalled) { return ( <span className="marketplace-edition-badge badge badge-normal-size"> - <CheckIcon size={14} className="little-spacer-right text-text-top" /> + <CheckIcon size={14} className="little-spacer-right text-bottom" /> {translate('marketplace.installed')} </span> ); @@ -59,6 +59,8 @@ export default class EditionBox extends React.PureComponent<Props> { render() { const { edition, editionStatus, readOnly } = this.props; const isInstalled = editionStatus && editionStatus.currentEditionKey === edition.key; + const uninstallInProgress = + editionStatus && editionStatus.installationStatus === 'UNINSTALL_IN_PROGRESS'; const installInProgress = editionStatus && ['AUTOMATIC_IN_PROGRESS', 'AUTOMATIC_READY'].includes(editionStatus.installationStatus); @@ -74,20 +76,20 @@ export default class EditionBox extends React.PureComponent<Props> { {translate('marketplace.learn_more')} </a> {!readOnly && - !isInstalled && ( - <button disabled={installInProgress} onClick={this.handleInstall}> - {translate('marketplace.install')} - </button> - )} - {!readOnly && - isInstalled && ( - <button - className="button-red" - disabled={installInProgress} - onClick={this.props.onUninstall}> - {translate('marketplace.uninstall')} - </button> - )} + (isInstalled ? ( + <button + className="button-red" + disabled={installInProgress || uninstallInProgress} + onClick={this.props.onUninstall}> + {translate('marketplace.uninstall')} + </button> + ) : ( + <button + disabled={installInProgress || uninstallInProgress} + onClick={this.handleInstall}> + {translate('marketplace.install')} + </button> + ))} </div> </div> ); diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx index 2430200ac27..f21e8a30588 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx @@ -49,8 +49,67 @@ export default class EditionsStatusNotif extends React.PureComponent<Props, Stat ); }; - renderStatusAlert() { + renderRestartMsg(edition?: Edition) { const { editionStatus, readOnly } = this.props; + return ( + <div className="alert alert-success"> + <span> + {edition ? ( + translateWithParameters( + 'marketplace.status_x.' + editionStatus.installationStatus, + edition.name + ) + ) : ( + translate('marketplace.status', editionStatus.installationStatus) + )} + </span> + {!readOnly && ( + <button className="js-restart spacer-left" onClick={this.handleOpenRestart}> + {translate('marketplace.restart')} + </button> + )} + {!readOnly && this.state.openRestart && <RestartForm onClose={this.hanleCloseRestart} />} + </div> + ); + } + + renderManualMsg(edition?: Edition) { + const { editionStatus } = this.props; + return ( + <div className="alert alert-danger"> + {edition ? ( + translateWithParameters( + 'marketplace.status_x.' + editionStatus.installationStatus, + edition.name + ) + ) : ( + translate('marketplace.status', editionStatus.installationStatus) + )} + <p className="spacer-left"> + {edition && ( + <a + className="button spacer-right" + download={`sonarqube-${edition.name}.zip`} + href={edition.downloadUrl} + target="_blank"> + {translate('marketplace.download_package')} + </a> + )} + <a + href="https://redirect.sonarsource.com/doc/how-to-install-an-edition.html" + target="_blank"> + {translate('marketplace.how_to_install')} + </a> + </p> + <a className="little-spacer-left" href="https://www.sonarsource.com" target="_blank"> + {translate('marketplace.how_to_install')} + </a> + </div> + ); + } + + renderStatusAlert() { + const { editionStatus } = this.props; const { installationStatus, nextEditionKey } = editionStatus; const nextEdition = this.props.editions && this.props.editions.find(edition => edition.key === nextEditionKey); @@ -65,59 +124,9 @@ export default class EditionsStatusNotif extends React.PureComponent<Props, Stat ); case 'AUTOMATIC_READY': case 'UNINSTALL_IN_PROGRESS': - return ( - <div className="alert alert-success"> - <span> - {nextEdition ? ( - translateWithParameters( - 'marketplace.status_x.' + installationStatus, - nextEdition.name - ) - ) : ( - translate('marketplace.status', installationStatus) - )} - </span> - {!readOnly && ( - <button className="js-restart spacer-left" onClick={this.handleOpenRestart}> - {translate('marketplace.restart')} - </button> - )} - {!readOnly && - this.state.openRestart && <RestartForm onClose={this.hanleCloseRestart} />} - </div> - ); + return this.renderRestartMsg(nextEdition); case 'MANUAL_IN_PROGRESS': - return ( - <div className="alert alert-danger"> - {nextEdition ? ( - translateWithParameters( - 'marketplace.status_x.' + installationStatus, - nextEdition.name - ) - ) : ( - translate('marketplace.status', installationStatus) - )} - <p className="spacer-left"> - {nextEdition && ( - <a - className="button spacer-right" - download={`sonarqube-${nextEdition.name}.zip`} - href={nextEdition.downloadUrl} - target="_blank"> - {translate('marketplace.download_package')} - </a> - )} - <a - href="https://redirect.sonarsource.com/doc/how-to-install-an-edition.html" - target="_blank"> - {translate('marketplace.how_to_install')} - </a> - </p> - <a className="little-spacer-left" href="https://www.sonarsource.com" target="_blank"> - {translate('marketplace.how_to_install')} - </a> - </div> - ); + return this.renderManualMsg(nextEdition); } return null; } @@ -127,12 +136,9 @@ export default class EditionsStatusNotif extends React.PureComponent<Props, Stat return ( <div> {installError && ( - <div className="alert alert-danger"> + <div className="alert alert-danger alert-cancel"> {installError} - <a - className="pull-right button-link text-danger" - href="#" - onClick={this.handleDismissError}> + <a className="button-link text-danger" href="#" onClick={this.handleDismissError}> <CloseIcon /> </a> </div> 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 index 10def5d07bd..58b58f371a7 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx @@ -130,11 +130,11 @@ export default class LicenseEditionSet extends React.PureComponent<Props, State> <textarea autoFocus={true} id="set-license" - className="display-block" - cols={62} + className="display-block input-super-large" onChange={this.handleLicenseChange} required={true} - rows={6} + rows={8} + style={{ resize: 'none' }} value={license} /> {previewStatus && ( diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/PluginActions.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/PluginActions.tsx index c895d0aedd1..501631edadc 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/PluginActions.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/PluginActions.tsx @@ -69,47 +69,53 @@ export default class PluginActions extends React.PureComponent<Props, State> { handleUninstall = () => this.doPluginAction(uninstallPlugin); handleTermsCheck = (checked: boolean) => this.setState({ acceptTerms: checked }); + renderBundled() { + const { plugin } = this.props; + + return ( + <div className="js-actions"> + {isPluginAvailable(plugin) && ( + <div> + <p className="little-spacer-bottom"> + {translate('marketplace.available_under_commercial_license')} + </p> + <a href={plugin.homepageUrl} target="_blank"> + {translate('marketplace.learn_more')} + </a> + </div> + )} + {isPluginInstalled(plugin) && ( + <p> + <CheckIcon className="little-spacer-right" /> + {translate('marketplace.installed')} + </p> + )} + {isPluginInstalled(plugin) && + plugin.updates && + plugin.updates.length > 0 && ( + <div className="spacer-top button-group"> + {plugin.updates.map((update, idx) => ( + <PluginUpdateButton + key={idx} + onClick={this.handleUpdate} + update={update} + disabled={this.state.loading} + /> + ))} + </div> + )} + </div> + ); + } + render() { const { plugin } = this.props; - const { loading } = this.state; if (plugin.editionBundled) { - return ( - <div className="js-actions"> - {isPluginAvailable(plugin) && ( - <div> - <p className="little-spacer-bottom"> - {translate('marketplace.available_under_commercial_license')} - </p> - <a href={plugin.homepageUrl} target="_blank"> - {translate('marketplace.learn_more')} - </a> - </div> - )} - {isPluginInstalled(plugin) && ( - <p> - <CheckIcon className="little-spacer-right" /> - {translate('marketplace.installed')} - </p> - )} - {isPluginInstalled(plugin) && - plugin.updates && - plugin.updates.length > 0 && ( - <div className="spacer-top button-group"> - {plugin.updates.map((update, idx) => ( - <PluginUpdateButton - key={idx} - onClick={this.handleUpdate} - update={update} - disabled={loading} - /> - ))} - </div> - )} - </div> - ); + return this.renderBundled(); } + const { loading } = this.state; return ( <div className="js-actions"> {isPluginAvailable(plugin) && 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 index 705d02f3907..37b76edc83e 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx @@ -57,6 +57,7 @@ export default class UninstallEditionForm extends React.PureComponent<Props, Sta .then(() => { this.props.updateEditionStatus({ ...this.props.editionStatus, + currentEditionKey: undefined, installationStatus: 'UNINSTALL_IN_PROGRESS' }); this.props.onClose(); 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 index 3f52d0cce13..be7f50ce171 100644 --- 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 @@ -53,7 +53,7 @@ it('should update the edition status after uninstall', async () => { expect(uninstallEdition).toHaveBeenCalled(); await new Promise(setImmediate); expect(updateEditionStatus).toHaveBeenCalledWith({ - currentEditionKey: 'foo', + currentEditionKey: undefined, installationStatus: 'UNINSTALL_IN_PROGRESS' }); }); diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap index 7b8a8eaa902..b7dbb58a562 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap @@ -123,7 +123,7 @@ exports[`should display installed badge 1`] = ` className="marketplace-edition-badge badge badge-normal-size" > <CheckIcon - className="little-spacer-right text-text-top" + className="little-spacer-right text-bottom" size={14} /> marketplace.installed diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionsStatusNotif-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionsStatusNotif-test.tsx.snap index e52e16c6fa7..a76a31c61d2 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionsStatusNotif-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionsStatusNotif-test.tsx.snap @@ -36,11 +36,11 @@ exports[`should display an in progress notif 1`] = ` exports[`should display install errors 1`] = ` <div> <div - className="alert alert-danger" + className="alert alert-danger alert-cancel" > Foo error <a - className="pull-right button-link text-danger" + className="button-link text-danger" href="#" onClick={[Function]} > 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 index 2e49983f7d3..4534dd328d2 100644 --- 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 @@ -39,12 +39,16 @@ exports[`should display correctly 1`] = ` </label> <textarea autoFocus={true} - className="display-block" - cols={62} + className="display-block input-super-large" id="set-license" onChange={[Function]} required={true} - rows={6} + rows={8} + style={ + Object { + "resize": "none", + } + } value="" /> <a diff --git a/server/sonar-web/src/main/js/apps/marketplace/utils.ts b/server/sonar-web/src/main/js/apps/marketplace/utils.ts index 1f9216c1c2e..31835b2ec56 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/utils.ts +++ b/server/sonar-web/src/main/js/apps/marketplace/utils.ts @@ -17,7 +17,7 @@ * 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 } from 'lodash'; +import { memoize, sortBy } from 'lodash'; import { Plugin, PluginAvailable, PluginInstalled, PluginPending } from '../../api/plugins'; import { Edition, EditionsPerVersion } from '../../api/marketplace'; import { cleanQuery, parseAsString, RawQuery, serializeString } from '../../helpers/query'; @@ -52,6 +52,15 @@ export function filterPlugins(plugins: Plugin[], search: string): Plugin[] { }); } +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 diff --git a/server/sonar-web/src/main/less/components/alerts.less b/server/sonar-web/src/main/less/components/alerts.less index 01bdc89ce1c..1cc1c516fe2 100644 --- a/server/sonar-web/src/main/less/components/alerts.less +++ b/server/sonar-web/src/main/less/components/alerts.less @@ -66,9 +66,12 @@ .alert-emphasis-variant(#3c763d, #dff0d8, #d6e9c6); } -.page-notifs .alert { +.alert-cancel { display: flex; justify-content: space-between; +} + +.page-notifs .alert { padding: 8px 10px; } |