123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- /*
- * SonarQube
- * Copyright (C) 2009-2019 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 { translate } from 'sonar-ui-common/helpers/l10n';
- import { SubmitButton, ResetButtonLink } from 'sonar-ui-common/components/controls/buttons';
- import Modal from 'sonar-ui-common/components/controls/Modal';
- import { Alert } from 'sonar-ui-common/components/ui/Alert';
- import Select from 'sonar-ui-common/components/controls/Select';
- import SeverityHelper from '../../../components/shared/SeverityHelper';
- import { activateRule, Profile } from '../../../api/quality-profiles';
- import { SEVERITIES } from '../../../helpers/constants';
- import { sortProfiles } from '../../quality-profiles/utils';
-
- interface Props {
- activation?: T.RuleActivation;
- modalHeader: string;
- onClose: () => void;
- onDone: (severity: string) => Promise<void>;
- organization: string | undefined;
- profiles: Profile[];
- rule: T.Rule | T.RuleDetails;
- }
-
- interface State {
- params: T.Dict<string>;
- profile: string;
- severity: string;
- submitting: boolean;
- }
-
- export default class ActivationFormModal extends React.PureComponent<Props, State> {
- mounted = false;
-
- constructor(props: Props) {
- super(props);
- const profilesWithDepth = this.getQualityProfilesWithDepth(props);
- this.state = {
- params: this.getParams(props),
- profile: profilesWithDepth.length > 0 ? profilesWithDepth[0].key : '',
- severity: props.activation ? props.activation.severity : props.rule.severity,
- submitting: false
- };
- }
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- getParams = ({ activation, rule } = this.props) => {
- const params: T.Dict<string> = {};
- if (rule && rule.params) {
- for (const param of rule.params) {
- params[param.key] = param.defaultValue || '';
- }
- if (activation && activation.params) {
- for (const param of activation.params) {
- params[param.key] = param.value;
- }
- }
- }
- return params;
- };
-
- // Choose QP which a user can administrate, which are the same language and which are not built-in
- getQualityProfilesWithDepth = ({ profiles } = this.props) => {
- return sortProfiles(
- profiles.filter(
- profile =>
- !profile.isBuiltIn &&
- profile.actions &&
- profile.actions.edit &&
- profile.language === this.props.rule.lang
- )
- ).map(profile => ({
- ...profile,
- // Decrease depth by 1, so the top level starts at 0
- depth: profile.depth - 1
- }));
- };
-
- handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
- event.preventDefault();
- this.setState({ submitting: true });
- const data = {
- key: this.state.profile,
- organization: this.props.organization,
- params: this.state.params,
- rule: this.props.rule.key,
- severity: this.state.severity
- };
- activateRule(data)
- .then(() => this.props.onDone(data.severity))
- .then(
- () => {
- if (this.mounted) {
- this.setState({ submitting: false });
- this.props.onClose();
- }
- },
- () => {
- if (this.mounted) {
- this.setState({ submitting: false });
- }
- }
- );
- };
-
- handleParameterChange = (event: React.SyntheticEvent<HTMLInputElement | HTMLTextAreaElement>) => {
- const { name, value } = event.currentTarget;
- this.setState((state: State) => ({ params: { ...state.params, [name]: value } }));
- };
-
- handleProfileChange = ({ value }: { value: string }) => {
- this.setState({ profile: value });
- };
-
- handleSeverityChange = ({ value }: { value: string }) => {
- this.setState({ severity: value });
- };
-
- renderSeverityOption = ({ value }: { value: string }) => {
- return <SeverityHelper severity={value} />;
- };
-
- render() {
- const { activation, rule } = this.props;
- const { profile, severity, submitting } = this.state;
- const { params = [] } = rule;
- const profilesWithDepth = this.getQualityProfilesWithDepth();
- const isCustomRule = !!(rule as T.RuleDetails).templateKey;
- const activeInAllProfiles = profilesWithDepth.length <= 0;
- const isUpdateMode = !!activation;
-
- return (
- <Modal contentLabel={this.props.modalHeader} onRequestClose={this.props.onClose} size="small">
- <form onSubmit={this.handleFormSubmit}>
- <div className="modal-head">
- <h2>{this.props.modalHeader}</h2>
- </div>
-
- <div className="modal-body modal-container">
- {!isUpdateMode && activeInAllProfiles && (
- <Alert variant="info">{translate('coding_rules.active_in_all_profiles')}</Alert>
- )}
-
- <div className="modal-field">
- <label>{translate('coding_rules.quality_profile')}</label>
- <Select
- className="js-profile"
- clearable={false}
- disabled={submitting || profilesWithDepth.length === 1}
- onChange={this.handleProfileChange}
- options={profilesWithDepth.map(profile => ({
- label: ' '.repeat(profile.depth) + profile.name,
- value: profile.key
- }))}
- value={profile}
- />
- </div>
- <div className="modal-field">
- <label>{translate('severity')}</label>
- <Select
- className="js-severity"
- clearable={false}
- disabled={submitting}
- onChange={this.handleSeverityChange}
- optionRenderer={this.renderSeverityOption}
- options={SEVERITIES.map(severity => ({
- label: translate('severity', severity),
- value: severity
- }))}
- searchable={false}
- value={severity}
- valueRenderer={this.renderSeverityOption}
- />
- </div>
- {isCustomRule ? (
- <div className="modal-field">
- <p className="note">{translate('coding_rules.custom_rule.activation_notice')}</p>
- </div>
- ) : (
- params.map(param => (
- <div className="modal-field" key={param.key}>
- <label title={param.key}>{param.key}</label>
- {param.type === 'TEXT' ? (
- <textarea
- disabled={submitting}
- name={param.key}
- onChange={this.handleParameterChange}
- placeholder={param.defaultValue}
- rows={3}
- value={this.state.params[param.key] || ''}
- />
- ) : (
- <input
- disabled={submitting}
- name={param.key}
- onChange={this.handleParameterChange}
- placeholder={param.defaultValue}
- type="text"
- value={this.state.params[param.key] || ''}
- />
- )}
- <div
- className="note"
- // Safe: defined by rule creator (instance admin?)
- dangerouslySetInnerHTML={{ __html: param.htmlDesc || '' }}
- />
- </div>
- ))
- )}
- </div>
-
- <footer className="modal-foot">
- {submitting && <i className="spinner spacer-right" />}
- <SubmitButton disabled={submitting || activeInAllProfiles}>
- {isUpdateMode ? translate('save') : translate('coding_rules.activate')}
- </SubmitButton>
- <ResetButtonLink disabled={submitting} onClick={this.props.onClose}>
- {translate('cancel')}
- </ResetButtonLink>
- </footer>
- </form>
- </Modal>
- );
- }
- }
|