aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js')
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js237
1 files changed, 237 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js
new file mode 100644
index 00000000000..539878dec9f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js
@@ -0,0 +1,237 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 React from 'react';
+import keyBy from 'lodash/keyBy';
+import ProfileRulesRow from './ProfileRulesRow';
+import { ProfileType } from '../propTypes';
+import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin';
+import { searchRules, takeFacet } from '../../../api/rules';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { formatMeasure } from '../../../helpers/measures';
+import {
+ getRulesUrl,
+ getDeprecatedActiveRulesUrl
+} from '../../../helpers/urls';
+
+const TYPES = ['BUG', 'VULNERABILITY', 'CODE_SMELL'];
+
+export default class ProfileRules extends React.Component {
+ static propTypes = {
+ profile: ProfileType.isRequired,
+ canAdmin: React.PropTypes.bool.isRequired
+ };
+
+ state = {
+ total: null,
+ activatedTotal: null,
+ allByType: keyBy(TYPES.map(t => ({ val: t, count: null })), 'val'),
+ activatedByType: keyBy(TYPES.map(t => ({ val: t, count: null })), 'val')
+ };
+
+ componentDidMount () {
+ this.mounted = true;
+ this.loadRules();
+ }
+
+ componentDidUpdate (prevProps) {
+ if (prevProps.profile !== this.props.profile) {
+ this.loadRules();
+ }
+ }
+
+ componentWillUnmount () {
+ this.mounted = false;
+ }
+
+ loadAllRules () {
+ return searchRules({
+ languages: this.props.profile.language,
+ ps: 1,
+ facets: 'types'
+ });
+ }
+
+ loadActivatedRules () {
+ return searchRules({
+ qprofile: this.props.profile.key,
+ activation: 'true',
+ ps: 1,
+ facets: 'types'
+ });
+ }
+
+ loadRules () {
+ Promise.all([
+ this.loadAllRules(),
+ this.loadActivatedRules()
+ ]).then(responses => {
+ if (this.mounted) {
+ const [allRules, activatedRules] = responses;
+ this.setState({
+ total: allRules.total,
+ activatedTotal: activatedRules.total,
+ allByType: keyBy(takeFacet(allRules, 'types'), 'val'),
+ activatedByType: keyBy(takeFacet(activatedRules, 'types'), 'val')
+ });
+ }
+ });
+ }
+
+ getTooltip (count, total) {
+ if (count == null || total == null) {
+ return '';
+ }
+
+ return translateWithParameters(
+ 'quality_profiles.x_activated_out_of_y',
+ formatMeasure(count, 'INT'),
+ formatMeasure(total, 'INT'));
+ }
+
+ renderActiveTitle () {
+ return (
+ <strong>
+ {translate('quality_profile.total_active_rules')}
+ </strong>
+ );
+ }
+
+ renderActiveCount () {
+ const rulesUrl = getRulesUrl({
+ qprofile: this.props.profile.key,
+ activation: 'true'
+ });
+
+ if (this.state.activatedTotal == null) {
+ return null;
+ }
+
+ return (
+ <a href={rulesUrl}>
+ <strong>
+ {formatMeasure(this.state.activatedTotal, 'SHORT_INT')}
+ </strong>
+ </a>
+ );
+ }
+
+ getTooltipForType (type) {
+ const { count } = this.state.activatedByType[type];
+ const total = this.state.allByType[type].count;
+ return this.getTooltip(count, total);
+ }
+
+ renderTitleForType (type) {
+ return <span>{translate('issue.type', type, 'plural')}</span>;
+ }
+
+ renderCountForType (type) {
+ const rulesUrl = getRulesUrl({
+ qprofile: this.props.profile.key,
+ activation: 'true',
+ types: type
+ });
+
+ const { count } = this.state.activatedByType[type];
+
+ if (count == null) {
+ return null;
+ }
+
+ return (
+ <a href={rulesUrl}>
+ {formatMeasure(count, 'SHORT_INT')}
+ </a>
+ );
+ }
+
+ renderDeprecated () {
+ const { profile } = this.props;
+
+ if (profile.activeDeprecatedRuleCount === 0) {
+ return null;
+ }
+
+ const url = getDeprecatedActiveRulesUrl({ qprofile: profile.key });
+
+ return (
+ <div className="quality-profile-rules-deprecated clearfix">
+ <div className="pull-left">
+ {translate('quality_profiles.deprecated_rules')}
+ </div>
+ <div className="pull-right">
+ <a href={url}>
+ {profile.activeDeprecatedRuleCount}
+ </a>
+ </div>
+ </div>
+ );
+ }
+
+ render () {
+ const { total, activatedTotal, allByType, activatedByType } = this.state;
+
+ const activateMoreUrl = getRulesUrl({
+ qprofile: this.props.profile.key,
+ activation: 'false'
+ });
+
+ return (
+ <div className="quality-profile-rules">
+ <header className="clearfix">
+ <h2 className="pull-left">{translate('rules')}</h2>
+
+ {this.props.canAdmin && (
+ <a href={activateMoreUrl}
+ className="button pull-right js-activate-rules">
+ {translate('quality_profiles.activate_more')}
+ </a>
+ )}
+ </header>
+
+ {this.renderDeprecated()}
+
+ <TooltipsContainer options={{ delay: { show: 250, hide: 0 } }}>
+ <ul className="quality-profile-rules-distribution">
+ <li key="all" className="big-spacer-bottom">
+ <ProfileRulesRow
+ count={activatedTotal}
+ total={total}
+ tooltip={this.getTooltip(activatedTotal, total)}
+ renderTitle={this.renderActiveTitle.bind(this)}
+ renderCount={this.renderActiveCount.bind(this)}/>
+ </li>
+
+ {TYPES.map(type => (
+ <li key={type} className="spacer-top">
+ <ProfileRulesRow
+ count={activatedByType[type].count}
+ total={allByType[type].count}
+ tooltip={this.getTooltipForType(type)}
+ renderTitle={this.renderTitleForType.bind(this, type)}
+ renderCount={this.renderCountForType.bind(this, type)}/>
+ </li>
+ ))}
+ </ul>
+ </TooltipsContainer>
+ </div>
+ );
+ }
+}