Bladeren bron

SONAR-10264 Create the project badges page for SonarCloud

- SONAR-10268 Create the badges page with the Scanned on SonarCloud badge
- SONAR-10272 Allow user to choose the color of the badge
- SONAR-10268 Add the "Scanned on SonarCloud" svg files
- SONAR-10264 SONAR-10271 Add standard measure badges
- SONAR-10264 Allow user to choose a metric for the standard badge
tags/7.5
Grégoire Aubert 6 jaren geleden
bovenliggende
commit
e375cacde2
27 gewijzigde bestanden met toevoegingen van 1019 en 26 verwijderingen
  1. 14
    9
      server/sonar-web/src/main/js/api/web-api.ts
  2. 4
    0
      server/sonar-web/src/main/js/app/styles/init/misc.css
  3. 3
    0
      server/sonar-web/src/main/js/app/theme.js
  4. 44
    0
      server/sonar-web/src/main/js/apps/overview/badges/BadgeButton.tsx
  5. 156
    0
      server/sonar-web/src/main/js/apps/overview/badges/BadgeParams.tsx
  6. 34
    0
      server/sonar-web/src/main/js/apps/overview/badges/BadgeSnippet.tsx
  7. 107
    0
      server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx
  8. 49
    0
      server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgeButton-test.tsx
  9. 73
    0
      server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgeParams-test.tsx
  10. 34
    0
      server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgesModal-test.tsx
  11. 39
    0
      server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgeButton-test.tsx.snap
  12. 56
    0
      server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgeParams-test.tsx.snap
  13. 89
    0
      server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgesModal-test.tsx.snap
  14. 58
    0
      server/sonar-web/src/main/js/apps/overview/badges/__tests__/utils-test.ts
  15. 85
    0
      server/sonar-web/src/main/js/apps/overview/badges/styles.css
  16. 56
    0
      server/sonar-web/src/main/js/apps/overview/badges/utils.ts
  17. 9
    2
      server/sonar-web/src/main/js/apps/overview/meta/Meta.js
  18. 3
    3
      server/sonar-web/src/main/js/apps/tutorials/onboarding/styles.css
  19. 3
    2
      server/sonar-web/src/main/js/apps/web-api/components/ResponseExample.tsx
  20. 9
    6
      server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx
  21. 1
    1
      server/sonar-web/src/main/js/components/SelectList/styles.css
  22. 3
    1
      server/sonar-web/src/main/js/components/controls/ClipboardButton.tsx
  23. 2
    2
      server/sonar-web/src/main/js/components/workspace/styles.css
  24. 25
    0
      server/sonar-web/src/main/webapp/images/project_badges/sonarcloud-black.svg
  25. 25
    0
      server/sonar-web/src/main/webapp/images/project_badges/sonarcloud-orange.svg
  26. 24
    0
      server/sonar-web/src/main/webapp/images/project_badges/sonarcloud-white.svg
  27. 14
    0
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 14
- 9
server/sonar-web/src/main/js/api/web-api.ts Bestand weergeven

@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { getJSON } from '../helpers/request';
import throwGlobalError from '../app/utils/throwGlobalError';

export interface Changelog {
description: string;
@@ -65,16 +66,20 @@ export interface Example {
format: string;
}

export function fetchWebApi(showInternal: boolean = true): Promise<Array<Domain>> {
return getJSON('/api/webservices/list', { include_internals: showInternal }).then(r =>
r.webServices.map((domain: any) => {
const deprecated = !domain.actions.find((action: any) => !action.deprecatedSince);
const internal = !domain.actions.find((action: any) => !action.internal);
return { ...domain, deprecated, internal };
})
);
export function fetchWebApi(showInternal = true): Promise<Domain[]> {
return getJSON('/api/webservices/list', { include_internals: showInternal })
.then(r =>
r.webServices.map((domain: any) => {
const deprecated = !domain.actions.find((action: any) => !action.deprecatedSince);
const internal = !domain.actions.find((action: any) => !action.internal);
return { ...domain, deprecated, internal };
})
)
.catch(throwGlobalError);
}

export function fetchResponseExample(domain: string, action: string): Promise<Example> {
return getJSON('/api/webservices/response_example', { controller: domain, action });
return getJSON('/api/webservices/response_example', { controller: domain, action }).catch(
throwGlobalError
);
}

+ 4
- 0
server/sonar-web/src/main/js/app/styles/init/misc.css Bestand weergeven

@@ -85,6 +85,10 @@ th.nowrap {
margin-top: 16px;
}

.huge-spacer-bottom {
margin-bottom: 40px;
}

.huge-spacer-top {
margin-top: 40px;
}

+ 3
- 0
server/sonar-web/src/main/js/app/theme.js Bestand weergeven

@@ -38,6 +38,7 @@ module.exports = {
gray71: '#b4b4b4',
gray67: '#aaa',
gray60: '#999',
gray40: '#404040',

barBackgroundColor: '#f3f3f3',
barBorderColor: '#e6e6e6',
@@ -48,6 +49,8 @@ module.exports = {
leakColor: '#fbf3d5',
leakBorderColor: '#eae3c7',

snippetFontColor: '#f0f0f0',

// sizes
gridSize: `${grid}px`,


+ 44
- 0
server/sonar-web/src/main/js/apps/overview/badges/BadgeButton.tsx Bestand weergeven

@@ -0,0 +1,44 @@
/*
* 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 * as classNames from 'classnames';
import { BadgeType } from './utils';
import { translate } from '../../../helpers/l10n';

interface Props {
onClick: (type: BadgeType) => void;
selected: boolean;
type: BadgeType;
url: string;
}

export default class BadgeButton extends React.PureComponent<Props> {
handleClick = () => this.props.onClick(this.props.type);

render() {
const { selected, type, url } = this.props;
const width = type !== BadgeType.measure ? '128px' : undefined;
return (
<button className={classNames('badge-button', { selected })} onClick={this.handleClick}>
<img src={url} alt={translate('overview.badges', type, 'alt')} width={width} />
</button>
);
}
}

+ 156
- 0
server/sonar-web/src/main/js/apps/overview/badges/BadgeParams.tsx Bestand weergeven

@@ -0,0 +1,156 @@
/*
* 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 { connect } from 'react-redux';
import Select from '../../../components/controls/Select';
import { fetchWebApi } from '../../../api/web-api';
import { Metric } from '../../../app/types';
import { BadgeColors, BadgeType, BadgeOptions } from './utils';
import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
import { fetchMetrics } from '../../../store/rootActions';
import { getMetrics } from '../../../store/rootReducer';

interface StateToProps {
metrics: { [key: string]: Metric };
}

interface DispatchToProps {
fetchMetrics: () => void;
}

interface OwnProps {
className?: string;
options: BadgeOptions;
type: BadgeType;
updateOptions: (options: Partial<BadgeOptions>) => void;
}

type Props = StateToProps & DispatchToProps & OwnProps;

interface State {
badgeMetrics: string[];
}

export class BadgeParams extends React.PureComponent<Props> {
mounted: boolean;
state: State = { badgeMetrics: [] };

componentDidMount() {
this.mounted = true;
this.props.fetchMetrics();
this.fetchBadgeMetrics();
}

componentWillUnmount() {
this.mounted = false;
}

fetchBadgeMetrics() {
fetchWebApi(false).then(
webservices => {
if (this.mounted) {
const domain = webservices.find(domain => domain.path === 'api/project_badges');
const ws = domain && domain.actions.find(ws => ws.key === 'measure');
const param = ws && ws.params && ws.params.find(param => param.key === 'metric');
if (param && param.possibleValues) {
this.setState({ badgeMetrics: param.possibleValues });
}
}
},
() => {}
);
}

getColorOptions = () =>
['white', 'black', 'orange'].map(color => ({
label: translate('overview.badges.options.colors', color),
value: color
}));

getMetricOptions = () => {
const { metrics } = this.props;
return this.state.badgeMetrics.map(key => {
const metric = metrics[key];
return {
value: key,
label: metric && getLocalizedMetricName(metric)
};
});
};

handleColorChange = ({ value }: { value: BadgeColors }) =>
this.props.updateOptions({ color: value });

handleMetricChange = ({ value }: { value: string }) =>
this.props.updateOptions({ metric: value });

render() {
const { className, options, type } = this.props;
switch (type) {
case BadgeType.marketing:
return (
<div className={className}>
<label className="big-spacer-right" htmlFor="badge-color">
{translate('color')}
</label>
<Select
className="input-medium"
clearable={false}
name="badge-color"
onChange={this.handleColorChange}
options={this.getColorOptions()}
searchable={false}
value={options.color}
/>
</div>
);
case BadgeType.measure:
return (
<div className={className}>
<label className="big-spacer-right" htmlFor="badge-metric">
{translate('overview.badges.metric')}
</label>
<Select
className="input-medium"
clearable={false}
name="badge-metric"
onChange={this.handleMetricChange}
options={this.getMetricOptions()}
searchable={false}
value={options.metric}
/>
</div>
);
default:
return null;
}
}
}

const mapDispatchToProps: DispatchToProps = { fetchMetrics };

const mapStateToProps = (state: any): StateToProps => ({
metrics: getMetrics(state)
});

export default connect<StateToProps, DispatchToProps, OwnProps>(
mapStateToProps,
mapDispatchToProps
)(BadgeParams);

+ 34
- 0
server/sonar-web/src/main/js/apps/overview/badges/BadgeSnippet.tsx Bestand weergeven

@@ -0,0 +1,34 @@
/*
* 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 ClipboardButton from '../../../components/controls/ClipboardButton';

interface Props {
snippet: string;
}

export default function BadgeSnippet({ snippet }: Props) {
return (
<div className="badge-snippet">
<pre>{snippet}</pre>
<ClipboardButton copyValue={snippet} tooltipPlacement="top" />
</div>
);
}

+ 107
- 0
server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx Bestand weergeven

@@ -0,0 +1,107 @@
/*
* 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 Modal from '../../../components/controls/Modal';
import BadgeButton from './BadgeButton';
import BadgeSnippet from './BadgeSnippet';
import BadgeParams from './BadgeParams';
import { getBadgeUrl, BadgeType, BadgeOptions } from './utils';
import { translate } from '../../../helpers/l10n';
import './styles.css';

interface Props {
branch: string;
project: string;
}

interface State {
open: boolean;
selectedType: BadgeType;
badgeOptions: BadgeOptions;
}

export default class BadgesModal extends React.PureComponent<Props, State> {
state: State = {
open: false,
selectedType: BadgeType.measure,
badgeOptions: { color: 'white', metric: 'alert_status' }
};

handleClose = () => this.setState({ open: false });

handleOpen = () => this.setState({ open: true });

handleSelectBadge = (selectedType: BadgeType) => this.setState({ selectedType });

handleUpdateOptions = (options: Partial<BadgeOptions>) =>
this.setState(state => ({
badgeOptions: { ...state.badgeOptions, ...options }
}));

handleCancelClick = () => this.handleClose();

render() {
const { branch, project } = this.props;
const { selectedType, badgeOptions } = this.state;
const header = translate('overview.badges.title');
const fullBadgeOptions = { branch, project, ...badgeOptions };
return (
<>
<button onClick={this.handleOpen}>{translate('overview.badges.get_badge')}</button>
{this.state.open && (
<Modal contentLabel={header} onRequestClose={this.handleClose}>
<header className="modal-head">
<h2>{header}</h2>
</header>
<div className="modal-body">
<p className="huge-spacer-bottom">{translate('overview.badges.description')}</p>
<div className="badges-list spacer-bottom">
{[BadgeType.measure, BadgeType.qualityGate, BadgeType.marketing].map(type => (
<BadgeButton
key={type}
onClick={this.handleSelectBadge}
selected={type === selectedType}
type={type}
url={getBadgeUrl(type, fullBadgeOptions)}
/>
))}
</div>
<p className="text-center note huge-spacer-bottom">
{translate('overview.badges', selectedType, 'description')}
</p>
<BadgeParams
className="big-spacer-bottom"
options={badgeOptions}
type={selectedType}
updateOptions={this.handleUpdateOptions}
/>
<BadgeSnippet snippet={getBadgeUrl(selectedType, fullBadgeOptions)} />
</div>
<footer className="modal-foot">
<button className="button-link js-modal-close" onClick={this.handleCancelClick}>
{translate('close')}
</button>
</footer>
</Modal>
)}
</>
);
}
}

+ 49
- 0
server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgeButton-test.tsx Bestand weergeven

@@ -0,0 +1,49 @@
/*
* 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 BadgeButton from '../BadgeButton';
import { BadgeType } from '../utils';
import { click } from '../../../../helpers/testUtils';

it('should display correctly', () => {
expect(getWrapper()).toMatchSnapshot();
expect(getWrapper({ selected: true })).toMatchSnapshot();
expect(getWrapper({ type: BadgeType.measure })).toMatchSnapshot();
});

it('should return the badge type on click', () => {
const onClick = jest.fn();
const wrapper = getWrapper({ onClick });
click(wrapper.find('button'));
expect(onClick).toHaveBeenCalledWith(BadgeType.marketing);
});

function getWrapper(props = {}) {
return shallow(
<BadgeButton
onClick={jest.fn()}
selected={false}
type={BadgeType.marketing}
url="http://foo.bar"
{...props}
/>
);
}

+ 73
- 0
server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgeParams-test.tsx Bestand weergeven

@@ -0,0 +1,73 @@
/*
* 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 { BadgeParams } from '../BadgeParams';
import { BadgeType } from '../utils';
import { Metric } from '../../../../app/types';

jest.mock('../../../../api/web-api', () => ({
fetchWebApi: () =>
Promise.resolve([
{
path: 'api/project_badges',
actions: [
{
key: 'measure',
params: [{ key: 'metric', possibleValues: ['alert_status', 'coverage'] }]
}
]
}
])
}));

const METRICS = {
alert_status: { key: 'alert_status', name: 'Quality Gate' } as Metric,
coverage: { key: 'coverage', name: 'Coverage' } as Metric
};

it('should display marketing badge params', () => {
const updateOptions = jest.fn();
const wrapper = getWrapper({ updateOptions });
expect(wrapper).toMatchSnapshot();
(wrapper.instance() as BadgeParams).handleColorChange({ value: 'black' });
expect(updateOptions).toHaveBeenCalledWith({ color: 'black' });
});

it('should display measure badge params', () => {
const updateOptions = jest.fn();
const wrapper = getWrapper({ updateOptions, type: BadgeType.measure });
expect(wrapper).toMatchSnapshot();
(wrapper.instance() as BadgeParams).handleColorChange({ value: 'black' });
expect(updateOptions).toHaveBeenCalledWith({ color: 'black' });
});

function getWrapper(props = {}) {
return shallow(
<BadgeParams
fetchMetrics={jest.fn()}
metrics={METRICS}
options={{ color: 'white', metric: 'alert_status' }}
type={BadgeType.marketing}
updateOptions={jest.fn()}
{...props}
/>
);
}

+ 34
- 0
server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgesModal-test.tsx Bestand weergeven

@@ -0,0 +1,34 @@
/*
* 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 BadgesModal from '../BadgesModal';
import { click } from '../../../../helpers/testUtils';

jest.mock('../../../../helpers/urls', () => ({
getHostUrl: () => 'host'
}));

it('should display the modal after click', () => {
const wrapper = shallow(<BadgesModal branch="branch-6.6" project="foo" />);
expect(wrapper).toMatchSnapshot();
click(wrapper.find('button'));
expect(wrapper.find('Modal')).toMatchSnapshot();
});

+ 39
- 0
server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgeButton-test.tsx.snap Bestand weergeven

@@ -0,0 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should display correctly 1`] = `
<button
className="badge-button"
onClick={[Function]}
>
<img
alt="overview.badges.marketing.alt"
src="http://foo.bar"
width="128px"
/>
</button>
`;

exports[`should display correctly 2`] = `
<button
className="badge-button selected"
onClick={[Function]}
>
<img
alt="overview.badges.marketing.alt"
src="http://foo.bar"
width="128px"
/>
</button>
`;

exports[`should display correctly 3`] = `
<button
className="badge-button"
onClick={[Function]}
>
<img
alt="overview.badges.measure.alt"
src="http://foo.bar"
/>
</button>
`;

+ 56
- 0
server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgeParams-test.tsx.snap Bestand weergeven

@@ -0,0 +1,56 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should display marketing badge params 1`] = `
<div>
<label
className="big-spacer-right"
htmlFor="badge-color"
>
color
</label>
<Select
className="input-medium"
clearable={false}
name="badge-color"
onChange={[Function]}
options={
Array [
Object {
"label": "overview.badges.options.colors.white",
"value": "white",
},
Object {
"label": "overview.badges.options.colors.black",
"value": "black",
},
Object {
"label": "overview.badges.options.colors.orange",
"value": "orange",
},
]
}
searchable={false}
value="white"
/>
</div>
`;

exports[`should display measure badge params 1`] = `
<div>
<label
className="big-spacer-right"
htmlFor="badge-metric"
>
overview.badges.metric
</label>
<Select
className="input-medium"
clearable={false}
name="badge-metric"
onChange={[Function]}
options={Array []}
searchable={false}
value="alert_status"
/>
</div>
`;

+ 89
- 0
server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgesModal-test.tsx.snap Bestand weergeven

@@ -0,0 +1,89 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should display the modal after click 1`] = `
<React.Fragment>
<button
onClick={[Function]}
>
overview.badges.get_badge
</button>
</React.Fragment>
`;

exports[`should display the modal after click 2`] = `
<Modal
contentLabel="overview.badges.title"
onRequestClose={[Function]}
>
<header
className="modal-head"
>
<h2>
overview.badges.title
</h2>
</header>
<div
className="modal-body"
>
<p
className="huge-spacer-bottom"
>
overview.badges.description
</p>
<div
className="badges-list spacer-bottom"
>
<BadgeButton
key="measure"
onClick={[Function]}
selected={true}
type="measure"
url="host/api/project_badges/measure?branch=branch-6.6&project=foo&metric=alert_status"
/>
<BadgeButton
key="quality_gate"
onClick={[Function]}
selected={false}
type="quality_gate"
url="host/api/project_badges/quality_gate?branch=branch-6.6&project=foo"
/>
<BadgeButton
key="marketing"
onClick={[Function]}
selected={false}
type="marketing"
url="host/images/project_badges/sonarcloud-white.svg"
/>
</div>
<p
className="text-center note huge-spacer-bottom"
>
overview.badges.measure.description
</p>
<Connect(BadgeParams)
className="big-spacer-bottom"
options={
Object {
"color": "white",
"metric": "alert_status",
}
}
type="measure"
updateOptions={[Function]}
/>
<BadgeSnippet
snippet="host/api/project_badges/measure?branch=branch-6.6&project=foo&metric=alert_status"
/>
</div>
<footer
className="modal-foot"
>
<button
className="button-link js-modal-close"
onClick={[Function]}
>
close
</button>
</footer>
</Modal>
`;

+ 58
- 0
server/sonar-web/src/main/js/apps/overview/badges/__tests__/utils-test.ts Bestand weergeven

@@ -0,0 +1,58 @@
/*
* 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 { getBadgeUrl, BadgeOptions, BadgeType } from '../utils';

jest.mock('../../../../helpers/urls', () => ({
getHostUrl: () => 'host'
}));

const options: BadgeOptions = {
branch: 'master',
color: 'white',
project: 'foo',
metric: 'alert_status'
};

describe('#getBadgeUrl', () => {
it('should generate correct marketing badge links', () => {
expect(getBadgeUrl(BadgeType.marketing, options)).toBe(
'host/images/project_badges/sonarcloud-white.svg'
);
expect(getBadgeUrl(BadgeType.marketing, { ...options, color: 'orange' })).toBe(
'host/images/project_badges/sonarcloud-orange.svg'
);
});

it('should generate correct quality gate badge links', () => {
expect(getBadgeUrl(BadgeType.qualityGate, options));
});

it('should generate correct measures badge links', () => {
expect(getBadgeUrl(BadgeType.measure, options)).toBe(
'host/api/project_badges/measure?branch=master&project=foo&metric=alert_status'
);
});

it('should ignore undefined parameters', () => {
expect(getBadgeUrl(BadgeType.measure, { color: 'white', metric: 'alert_status' })).toBe(
'host/api/project_badges/measure?metric=alert_status'
);
});
});

+ 85
- 0
server/sonar-web/src/main/js/apps/overview/badges/styles.css Bestand weergeven

@@ -0,0 +1,85 @@
/*
* 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.
*/
.badges-list {
display: flex;
justify-content: space-around;
justify-content: space-evenly;
flex-wrap: nowrap;
}

.badge-button {
display: flex;
justify-content: center;
padding: var(--gridSize);
min-width: 146px;
height: 116px;
background-color: var(--barBackgroundColor);
border: solid 1px var(--barBorderColor);
border-radius: 3px;
transition: all 0.3s ease;
}

.badge-button:hover,
.badge-button:focus,
.badge-button:active {
background-color: var(--barBackgroundColor);
border-color: var(--blue);
box-shadow: none;
}

.badge-button.selected {
background-color: var(--lightBlue);
border-color: var(--darkBlue);
}

.badge-snippet {
position: relative;
margin: var(--gridSize) 0;
background: var(--gray40);
color: var(--snippetFontColor);
border-radius: 3px;
}

.badge-snippet pre {
padding: calc(2 * var(--gridSize));
padding-bottom: 40px;
overflow: auto;
}

.badge-snippet button {
position: absolute;
top: auto;
top: 40px;
right: calc(2 * var(--gridSize));
height: var(--smallControlHeight);
line-height: 18px;
border: 1px solid #fff;
color: #fff;
font-size: 11px;
font-weight: normal;
user-select: none;
}

.badge-snippet button:hover,
.badge-snippet button:focus,
.badge-snippet button:active {
background-color: #fff;
color: var(--gray40);
}

+ 56
- 0
server/sonar-web/src/main/js/apps/overview/badges/utils.ts Bestand weergeven

@@ -0,0 +1,56 @@
/*
* 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 { omitNil } from '../../../helpers/request';
import { getHostUrl } from '../../../helpers/urls';

export type BadgeColors = 'white' | 'black' | 'orange';

export interface BadgeOptions {
branch?: string;
color?: BadgeColors;
project?: string;
metric?: string;
}

export enum BadgeType {
measure = 'measure',
qualityGate = 'quality_gate',
marketing = 'marketing'
}

export function getBadgeUrl(
type: BadgeType,
{ branch, project, color = 'white', metric = 'alert_status' }: BadgeOptions
) {
switch (type) {
case BadgeType.marketing:
return `${getHostUrl()}/images/project_badges/sonarcloud-${color}.svg`;
case BadgeType.qualityGate:
return `${getHostUrl()}/api/project_badges/quality_gate?${stringify(
omitNil({ branch, project })
)}`;
case BadgeType.measure:
default:
return `${getHostUrl()}/api/project_badges/measure?${stringify(
omitNil({ branch, project, metric })
)}`;
}
}

+ 9
- 2
server/sonar-web/src/main/js/apps/overview/meta/Meta.js Bestand weergeven

@@ -27,7 +27,9 @@ import MetaQualityProfiles from './MetaQualityProfiles';
import AnalysesList from '../events/AnalysesList';
import MetaSize from './MetaSize';
import MetaTags from './MetaTags';
import { areThereCustomOrganizations } from '../../../store/rootReducer';
import BadgesModal from '../badges/BadgesModal';
import { areThereCustomOrganizations, getGlobalSettingValue } from '../../../store/rootReducer';
import { Visibility } from '../../../app/types';

const Meta = ({
branch,
@@ -38,9 +40,10 @@ const Meta = ({
onComponentChange,
onSonarCloud
}) => {
const { qualifier, description, qualityProfiles, qualityGate } = component;
const { qualifier, description, qualityProfiles, qualityGate, visibility } = component;

const isProject = qualifier === 'TRK';
const isPrivate = visibility === Visibility.Private;

const hasDescription = !!description;
const hasQualityProfiles = Array.isArray(qualityProfiles) && qualityProfiles.length > 0;
@@ -87,6 +90,10 @@ const Meta = ({
<MetaKey component={component} />

{hasOrganization && <MetaOrganizationKey component={component} />}

{onSonarCloud &&
isProject &&
!isPrivate && <BadgesModal branch={branch} project={component.key} />}
</div>
);
};

+ 3
- 3
server/sonar-web/src/main/js/apps/tutorials/onboarding/styles.css Bestand weergeven

@@ -66,8 +66,8 @@
.onboarding-command pre {
padding: 15px;
border-radius: 2px;
background: #404040;
color: #f0f0f0;
background: var(--gray40);
color: var(--snippetFontColor);
overflow: auto;
}

@@ -88,7 +88,7 @@
.onboarding-command button:focus,
.onboarding-command button:active {
background-color: #fff;
color: #404040;
color: var(--gray40);
}

.onboarding-command-oneline pre {

+ 3
- 2
server/sonar-web/src/main/js/apps/web-api/components/ResponseExample.tsx Bestand weergeven

@@ -55,8 +55,9 @@ export default class ResponseExample extends React.PureComponent<Props, State> {

fetchResponseExample() {
const { domain, action } = this.props;
fetchResponseExampleApi(domain.path, action.key).then(responseExample =>
this.setState({ responseExample })
fetchResponseExampleApi(domain.path, action.key).then(
responseExample => this.setState({ responseExample }),
() => {}
);
}


+ 9
- 6
server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx Bestand weergeven

@@ -81,12 +81,15 @@ export default class WebApiApp extends React.PureComponent<Props, State> {
}
}

fetchList(cb?: () => void) {
fetchWebApi().then(domains => {
if (this.mounted) {
this.setState({ domains }, cb);
}
});
fetchList() {
fetchWebApi().then(
domains => {
if (this.mounted) {
this.setState({ domains });
}
},
() => {}
);
}

scrollToAction = () => {

+ 1
- 1
server/sonar-web/src/main/js/components/SelectList/styles.css Bestand weergeven

@@ -58,7 +58,7 @@
margin-top: -1px;
padding: 5px 10px;
border-top: 1px solid #e0e0e0;
color: #404040;
color: var(--gray40);
transition: transform 0.3s ease;
}


+ 3
- 1
server/sonar-web/src/main/js/components/controls/ClipboardButton.tsx Bestand weergeven

@@ -59,7 +59,9 @@ export default class ClipboardButton extends React.PureComponent<Props, State> {

componentWillUnmount() {
this.mounted = false;
this.clipboard.destroy();
if (this.clipboard) {
this.clipboard.destroy();
}
}

showTooltip = () => {

+ 2
- 2
server/sonar-web/src/main/js/components/workspace/styles.css Bestand weergeven

@@ -34,7 +34,7 @@
vertical-align: middle;
margin-right: 10px;
padding: 3px 10px;
background-color: #404040;
background-color: var(--gray40);
color: #fff;
font-size: var(--smallFontSize);
font-weight: 300;
@@ -63,7 +63,7 @@
height: 30px;
padding: 3px 10px;
box-sizing: border-box;
background-color: #404040;
background-color: var(--gray40);
color: #fff;
font-weight: 300;
}

+ 25
- 0
server/sonar-web/src/main/webapp/images/project_badges/sonarcloud-black.svg Bestand weergeven

@@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 535 122" viewBox="0 0 535 122" width="128" height="30">
<path d="m523.1 120h-510.7c-5.7 0-10.4-4.7-10.4-10.4v-97.2c0-5.7 4.7-10.4 10.4-10.4h510.7c5.7 0 10.4 4.7 10.4 10.4v97.2c0 5.7-4.7 10.4-10.4 10.4z" fill="#070706"/>
<g fill="#fff">
<path d="m135 89.6c2.4 1.5 7.5 3.2 11.4 3.2 4 0 5.6-1.4 5.6-3.6s-1.3-3.2-6.2-4.9c-8.8-2.9-12.1-7.7-12-12.7 0-7.9 6.8-13.9 17.2-13.9 4.9 0 9.4 1.1 12 2.4l-2.3 9.1c-1.9-1-5.5-2.4-9.2-2.4-3.2 0-5 1.3-5 3.5 0 2 1.6 3 6.8 4.9 8.1 2.8 11.4 6.8 11.5 13.1 0 7.9-6.2 13.7-18.4 13.7-5.5 0-10.5-1.2-13.7-2.9z"/>
<path d="m211.1 79.4c0 15.5-11 22.6-22.4 22.6-12.4 0-21.9-8.1-21.9-21.8s9-22.4 22.6-22.4c13.1 0 21.7 8.9 21.7 21.6zm-30.6.5c0 7.3 3 12.7 8.7 12.7 5.1 0 8.4-5.1 8.4-12.7 0-6.3-2.4-12.7-8.4-12.7-6.4-.1-8.7 6.4-8.7 12.7z"/>
<path d="m215.8 72.2c0-5.3-.2-9.8-.3-13.5h11.4l.6 5.8h.3c1.7-2.7 6.1-6.8 13.1-6.8 8.7 0 15.2 5.7 15.2 18.2v25.1h-13.2v-23.4c0-5.5-1.9-9.2-6.7-9.2-3.6 0-5.8 2.5-6.7 4.9-.3.8-.5 2.1-.5 3.3v24.4h-13.2z"/>
<path d="m287.2 101.1-.8-4.2h-.3c-2.8 3.4-7.1 5.2-12.1 5.2-8.6 0-13.7-6.2-13.7-13 0-11 9.9-16.3 24.9-16.2v-.6c0-2.3-1.2-5.5-7.7-5.5-4.3 0-8.9 1.5-11.7 3.2l-2.4-8.5c2.9-1.6 8.8-3.7 16.5-3.7 14.1 0 18.6 8.3 18.6 18.3v14.7c0 4.1.2 8 .6 10.3zm-1.5-20c-6.9-.1-12.3 1.6-12.3 6.7 0 3.4 2.3 5 5.2 5 3.3 0 6-2.2 6.8-4.9.2-.7.3-1.5.3-2.3z"/>
<path d="m305.4 72.7c0-6.2-.2-10.3-.3-14h11.4l.4 7.8h.3c2.2-6.2 7.4-8.8 11.4-8.8 1.2 0 1.8 0 2.8.2v12.4c-1-.2-2.1-.3-3.6-.3-4.9 0-8.1 2.6-9 6.7-.2.9-.3 1.9-.3 2.9v21.5h-13.2v-28.4z"/>
<path d="m367.6 99.5c-2 1-6.4 2.4-12 2.4-12.7 0-20.9-8.6-20.9-21.4 0-12.9 8.8-22.3 22.5-22.3 4.5 0 8.5 1.1 10.6 2.2l-1.7 5.9c-1.8-1-4.7-2-8.8-2-9.6 0-14.8 7.1-14.8 15.9 0 9.7 6.2 15.7 14.6 15.7 4.3 0 7.2-1.1 9.4-2.1z"/>
<path d="m373 39.6h7.6v61.5h-7.6z"/>
<path d="m428 79.8c0 15.5-10.7 22.3-20.9 22.3-11.4 0-20.1-8.3-20.1-21.6 0-14 9.2-22.3 20.8-22.3 12 0 20.2 8.7 20.2 21.6zm-33.3.4c0 9.2 5.3 16.1 12.7 16.1 7.3 0 12.7-6.8 12.7-16.3 0-7.1-3.6-16.1-12.6-16.1-8.9 0-12.8 8.3-12.8 16.3z"/>
<path d="m469.2 89.7c0 4.3.1 8.1.3 11.4h-6.8l-.4-6.8h-.2c-2 3.4-6.4 7.8-13.9 7.8-6.6 0-14.5-3.6-14.5-18.4v-24.6h7.6v23.2c0 8 2.4 13.3 9.4 13.3 5.1 0 8.7-3.6 10.1-6.9.4-1.1.7-2.5.7-3.9v-25.7h7.6v30.6z"/>
<path d="m514.6 39.6v50.7c0 3.7.1 8 .3 10.8h-6.8l-.3-7.3h-.2c-2.3 4.7-7.5 8.2-14.3 8.2-10.1 0-17.9-8.6-17.9-21.3-.1-14 8.6-22.5 18.8-22.5 6.4 0 10.7 3 12.7 6.4h.2v-25zm-7.6 36.6c0-1-.1-2.3-.3-3.2-1.1-4.9-5.3-8.8-11-8.8-7.9 0-12.6 6.9-12.6 16.2 0 8.5 4.2 15.5 12.4 15.5 5.1 0 9.8-3.4 11.2-9.1.3-1 .3-2.1.3-3.3z"/>
</g>
<path d="m111.3 56.9c-3.6-4.4-8.5-7.7-14-9.4 0-.1 0-.2 0-.3 0-16.1-13-29.2-29.1-29.2s-29.2 13.1-29.2 29.2v.4c-11.8 3.7-20.4 14.8-20.4 27.8 0 16.1 13.1 29.2 29.2 29.2 7.8 0 15.1-3 20.5-8.4 5.3 5.2 12.5 8.4 20.5 8.4 16.1 0 29.2-13.1 29.2-29.2-.1-6.7-2.4-13.3-6.7-18.5zm-22.6 40.3c-12 0-21.8-9.8-21.8-21.8 0-2-1.6-3.7-3.7-3.7-2 0-3.7 1.6-3.7 3.7 0 5.5 1.5 10.6 4.1 14.9-4.1 4.4-9.8 6.9-15.9 6.9-12 0-21.8-9.8-21.8-21.8s9.8-21.8 21.8-21.8c2.6 0 5.1.5 7.5 1.3h.1c.8.3 2 .8 2.4 1.1 1.5 1.3 3.9 1.1 5.2-.4s1.1-3.9-.4-5.2c-1.6-1.4-4.1-2.3-4.7-2.5-3.2-1.2-6.6-1.8-10-1.8-.5 0-.9 0-1.4 0 .5-11.6 10.1-20.9 21.8-20.9 12 0 21.8 9.8 21.8 21.8 0 7-3.4 13.7-9.2 17.8-1.7 1.2-2 3.5-.9 5.1.7 1 1.8 1.5 3 1.5.7 0 1.5-.2 2.1-.7 5.5-3.9 9.4-9.6 11.2-16 8.4 3.1 14.3 11.3 14.3 20.5.1 12.2-9.7 22-21.8 22z" fill="#f60"/>
<path d="m133.4 40.8c1.3.8 3.9 1.7 6 1.7 2.6 0 3.7-1.1 3.7-2.6 0-1.6-1-2.4-3.8-3.4-4.5-1.6-6.5-4.1-6.4-6.8 0-4.1 3.4-7.3 8.8-7.3 2.6 0 4.8.7 6.2 1.4l-1.1 4.2c-1-.6-2.9-1.3-4.9-1.3-2.1 0-3.3 1-3.3 2.4 0 1.5 1.1 2.2 4.1 3.3 4.2 1.5 6.2 3.7 6.2 7.1 0 4.2-3.3 7.3-9.5 7.3-2.8 0-5.4-.7-7.1-1.6z" fill="#fff"/>
<path d="m170.7 45.5c-1.2.6-3.7 1.2-6.6 1.2-7.3 0-12-4.6-12-11.9 0-7 4.8-12.4 13-12.4 2.2 0 4.3.5 5.7 1.1l-1.1 4.4c-1-.4-2.3-.9-4.4-.9-4.5 0-7.2 3.3-7.1 7.6 0 4.8 3.1 7.5 7.1 7.5 2.1 0 3.5-.4 4.6-.9z" fill="#fff"/>
<path d="m188.2 46.2-.4-2.6h-.1c-1.4 1.8-3.9 3.1-6.9 3.1-4.7 0-7.3-3.4-7.3-6.9 0-5.9 5.2-8.8 13.8-8.8v-.4c0-1.5-.6-4.1-4.7-4.1-2.3 0-4.7.7-6.3 1.7l-1.1-3.8c1.7-1.1 4.7-2.1 8.4-2.1 7.5 0 9.6 4.7 9.6 9.8v8.4c0 2.1.1 4.2.3 5.6h-5.3zm-.8-11.4c-4.2-.1-8.1.8-8.1 4.4 0 2.3 1.5 3.3 3.3 3.3 2.3 0 4.1-1.5 4.6-3.2.1-.4.2-.9.2-1.3z" fill="#fff"/>
<path d="m199.4 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z" fill="#fff"/>
<path d="m226.7 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z" fill="#fff"/>
<path d="m258.2 36.2c.1 4.2 3.4 6 7.2 6 2.7 0 4.7-.4 6.5-1.1l.9 4.1c-2 .8-4.8 1.4-8.1 1.4-7.6 0-12-4.6-12-11.8 0-6.5 3.9-12.5 11.4-12.5 7.6 0 10 6.2 10 11.3 0 1.1-.1 2-.2 2.5h-15.7zm10.2-4.1c0-2.2-.9-5.7-4.8-5.7-3.6 0-5.2 3.3-5.4 5.7z" fill="#fff"/>
<path d="m300.2 12.2v27.4c0 2.4.1 5 .2 6.5h-5.3l-.2-3.7h-.1c-1.4 2.6-4.2 4.2-7.6 4.2-5.5 0-9.9-4.7-9.9-11.9 0-7.8 4.8-12.4 10.4-12.4 3.2 0 5.5 1.3 6.5 3.1h.1v-13.2zm-5.9 20.4c0-.5 0-1.1-.1-1.5-.5-2.3-2.4-4.2-5.1-4.2-3.8 0-5.9 3.3-5.9 7.7 0 4.3 2.1 7.4 5.8 7.4 2.4 0 4.5-1.6 5.1-4.2.1-.5.2-1.1.2-1.7z" fill="#fff"/>
<path d="m338.5 34.3c0 8.6-6 12.4-12 12.4-6.6 0-11.7-4.5-11.7-12 0-7.6 5-12.3 12-12.3 7.1 0 11.7 4.8 11.7 11.9zm-17.5.3c0 4.5 2.2 7.9 5.8 7.9 3.3 0 5.7-3.3 5.7-8 0-3.6-1.6-7.8-5.6-7.8-4.3 0-5.9 4-5.9 7.9z" fill="#fff"/>
<path d="m343.3 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z" fill="#fff"/>
</svg>

+ 25
- 0
server/sonar-web/src/main/webapp/images/project_badges/sonarcloud-orange.svg Bestand weergeven

@@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 535 122" viewBox="0 0 535 122" width="128" height="30">
<path d="m523.1 120h-510.7c-5.7 0-10.4-4.7-10.4-10.4v-97.2c0-5.7 4.7-10.4 10.4-10.4h510.7c5.7 0 10.4 4.7 10.4 10.4v97.2c0 5.7-4.7 10.4-10.4 10.4z" fill="#f60"/>
<g fill="#fff">
<path d="m135 89.6c2.4 1.5 7.5 3.2 11.4 3.2 4 0 5.6-1.4 5.6-3.6s-1.3-3.2-6.2-4.9c-8.8-2.9-12.1-7.7-12-12.7 0-7.9 6.8-13.9 17.2-13.9 4.9 0 9.4 1.1 12 2.4l-2.3 9.1c-1.9-1-5.5-2.4-9.2-2.4-3.2 0-5 1.3-5 3.5 0 2 1.6 3 6.8 4.9 8.1 2.8 11.4 6.8 11.5 13.1 0 7.9-6.2 13.7-18.4 13.7-5.5 0-10.5-1.2-13.7-2.9z"/>
<path d="m211.1 79.4c0 15.5-11 22.6-22.4 22.6-12.4 0-21.9-8.1-21.9-21.8s9-22.4 22.6-22.4c13.1 0 21.7 8.9 21.7 21.6zm-30.6.5c0 7.3 3 12.7 8.7 12.7 5.1 0 8.4-5.1 8.4-12.7 0-6.3-2.4-12.7-8.4-12.7-6.4-.1-8.7 6.4-8.7 12.7z"/>
<path d="m215.8 72.2c0-5.3-.2-9.8-.3-13.5h11.4l.6 5.8h.3c1.7-2.7 6.1-6.8 13.1-6.8 8.7 0 15.2 5.7 15.2 18.2v25.1h-13.2v-23.4c0-5.5-1.9-9.2-6.7-9.2-3.6 0-5.8 2.5-6.7 4.9-.3.8-.5 2.1-.5 3.3v24.4h-13.2z"/>
<path d="m287.2 101.1-.8-4.2h-.3c-2.8 3.4-7.1 5.2-12.1 5.2-8.6 0-13.7-6.2-13.7-13 0-11 9.9-16.3 24.9-16.2v-.6c0-2.3-1.2-5.5-7.7-5.5-4.3 0-8.9 1.5-11.7 3.2l-2.4-8.5c2.9-1.6 8.8-3.7 16.5-3.7 14.1 0 18.6 8.3 18.6 18.3v14.7c0 4.1.2 8 .6 10.3zm-1.5-20c-6.9-.1-12.3 1.6-12.3 6.7 0 3.4 2.3 5 5.2 5 3.3 0 6-2.2 6.8-4.9.2-.7.3-1.5.3-2.3z"/>
<path d="m305.4 72.7c0-6.2-.2-10.3-.3-14h11.4l.4 7.8h.3c2.2-6.2 7.4-8.8 11.4-8.8 1.2 0 1.8 0 2.8.2v12.4c-1-.2-2.1-.3-3.6-.3-4.9 0-8.1 2.6-9 6.7-.2.9-.3 1.9-.3 2.9v21.5h-13.2v-28.4z"/>
<path d="m367.6 99.5c-2 1-6.4 2.4-12 2.4-12.7 0-20.9-8.6-20.9-21.4 0-12.9 8.8-22.3 22.5-22.3 4.5 0 8.5 1.1 10.6 2.2l-1.7 5.9c-1.8-1-4.7-2-8.8-2-9.6 0-14.8 7.1-14.8 15.9 0 9.7 6.2 15.7 14.6 15.7 4.3 0 7.2-1.1 9.4-2.1z"/>
<path d="m373 39.6h7.6v61.5h-7.6z"/>
<path d="m428 79.8c0 15.5-10.7 22.3-20.9 22.3-11.4 0-20.1-8.3-20.1-21.6 0-14 9.2-22.3 20.8-22.3 12 0 20.2 8.7 20.2 21.6zm-33.3.4c0 9.2 5.3 16.1 12.7 16.1 7.3 0 12.7-6.8 12.7-16.3 0-7.1-3.6-16.1-12.6-16.1-8.9 0-12.8 8.3-12.8 16.3z"/>
<path d="m469.2 89.7c0 4.3.1 8.1.3 11.4h-6.8l-.4-6.8h-.2c-2 3.4-6.4 7.8-13.9 7.8-6.6 0-14.5-3.6-14.5-18.4v-24.6h7.6v23.2c0 8 2.4 13.3 9.4 13.3 5.1 0 8.7-3.6 10.1-6.9.4-1.1.7-2.5.7-3.9v-25.7h7.6v30.6z"/>
<path d="m514.6 39.6v50.7c0 3.7.1 8 .3 10.8h-6.8l-.3-7.3h-.2c-2.3 4.7-7.5 8.2-14.3 8.2-10.1 0-17.9-8.6-17.9-21.3-.1-14 8.6-22.5 18.8-22.5 6.4 0 10.7 3 12.7 6.4h.2v-25zm-7.6 36.6c0-1-.1-2.3-.3-3.2-1.1-4.9-5.3-8.8-11-8.8-7.9 0-12.6 6.9-12.6 16.2 0 8.5 4.2 15.5 12.4 15.5 5.1 0 9.8-3.4 11.2-9.1.3-1 .3-2.1.3-3.3z"/>
<path d="m111.3 56.9c-3.6-4.4-8.5-7.7-14-9.4 0-.1 0-.2 0-.3 0-16.1-13-29.2-29.1-29.2s-29.2 13.1-29.2 29.2v.4c-11.8 3.7-20.4 14.8-20.4 27.8 0 16.1 13.1 29.2 29.2 29.2 7.8 0 15.1-3 20.5-8.4 5.3 5.2 12.5 8.4 20.5 8.4 16.1 0 29.2-13.1 29.2-29.2-.1-6.7-2.4-13.3-6.7-18.5zm-22.6 40.3c-12 0-21.8-9.8-21.8-21.8 0-2-1.6-3.7-3.7-3.7-2 0-3.7 1.6-3.7 3.7 0 5.5 1.5 10.6 4.1 14.9-4.1 4.4-9.8 6.9-15.9 6.9-12 0-21.8-9.8-21.8-21.8s9.8-21.8 21.8-21.8c2.6 0 5.1.5 7.5 1.3h.1c.8.3 2 .8 2.4 1.1 1.5 1.3 3.9 1.1 5.2-.4s1.1-3.9-.4-5.2c-1.6-1.4-4.1-2.3-4.7-2.5-3.2-1.2-6.6-1.8-10-1.8-.5 0-.9 0-1.4 0 .5-11.6 10.1-20.9 21.8-20.9 12 0 21.8 9.8 21.8 21.8 0 7-3.4 13.7-9.2 17.8-1.7 1.2-2 3.5-.9 5.1.7 1 1.8 1.5 3 1.5.7 0 1.5-.2 2.1-.7 5.5-3.9 9.4-9.6 11.2-16 8.4 3.1 14.3 11.3 14.3 20.5.1 12.2-9.7 22-21.8 22z"/>
<path d="m133.4 40.8c1.3.8 3.9 1.7 6 1.7 2.6 0 3.7-1.1 3.7-2.6 0-1.6-1-2.4-3.8-3.4-4.5-1.6-6.5-4.1-6.4-6.8 0-4.1 3.4-7.3 8.8-7.3 2.6 0 4.8.7 6.2 1.4l-1.1 4.2c-1-.6-2.9-1.3-4.9-1.3-2.1 0-3.3 1-3.3 2.4 0 1.5 1.1 2.2 4.1 3.3 4.2 1.5 6.2 3.7 6.2 7.1 0 4.2-3.3 7.3-9.5 7.3-2.8 0-5.4-.7-7.1-1.6z"/>
<path d="m170.7 45.5c-1.2.6-3.7 1.2-6.6 1.2-7.3 0-12-4.6-12-11.9 0-7 4.8-12.4 13-12.4 2.2 0 4.3.5 5.7 1.1l-1.1 4.4c-1-.4-2.3-.9-4.4-.9-4.5 0-7.2 3.3-7.1 7.6 0 4.8 3.1 7.5 7.1 7.5 2.1 0 3.5-.4 4.6-.9z"/>
<path d="m188.2 46.2-.4-2.6h-.1c-1.4 1.8-3.9 3.1-6.9 3.1-4.7 0-7.3-3.4-7.3-6.9 0-5.9 5.2-8.8 13.8-8.8v-.4c0-1.5-.6-4.1-4.7-4.1-2.3 0-4.7.7-6.3 1.7l-1.1-3.8c1.7-1.1 4.7-2.1 8.4-2.1 7.5 0 9.6 4.7 9.6 9.8v8.4c0 2.1.1 4.2.3 5.6h-5.3zm-.8-11.4c-4.2-.1-8.1.8-8.1 4.4 0 2.3 1.5 3.3 3.3 3.3 2.3 0 4.1-1.5 4.6-3.2.1-.4.2-.9.2-1.3z"/>
<path d="m199.4 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z"/>
<path d="m226.7 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z"/>
<path d="m258.2 36.2c.1 4.2 3.4 6 7.2 6 2.7 0 4.7-.4 6.5-1.1l.9 4.1c-2 .8-4.8 1.4-8.1 1.4-7.6 0-12-4.6-12-11.8 0-6.5 3.9-12.5 11.4-12.5 7.6 0 10 6.2 10 11.3 0 1.1-.1 2-.2 2.5h-15.7zm10.2-4.1c0-2.2-.9-5.7-4.8-5.7-3.6 0-5.2 3.3-5.4 5.7z"/>
<path d="m300.2 12.2v27.4c0 2.4.1 5 .2 6.5h-5.3l-.2-3.7h-.1c-1.4 2.6-4.2 4.2-7.6 4.2-5.5 0-9.9-4.7-9.9-11.9 0-7.8 4.8-12.4 10.4-12.4 3.2 0 5.5 1.3 6.5 3.1h.1v-13.2zm-5.9 20.4c0-.5 0-1.1-.1-1.5-.5-2.3-2.4-4.2-5.1-4.2-3.8 0-5.9 3.3-5.9 7.7 0 4.3 2.1 7.4 5.8 7.4 2.4 0 4.5-1.6 5.1-4.2.1-.5.2-1.1.2-1.7z"/>
<path d="m338.5 34.3c0 8.6-6 12.4-12 12.4-6.6 0-11.7-4.5-11.7-12 0-7.6 5-12.3 12-12.3 7.1 0 11.7 4.8 11.7 11.9zm-17.5.3c0 4.5 2.2 7.9 5.8 7.9 3.3 0 5.7-3.3 5.7-8 0-3.6-1.6-7.8-5.6-7.8-4.3 0-5.9 4-5.9 7.9z"/>
<path d="m343.3 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z"/>
</g>
</svg>

+ 24
- 0
server/sonar-web/src/main/webapp/images/project_badges/sonarcloud-white.svg Bestand weergeven

@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 535 122" viewBox="0 0 535 122" width="128" height="30">
<path d="m522.2 119.5h-508.8c-5.7 0-10.3-4.7-10.3-10.3v-96.4c-.1-5.6 4.6-10.3 10.3-10.3h508.8c5.7 0 10.3 4.7 10.3 10.3v96.3c0 5.7-4.7 10.4-10.3 10.4z" fill="#fff"/>
<path d="m522.2 121h-508.8c-6.5 0-11.8-5.3-11.8-11.8v-96.4c-.1-6.5 5.2-11.8 11.8-11.8h508.8c6.5 0 11.8 5.3 11.8 11.8v96.3c0 6.6-5.3 11.9-11.8 11.9zm-508.8-117c-4.9 0-8.9 4-8.9 8.9v96.3c0 4.9 4 8.9 8.9 8.9h508.8c4.9 0 8.9-4 8.9-8.9v-96.4c0-4.9-4-8.9-8.9-8.9h-508.8z" fill="#cfd3d7"/>
<path d="m135 89.6c2.4 1.5 7.5 3.2 11.4 3.2 4 0 5.6-1.4 5.6-3.6s-1.3-3.2-6.2-4.9c-8.8-2.9-12.1-7.7-12-12.7 0-7.9 6.8-13.9 17.2-13.9 4.9 0 9.4 1.1 12 2.4l-2.3 9.1c-1.9-1-5.5-2.4-9.2-2.4-3.2 0-5 1.3-5 3.5 0 2 1.6 3 6.8 4.9 8.1 2.8 11.4 6.8 11.5 13.1 0 7.9-6.2 13.7-18.4 13.7-5.5 0-10.5-1.2-13.7-2.9z"/>
<path d="m211.1 79.4c0 15.5-11 22.6-22.4 22.6-12.4 0-21.9-8.1-21.9-21.8s9-22.4 22.6-22.4c13.1 0 21.7 8.9 21.7 21.6zm-30.6.5c0 7.3 3 12.7 8.7 12.7 5.1 0 8.4-5.1 8.4-12.7 0-6.3-2.4-12.7-8.4-12.7-6.4-.1-8.7 6.4-8.7 12.7z"/>
<path d="m215.8 72.2c0-5.3-.2-9.8-.3-13.5h11.4l.6 5.8h.3c1.7-2.7 6.1-6.8 13.1-6.8 8.7 0 15.2 5.7 15.2 18.2v25.1h-13.2v-23.4c0-5.5-1.9-9.2-6.7-9.2-3.6 0-5.8 2.5-6.7 4.9-.3.8-.5 2.1-.5 3.3v24.4h-13.2z"/>
<path d="m287.2 101.1-.8-4.2h-.3c-2.8 3.4-7.1 5.2-12.1 5.2-8.6 0-13.7-6.2-13.7-13 0-11 9.9-16.3 24.9-16.2v-.6c0-2.3-1.2-5.5-7.7-5.5-4.3 0-8.9 1.5-11.7 3.2l-2.4-8.5c2.9-1.6 8.8-3.7 16.5-3.7 14.1 0 18.6 8.3 18.6 18.3v14.7c0 4.1.2 8 .6 10.3zm-1.5-20c-6.9-.1-12.3 1.6-12.3 6.7 0 3.4 2.3 5 5.2 5 3.3 0 6-2.2 6.8-4.9.2-.7.3-1.5.3-2.3z"/>
<path d="m305.4 72.7c0-6.2-.2-10.3-.3-14h11.4l.4 7.8h.3c2.2-6.2 7.4-8.8 11.4-8.8 1.2 0 1.8 0 2.8.2v12.4c-1-.2-2.1-.3-3.6-.3-4.9 0-8.1 2.6-9 6.7-.2.9-.3 1.9-.3 2.9v21.5h-13.2v-28.4z"/>
<path d="m367.6 99.5c-2 1-6.4 2.4-12 2.4-12.7 0-20.9-8.6-20.9-21.4 0-12.9 8.8-22.3 22.5-22.3 4.5 0 8.5 1.1 10.6 2.2l-1.7 5.9c-1.8-1-4.7-2-8.8-2-9.6 0-14.8 7.1-14.8 15.9 0 9.7 6.2 15.7 14.6 15.7 4.3 0 7.2-1.1 9.4-2.1z"/>
<path d="m373 39.6h7.6v61.5h-7.6z"/>
<path d="m428 79.8c0 15.5-10.7 22.3-20.9 22.3-11.4 0-20.1-8.3-20.1-21.6 0-14 9.2-22.3 20.8-22.3 12 0 20.2 8.7 20.2 21.6zm-33.3.4c0 9.2 5.3 16.1 12.7 16.1 7.3 0 12.7-6.8 12.7-16.3 0-7.1-3.6-16.1-12.6-16.1-8.9 0-12.8 8.3-12.8 16.3z"/>
<path d="m469.2 89.7c0 4.3.1 8.1.3 11.4h-6.8l-.4-6.8h-.2c-2 3.4-6.4 7.8-13.9 7.8-6.6 0-14.5-3.6-14.5-18.4v-24.6h7.6v23.2c0 8 2.4 13.3 9.4 13.3 5.1 0 8.7-3.6 10.1-6.9.4-1.1.7-2.5.7-3.9v-25.7h7.6v30.6z"/>
<path d="m514.6 39.6v50.7c0 3.7.1 8 .3 10.8h-6.8l-.3-7.3h-.2c-2.3 4.7-7.5 8.2-14.3 8.2-10.1 0-17.9-8.6-17.9-21.3-.1-14 8.6-22.5 18.8-22.5 6.4 0 10.7 3 12.7 6.4h.2v-25zm-7.6 36.6c0-1-.1-2.3-.3-3.2-1.1-4.9-5.3-8.8-11-8.8-7.9 0-12.6 6.9-12.6 16.2 0 8.5 4.2 15.5 12.4 15.5 5.1 0 9.8-3.4 11.2-9.1.3-1 .3-2.1.3-3.3z"/>
<path d="m111.3 56.9c-3.6-4.4-8.5-7.7-14-9.4 0-.1 0-.2 0-.3 0-16.1-13-29.2-29.1-29.2s-29.2 13.1-29.2 29.2v.4c-11.8 3.7-20.4 14.8-20.4 27.8 0 16.1 13.1 29.2 29.2 29.2 7.8 0 15.1-3 20.5-8.4 5.3 5.2 12.5 8.4 20.5 8.4 16.1 0 29.2-13.1 29.2-29.2-.1-6.7-2.4-13.3-6.7-18.5zm-22.6 40.3c-12 0-21.8-9.8-21.8-21.8 0-2-1.6-3.7-3.7-3.7-2 0-3.7 1.6-3.7 3.7 0 5.5 1.5 10.6 4.1 14.9-4.1 4.4-9.8 6.9-15.9 6.9-12 0-21.8-9.8-21.8-21.8s9.8-21.8 21.8-21.8c2.6 0 5.1.5 7.5 1.3h.1c.8.3 2 .8 2.4 1.1 1.5 1.3 3.9 1.1 5.2-.4s1.1-3.9-.4-5.2c-1.6-1.4-4.1-2.3-4.7-2.5-3.2-1.2-6.6-1.8-10-1.8-.5 0-.9 0-1.4 0 .5-11.6 10.1-20.9 21.8-20.9 12 0 21.8 9.8 21.8 21.8 0 7-3.4 13.7-9.2 17.8-1.7 1.2-2 3.5-.9 5.1.7 1 1.8 1.5 3 1.5.7 0 1.5-.2 2.1-.7 5.5-3.9 9.4-9.6 11.2-16 8.4 3.1 14.3 11.3 14.3 20.5.1 12.2-9.7 22-21.8 22z" fill="#f60"/>
<path d="m133.4 40.8c1.3.8 3.9 1.7 6 1.7 2.6 0 3.7-1.1 3.7-2.6 0-1.6-1-2.4-3.8-3.4-4.5-1.6-6.5-4.1-6.4-6.8 0-4.1 3.4-7.3 8.8-7.3 2.6 0 4.8.7 6.2 1.4l-1.1 4.2c-1-.6-2.9-1.3-4.9-1.3-2.1 0-3.3 1-3.3 2.4 0 1.5 1.1 2.2 4.1 3.3 4.2 1.5 6.2 3.7 6.2 7.1 0 4.2-3.3 7.3-9.5 7.3-2.8 0-5.4-.7-7.1-1.6z"/>
<path d="m170.7 45.5c-1.2.6-3.7 1.2-6.6 1.2-7.3 0-12-4.6-12-11.9 0-7 4.8-12.4 13-12.4 2.2 0 4.3.5 5.7 1.1l-1.1 4.4c-1-.4-2.3-.9-4.4-.9-4.5 0-7.2 3.3-7.1 7.6 0 4.8 3.1 7.5 7.1 7.5 2.1 0 3.5-.4 4.6-.9z"/>
<path d="m188.2 46.2-.4-2.6h-.1c-1.4 1.8-3.9 3.1-6.9 3.1-4.7 0-7.3-3.4-7.3-6.9 0-5.9 5.2-8.8 13.8-8.8v-.4c0-1.5-.6-4.1-4.7-4.1-2.3 0-4.7.7-6.3 1.7l-1.1-3.8c1.7-1.1 4.7-2.1 8.4-2.1 7.5 0 9.6 4.7 9.6 9.8v8.4c0 2.1.1 4.2.3 5.6h-5.3zm-.8-11.4c-4.2-.1-8.1.8-8.1 4.4 0 2.3 1.5 3.3 3.3 3.3 2.3 0 4.1-1.5 4.6-3.2.1-.4.2-.9.2-1.3z"/>
<path d="m199.4 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z"/>
<path d="m226.7 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z"/>
<path d="m258.2 36.2c.1 4.2 3.4 6 7.2 6 2.7 0 4.7-.4 6.5-1.1l.9 4.1c-2 .8-4.8 1.4-8.1 1.4-7.6 0-12-4.6-12-11.8 0-6.5 3.9-12.5 11.4-12.5 7.6 0 10 6.2 10 11.3 0 1.1-.1 2-.2 2.5h-15.7zm10.2-4.1c0-2.2-.9-5.7-4.8-5.7-3.6 0-5.2 3.3-5.4 5.7z"/>
<path d="m300.2 12.2v27.4c0 2.4.1 5 .2 6.5h-5.3l-.2-3.7h-.1c-1.4 2.6-4.2 4.2-7.6 4.2-5.5 0-9.9-4.7-9.9-11.9 0-7.8 4.8-12.4 10.4-12.4 3.2 0 5.5 1.3 6.5 3.1h.1v-13.2zm-5.9 20.4c0-.5 0-1.1-.1-1.5-.5-2.3-2.4-4.2-5.1-4.2-3.8 0-5.9 3.3-5.9 7.7 0 4.3 2.1 7.4 5.8 7.4 2.4 0 4.5-1.6 5.1-4.2.1-.5.2-1.1.2-1.7z"/>
<path d="m338.5 34.3c0 8.6-6 12.4-12 12.4-6.6 0-11.7-4.5-11.7-12 0-7.6 5-12.3 12-12.3 7.1 0 11.7 4.8 11.7 11.9zm-17.5.3c0 4.5 2.2 7.9 5.8 7.9 3.3 0 5.7-3.3 5.7-8 0-3.6-1.6-7.8-5.6-7.8-4.3 0-5.9 4-5.9 7.9z"/>
<path d="m343.3 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z"/>
</svg>

+ 14
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties Bestand weergeven

@@ -2319,6 +2319,20 @@ overview.complexity_tooltip.file={0} files have complexity around {1}

overview.deprecated_profile=This quality profile uses {0} deprecated rules and should be updated.

overview.badges.get_badge=Get project badges
overview.badges.title=Badges
overview.badges.description=Show the status of your project metrics on your README or website. Pick your style:
overview.badges.metric=Metric
overview.badges.options.colors.white=White
overview.badges.options.colors.black=Black
overview.badges.options.colors.orange=Orange
overview.badges.measure.alt=Standard badge
overview.badges.measure.description=This badge dynamically displays the current status of one metric of your project.
overview.badges.marketing.alt=Scanned on SonarCloud badge
overview.badges.marketing.description=This badge lets you advertise that you're using SonarCloud for code quality.
overview.badges.quality_gate.alt=SonarCloud Quality Gate badge
overview.badges.quality_gate.description=This badge dynamically displays the current quality gate status of your project.

widget.as_calculated_on_x=As calculated on {0}



Laden…
Annuleren
Opslaan